Course contents 24 / 27

24 / 27 · Learning Without Labels

Dimensionality reduction

Describing high-dimensional data by a few coordinates: principal component analysis derived both as variance maximization and as minimal reconstruction error and computed through the SVD, explained variance and the choice of the number of components, whitening, probabilistic PCA, kernel PCA, and how to read t-SNE and UMAP maps, with autoencoders as the nonlinear generalization.

On this page 16

The mixture models of the previous note explained each point by a discrete latent variable, the cluster it came from. Much data is better explained by a few continuous ones. An 8×88 \times 8 image of a written digit has 64 pixel values, but the images vary along far fewer directions: which digit, its slant, its width, the thickness of the stroke. The points fill a thin region of R64\mathbb R^{64}, close to a low-dimensional subspace or surface. Finding that region gives coordinates that compress the data, remove noise, make distances more meaningful and let us look at the data in two dimensions. This note develops the linear answer, principal component analysis, from two directions that meet in one eigenvalue problem, gives it a probabilistic reading as a latent-variable model, and then turns to nonlinear methods that keep neighbourhoods rather than variance.

Notation. x1,…,xn∈Rdx_1, \dots, x_n \in \mathbb R^d are the data points, centred so that ∑ixi=0\sum_i x_i = 0, and X∈Rn×dX \in \mathbb R^{n \times d} is the matrix with rows xi⊤x_i^\top. S=1nX⊤XS = \frac1n X^\top X is the sample covariance matrix, with eigenvalues λ1≥⋯≥λd≥0\lambda_1 \ge \dots \ge \lambda_d \ge 0 and orthonormal eigenvectors v1,…,vdv_1, \dots, v_d. k<dk < d is the target dimension, W∈Rd×kW \in \mathbb R^{d \times k} a matrix with orthonormal columns (W⊤W=IkW^\top W = I_k), and zi=W⊤xi∈Rkz_i = W^\top x_i \in \mathbb R^k the new coordinates of xix_i. X=UΣV⊤X = U \Sigma V^\top is the singular value decomposition, with singular values σ1≥σ2≥…\sigma_1 \ge \sigma_2 \ge \dots.

Why reduce the dimension#

Four reasons, often at once:

  • Looking. Plots have two axes. A map to two dimensions is the only way to see all 64 features of a dataset in one scatter plot.
  • Compression. Storing kk coordinates instead of dd saves memory and makes everything downstream faster.
  • Denoising. If the signal lives in a few directions and the noise is spread over all of them, discarding the weak directions removes mostly noise.
  • Better distances. In high dimension, distances between points concentrate around a common value (the shell of the Gaussian), which hurts nearest neighbours and the clustering methods of this section. Distances in a well-chosen low-dimensional space are more informative.

PCA as variance maximization#

Project the data onto a single direction, a unit vector ww. The projections w⊤xiw^\top x_i have mean zero, because the data are centred, and variance

1n∑i=1n(w⊤xi)2=w⊤(1n∑i=1nxixi⊤)w=w⊤Sw.\frac1n \sum_{i=1}^n (w^\top x_i)^2 = w^\top \Big(\frac1n \sum_{i=1}^n x_i x_i^\top\Big) w = w^\top S w .

A direction that keeps the points spread out keeps the differences between them, so ask for the direction of largest variance: maximize w⊤Sww^\top S w subject to w⊤w=1w^\top w = 1. The Lagrangian w⊤Sw−λ(w⊤w−1)w^\top S w - \lambda (w^\top w - 1) has gradient 2Sw−2λw2Sw - 2\lambda w, which vanishes when

Sw=λw.S w = \lambda w .

So ww must be an eigenvector of SS, and then the variance is w⊤Sw=λw⊤w=λw^\top S w = \lambda w^\top w = \lambda: the best direction is the eigenvector with the largest eigenvalue, v1v_1, the first principal component. The second component is the direction of largest variance orthogonal to the first. Writing any unit ww in the eigenbasis, w=∑jcjvjw = \sum_j c_j v_j with ∑jcj2=1\sum_j c_j^2 = 1, gives w⊤Sw=∑jλjcj2w^\top S w = \sum_j \lambda_j c_j^2; with c1=0c_1 = 0 forced by orthogonality, the maximum is λ2\lambda_2, at w=v2w = v_2. Continuing, the kk leading eigenvectors are the kk principal components, and the variance of the data along vjv_j is exactly λj\lambda_j.

The multivariate Gaussian note met the same eigenvectors as the axes of the covariance ellipse. PCA is that geometry estimated from data: the principal components are the axes of the ellipse that best describes the cloud, ordered by length.

PCA as reconstruction-error minimization#

Now ask a different question. Represent each point by kk numbers zi=W⊤xiz_i = W^\top x_i and reconstruct it as x^i=Wzi=WW⊤xi\hat x_i = W z_i = W W^\top x_i. Which WW makes the reconstructions closest to the data? Because WW⊤W W^\top is an orthogonal projection, the residual xi−x^ix_i - \hat x_i is orthogonal to x^i\hat x_i, and by Pythagoras

∥xi−WW⊤xi∥2=∥xi∥2−∥W⊤xi∥2.\lVert x_i - W W^\top x_i \rVert^2 = \lVert x_i \rVert^2 - \lVert W^\top x_i \rVert^2 .

Averaging over the points,

1n∑i=1n∥xi−x^i∥2=tr⁡S−tr⁡(W⊤SW).\frac1n \sum_{i=1}^n \lVert x_i - \hat x_i \rVert^2 = \operatorname{tr} S - \operatorname{tr}(W^\top S W) .

The first term is the total variance, fixed by the data; the second is the variance kept by the projection. Minimizing the reconstruction error is therefore the same problem as maximizing the kept variance, and its solution is the same: W=(v1,…,vk)W = (v_1, \dots, v_k), with minimal error

1n∑i=1n∥xi−x^i∥2=∑j>kλj,\frac1n \sum_{i=1}^n \lVert x_i - \hat x_i \rVert^2 = \sum_{j > k} \lambda_j ,

the sum of the discarded eigenvalues. (That the kk leading eigenvectors maximize tr⁡(W⊤SW)\operatorname{tr}(W^\top S W) over all orthonormal WW follows from the same eigenbasis argument applied to each column, or from Ky Fan’s theorem.) The two derivations are the two historical origins of PCA: Pearson (1901) looked for the line and plane of closest fit, Hotelling (1933) for components of maximal variance.

Figure

Two sides of one problem: variance and reconstruction error

A point cloud projected onto its first principal componentAn elongated cloud of points, a line along its long axis, the projections of the points onto it and the short residual segments.secondfirst componentresidualsA point cloud projected onto its first principal componentAn elongated cloud of points, a line along its long axis, the projections of the points onto it and the short residual segments.secondfirst componentresiduals
60 points from N(0, Σ), Σ = [[2, 1.2], [1.2, 1]]. The red line is the sample's first principal component: the eigenvector of the sample covariance with the largest eigenvalue, 2.59. The projections of the points onto it (red dots) have variance 2.59, the largest over all directions; the mean squared length of the residuals (amber segments) equals the second eigenvalue, 0.21, the smallest possible. The two add up to the total variance 2.80, so maximizing the first and minimizing the second are the same problem. The first component explains 92.4% of the sample variance (2.8 / 3 = 93.3% for the distribution itself).

Worked example. For the covariance Σ=(21.21.21)\Sigma = \begin{pmatrix} 2 & 1.2 \\ 1.2 & 1 \end{pmatrix} of the probability course, the eigenvalues are 2.82.8 and 0.20.2 and the first eigenvector is v1=(0.832,0.555)v_1 = (0.832, 0.555). The first component keeps 2.8/3=93.3%2.8 / 3 = 93.3\% of the variance, and reconstructing from it alone leaves a mean squared error of 0.20.2. The point x=(1,1)x = (1, 1) gets the coordinate z=v1⊤x=1.387z = v_1^\top x = 1.387 and the reconstruction x^=zv1=(1.154,0.769)\hat x = z v_1 = (1.154, 0.769); the residual (−0.154,0.231)(-0.154, 0.231) has squared length 0.0770.077, and indeed ∥x∥2=2=1.3872+0.077\lVert x \rVert^2 = 2 = 1.387^2 + 0.077.

Through the SVD#

Every matrix has a singular value decomposition X=UΣV⊤X = U \Sigma V^\top, with U∈Rn×rU \in \mathbb R^{n \times r} and V∈Rd×rV \in \mathbb R^{d \times r} having orthonormal columns and Σ\Sigma diagonal with σ1≥⋯≥σr>0\sigma_1 \ge \dots \ge \sigma_r > 0 (rr the rank). Then

S=1nX⊤X=1nVΣU⊤UΣV⊤=VΣ2nV⊤,S = \frac1n X^\top X = \frac1n V \Sigma U^\top U \Sigma V^\top = V \frac{\Sigma^2}{n} V^\top ,

an eigendecomposition: the principal components are the right singular vectors VV, the eigenvalues are λj=σj2/n\lambda_j = \sigma_j^2 / n, and the coordinates of all points are XV=UΣXV = U\Sigma. Two reasons to compute PCA this way. Forming X⊤XX^\top X squares the condition number, so small eigenvalues lose precision, while the SVD works on XX directly. And for large data a truncated or randomized SVD computes only the kk leading components, without the full d×dd \times d matrix.

The SVD also says that PCA is optimal in a stronger sense. By the Eckart–Young theorem (Eckart and Young, 1936), the best approximation of XX by any matrix of rank kk in the Frobenius norm is the truncated SVD Xk=UkΣkVk⊤X_k = U_k \Sigma_k V_k^\top, with error ∥X−Xk∥F2=∑j>kσj2\lVert X - X_k \rVert_F^2 = \sum_{j > k} \sigma_j^2. The PCA reconstruction XWW⊤X W W^\top with W=VkW = V_k is exactly XkX_k: no rank-kk linear summary of the data does better. Truncated SVD of a word–document count matrix is latent semantic analysis, the baseline of the GloVe note.

Explained variance and choosing k#

The share of variance kept by the first kk components is the explained variance ratio

λ1+⋯+λkλ1+⋯+λd,\frac{\lambda_1 + \dots + \lambda_k}{\lambda_1 + \dots + \lambda_d} ,

and one minus it is the relative reconstruction error. On the 1797 digit images of scikit-learn, 64 pixels each, the first two components explain 14.9% and 13.6% of the variance; 5 components explain half, 21 explain 90% and 29 explain 95%. With 21 components the mean squared reconstruction error per image is 116.3, the sum of the 43 discarded eigenvalues, as the derivation above predicts.

There is no single rule for kk. Common choices:

  • A share of variance, such as 90% or 95%. Simple, but the threshold is arbitrary.
  • The elbow of the scree plot, the eigenvalues in decreasing order: keep the components before the curve flattens into a floor of small, similar eigenvalues, which is what isotropic noise produces.
  • The downstream task: choose kk by the validation error of the model that uses the components.
  • The likelihood of probabilistic PCA (below), on held-out data or with a penalty like BIC.
Figure

How many components to keep

Eigenvalues and cumulative explained varianceThree tall bars and seven low ones at the noise level; the cumulative curve rises to 0.899 in three components and then almost linearly.eigenvalues (log scale)0.1110mean of the rest: 0.25012345678910component jcumulative explained variance0.40.60.8190%0.89912345678910component jEigenvalues and cumulative explained varianceThree tall bars and seven low ones at the noise level; the cumulative curve rises to 0.899 in three components and then almost linearly.eigenvalues (log scale)0.1110mean of the rest: 0.25012345678910component jcumulative explained variance0.40.60.8190%0.89912345678910component j
500 points in 10 dimensions generated by three latent factors and isotropic noise of variance 0.25. Left: the eigenvalues of the sample covariance, 8.98, 4.13, 2.41, then a floor of seven values between 0.31 and 0.19: the elbow is after the third component. The mean of the seven discarded values, 0.250, is the noise-variance estimate of probabilistic PCA, here equal to the true value. Right: the cumulative share of explained variance; three components explain 89.9%, and a 90% rule would keep four.

The figure shows the first two rules disagreeing. The data have three directions of signal and noise in all ten; the scree plot shows the three, but they explain only 89.9% of the variance, so a 90% rule keeps a fourth component that is pure noise.

Standardizing and whitening#

PCA depends on the units of the features. A feature measured in grams has a thousand times the standard deviation of the same feature in kilograms and dominates the first component. When features have unrelated units, standardize each to unit variance first; PCA then works with the correlation matrix instead of the covariance. When they share a unit, as pixels do, the raw covariance is usually right.

Whitening goes one step further and rescales each component to unit variance:

zi=Λk−1/2W⊤xi,Λk=diag⁡(λ1,…,λk).z_i = \Lambda_k^{-1/2} W^\top x_i, \qquad \Lambda_k = \operatorname{diag}(\lambda_1, \dots, \lambda_k) .

The new coordinates are uncorrelated with unit variance, 1n∑izizi⊤=Ik\frac1n \sum_i z_i z_i^\top = I_k, the sample version of the whitening map of the multivariate Gaussian note. Whitening helps methods that assume comparable, uncorrelated inputs, such as independent component analysis or distance-based methods. Its risk is the division by λj\sqrt{\lambda_j}: directions with tiny eigenvalues, often pure noise, are amplified to the same scale as the signal, so whitening is combined with truncation to kk components or with a small constant added to each λj\lambda_j.

Probabilistic PCA#

PCA as described is a projection, not a model: it assigns no probability to a point. Probabilistic PCA (Tipping and Bishop, 1999) is the continuous analogue of the Gaussian mixture. A latent vector z∼N(0,Ik)z \sim \mathcal N(0, I_k) is mapped linearly into the data space and blurred by isotropic noise, x=Wz+μ+εx = Wz + \mu + \varepsilon with ε∼N(0,σ2Id)\varepsilon \sim \mathcal N(0, \sigma^2 I_d); by the affine-map rule, x∼N(μ,WW⊤+σ2Id)x \sim \mathcal N(\mu, W W^\top + \sigma^2 I_d), a Gaussian whose covariance is “rank kk plus noise”. Its maximum likelihood solution is PCA in disguise: the columns of WW span the kk leading eigenvectors, W=Vk(Λk−σ2I)1/2RW = V_k (\Lambda_k - \sigma^2 I)^{1/2} R for any rotation RR, and the noise variance is the average of the discarded eigenvalues, σ2=1d−k∑j>kλj\sigma^2 = \frac{1}{d - k}\sum_{j > k} \lambda_j (Bishop, 2006, Section 12.2.1). In the scree figure that average is 0.2500.250, the true noise variance; for the digits with k=10k = 10 it is 5.835.83. The posterior mean of the latent vector is E[z∣x]=(W⊤W+σ2Ik)−1W⊤(x−μ)\mathbb E[z \mid x] = (W^\top W + \sigma^2 I_k)^{-1} W^\top (x - \mu). It is not the vector of principal coordinates Vk⊤(x−μ)V_k^\top (x - \mu), because WW holds scaled and rotated loadings, not the orthonormal eigenvectors: as σ2→0\sigma^2 \to 0 it tends to R⊤Λk−1/2Vk⊤(x−μ)R^\top \Lambda_k^{-1/2} V_k^\top (x - \mu), the principal coordinates divided by their standard deviations and rotated by RR. What becomes the PCA projection is the reconstruction in data space, W E[z∣x]+μ→VkVk⊤(x−μ)+μW \, \mathbb E[z \mid x] + \mu \to V_k V_k^\top (x - \mu) + \mu. The model can also be fitted by EM, which avoids the d×dd \times d covariance and handles missing values, and because it is a density it gives a likelihood for choosing kk and a score for unusual points, the starting point of the next note.

Kernel PCA#

PCA finds linear structure only. Two concentric circles have no direction of large variance that separates them: every projection to a line mixes the rings. Kernel PCA (Schölkopf, Smola and Müller, 1998) runs PCA in a feature space φ(x)\varphi(x) reached through a kernel k(x,x′)=φ(x)⊤φ(x′)k(x, x') = \varphi(x)^\top \varphi(x'), as in the kernels note. The principal directions in feature space are combinations of the mapped points, ∑iαiφ(xi)\sum_i \alpha_i \varphi(x_i), and the eigenvalue problem becomes one for the n×nn \times n kernel matrix Kij=k(xi,xj)K_{ij} = k(x_i, x_j), centred in feature space,

K~=K−1nK−K1n+1nK1n,K~α=nλα,\tilde K = K - \mathbf 1_n K - K \mathbf 1_n + \mathbf 1_n K \mathbf 1_n , \qquad \tilde K \alpha = n \lambda \alpha ,

where 1n\mathbf 1_n is the n×nn \times n matrix with all entries 1/n1/n and α\alpha is scaled so that the direction has unit length, nλ α⊤α=1n\lambda\, \alpha^\top \alpha = 1. The coordinate of a point on a component is ∑iαik~(xi,x)\sum_i \alpha_i \tilde k(x_i, x), computed with kernels only. With k(x,x′)=(x⊤x′)2k(x, x') = (x^\top x')^2 the feature space contains x12x_1^2, x22x_2^2 and x1x2x_1 x_2, so the squared radius x12+x22x_1^2 + x_2^2, which separates the circles, is a linear function there. In the Code section, a logistic regression on two ordinary principal components of the circles reaches a training accuracy of 0.502, chance, and on three kernel components 1.0. Both are fitted and scored on the same 400 points, so they show what each representation can express linearly, not a test score. Only the third kernel component separates the rings by itself: kernel PCA ranks directions by variance in feature space, not by usefulness. The price is an n×nn \times n eigenproblem, and the components have no simple interpretation in the input space.

t-SNE and UMAP: neighbourhoods instead of variance#

For visualization, preserving large distances is often the wrong goal. A 2-D linear projection of the digits keeps the directions of largest spread, and the ten digit classes overlap heavily. t-SNE (van der Maaten and Hinton, 2008) asks instead that points close in the data stay close in the plane.

  1. Turn distances in the data into neighbour probabilities. For each point xix_i, pj∣i∝exp⁡(−∥xi−xj∥2/2σi2)p_{j \mid i} \propto \exp\big(-\lVert x_i - x_j \rVert^2 / 2\sigma_i^2\big), normalized over j≠ij \ne i, where each bandwidth σi\sigma_i is chosen so that the distribution has a set perplexity, 2H(p⋅∣i)2^{H(p_{\cdot \mid i})}, roughly the number of neighbours that count; typical values are 5 to 50. Symmetrize: pij=(pj∣i+pi∣j)/2np_{ij} = (p_{j \mid i} + p_{i \mid j}) / 2n.
  2. Define the same kind of probabilities for points yiy_i in the plane, with a heavy-tailed Student-tt kernel: qij∝(1+∥yi−yj∥2)−1q_{ij} \propto (1 + \lVert y_i - y_j \rVert^2)^{-1}, normalized over all pairs.
  3. Move the yiy_i by gradient descent to minimize DKL(P ∥ Q)=∑i≠jpijlog⁡(pij/qij)D_{\mathrm{KL}}(P \,\|\, Q) = \sum_{i \ne j} p_{ij} \log (p_{ij} / q_{ij}).

The asymmetry of the KL divergence decides what is preserved. A pair with large pijp_{ij}, true neighbours, placed far apart (small qijq_{ij}) costs a lot; a pair with small pijp_{ij} placed close costs little. t-SNE keeps neighbourhoods and is free with everything else. The heavy tail answers the crowding problem: a point in 64 dimensions can have many equidistant neighbours, a point in the plane cannot, and the tt kernel lets moderately distant points be pushed far away instead of piling up in the middle. In the Code section, 58.4% of the 10 nearest neighbours of each digit image in 64 dimensions are among its 10 nearest neighbours in a t-SNE map, against 11.8% in the 2-D PCA projection.

UMAP (McInnes, Healy and Melville, 2018) follows the same plan with different choices: a weighted kk-nearest-neighbour graph in the data, a similar graph in the plane, and a cross-entropy between their edge weights optimized by stochastic gradient descent. It is much faster, scales to millions of points and can embed new points, and it is often said to keep more global structure; the reading rules below apply to it as well.

How to read such maps (Wattenberg, Viégas and Johnson, 2016):

  • Hyperparameters really matter. The perplexity (for UMAP, the number of neighbours) changes the picture; look at several values.
  • Cluster sizes mean nothing. t-SNE expands dense clusters and contracts sparse ones, so the area of a cluster says nothing about its spread in the data.
  • Distances between clusters may mean nothing. Two clusters far apart in the map may be close in the data, and the other way round.
  • Random noise can look clustered, especially at low perplexity.
  • Shapes and topology need more than one plot. A ring or a line in the map may be an artefact of one run.

What a map shows reliably is which points are neighbours. It is for looking, not for measuring: its axes have no meaning, there is no map of new points in plain t-SNE, and its coordinates are poor features for a model. The autoencoder note reads t-SNE plots of learned codes with exactly these cautions, and the word embeddings note compares PCA and t-SNE projections of word vectors.

Autoencoders: nonlinear PCA#

PCA is the best linear encoder–decoder pair: z=W⊤xz = W^\top x, x^=Wz\hat x = Wz, trained to minimize squared reconstruction error. Training a linear network with a kk-unit bottleneck on the same loss by gradient descent reaches the same subspace: its minimum spans the principal subspace, and it has no other local minima, only saddle points (Baldi and Hornik, 1989). Replacing the two linear maps by neural networks, an encoder z=fφ(x)z = f_\varphi(x) and a decoder x^=gθ(z)\hat x = g_\theta(z), gives an autoencoder, which can follow a curved surface instead of a flat subspace. The autoencoder note of the generative models course builds convolutional autoencoders for images on this idea, and the probabilistic version, with the encoder as an approximate posterior, is the variational autoencoder, probabilistic PCA made nonlinear.

What changed#

PCAProbabilistic PCAKernel PCAt-SNE / UMAPAutoencoder
Maplinear, z=W⊤xz = W^\top xlinear, with a densitylinear in feature spacenone: positions optimizedneural encoder and decoder
Preservesvariance, large distancesthe same, plus a likelihoodvariance in feature spaceneighbourhoodswhat reconstruction needs
New pointsprojectprojectkernels with the training setnot in plain t-SNEencode
SolutionSVD, exactclosed form or EMn×nn \times n eigenproblemnon-convex, randomnon-convex, gradient descent
Usecompression, denoising, featuresthe same, density, missing datanonlinear featuresvisualization onlyrepresentations, generation

Limitations#

  • Linear and variance-driven. PCA keeps the directions of largest variance, which need not be the directions that matter for a task; a small-variance direction can carry all the class information.
  • Sensitive to scale and outliers. Units decide the components, and a single far point can capture one; robust PCA variants exist for that.
  • Components are hard to interpret. Each is a mixture of all features, with signs fixed only up to ±1\pm 1.
  • Nonlinear maps are for looking. t-SNE and UMAP distort sizes and distances, depend on hyperparameters and random seeds, and plain t-SNE gives no function for new points. UMAP has one (transform in the umap-learn library), but the new positions inherit the same distortions; a pipeline that feeds an embedding to a model must fit it on the training split only.
  • The intrinsic dimension is a modelling choice. Every rule for kk encodes an assumption about what is signal and what is noise.

What you should remember#

  • PCA projects centred data onto the leading eigenvectors of the covariance SS; the variance along vjv_j is λj\lambda_j.
  • Maximizing kept variance and minimizing squared reconstruction error are the same problem, since 1n∑∥xi−x^i∥2=tr⁡S−tr⁡(W⊤SW)\frac1n\sum \lVert x_i - \hat x_i\rVert^2 = \operatorname{tr} S - \operatorname{tr}(W^\top S W); the minimal error is ∑j>kλj\sum_{j > k} \lambda_j.
  • Compute PCA by the SVD X=UΣV⊤X = U\Sigma V^\top: components VV, eigenvalues σj2/n\sigma_j^2/n, coordinates UΣU\Sigma; by Eckart–Young no rank-kk approximation is better.
  • Choose kk by explained variance, the scree elbow, the downstream task or the likelihood; the rules can disagree.
  • Standardize features with different units; whitening rescales components to unit variance and amplifies noise directions.
  • Probabilistic PCA, x∼N(μ,WW⊤+σ2I)x \sim \mathcal N(\mu, WW^\top + \sigma^2 I), has PCA as its maximum likelihood solution and σ2\sigma^2 the mean of the discarded eigenvalues.
  • Kernel PCA is PCA of the centred kernel matrix: nonlinear components at the cost of an n×nn \times n eigenproblem.
  • t-SNE and UMAP preserve neighbourhoods; cluster sizes and gaps between clusters in their maps carry little information.
  • A linear autoencoder learns the PCA subspace; a nonlinear one generalizes it.

Code#

PCA by the SVD, explained variance and reconstruction error on the digits, probabilistic PCA, whitening, kernel PCA, and neighbourhoods in PCA and t-SNE maps:

import numpy as np
from sklearn.datasets import load_digits, make_circles
from sklearn.decomposition import PCA, KernelPCA
from sklearn.manifold import TSNE
from sklearn.neighbors import NearestNeighbors
from sklearn.linear_model import LogisticRegression

# the population: eigenvalues of the covariance and the share of the first component
S = np.array([[2.0, 1.2], [1.2, 1.0]])
lam = np.linalg.eigvalsh(S)[::-1]
print(lam.round(3), round(lam[0] / lam.sum(), 3))                  # [2.8 0.2] 0.933

# PCA of a sample through the SVD of the centred data matrix: the same as the eigenvectors of the covariance
rng = np.random.default_rng(0)
X = rng.multivariate_normal([0, 0], S, size=2000)
Xc = X - X.mean(axis=0)
U, s, Vt = np.linalg.svd(Xc, full_matrices=False)
ev = np.linalg.eigvalsh(Xc.T @ Xc / len(X))[::-1]
print((s ** 2 / len(X)).round(3), ev.round(3), np.abs(Vt[0]).round(3))   # [2.812 0.198] [2.812 0.198] [0.826 0.563]

# digits: 1797 images of 8 x 8 pixels; share of the first two components, components needed for 50%, 90%, 95%
D = load_digits().data
p = PCA().fit(D)
cum = np.cumsum(p.explained_variance_ratio_)
print(p.explained_variance_ratio_[:2].round(3), [int(np.searchsorted(cum, q) + 1) for q in (0.5, 0.9, 0.95)])
# [0.149 0.136] [5, 21, 29]

# reconstruction error with k components = the sum of the discarded eigenvalues (per image, with the 1/n covariance)
for k in (2, 10, 21):
    pk = PCA(k).fit(D)
    err = ((D - pk.inverse_transform(pk.transform(D))) ** 2).sum(axis=1).mean()
    lam_n = p.explained_variance_ * (len(D) - 1) / len(D)
    print(k, round(err, 1), round(lam_n[k:].sum(), 1), round(cum[k - 1], 3))
# 2 858.9 858.9 0.285 | 10 314.5 314.5 0.738 | 21 116.3 116.3 0.903

# probabilistic PCA: the noise variance is the mean of the discarded eigenvalues
p10 = PCA(10).fit(D)
print(round(p10.noise_variance_, 3), round(p.explained_variance_[10:].mean(), 3))   # 5.828 5.828
# whitening: the rescaled coordinates have identity covariance
Z = PCA(10, whiten=True).fit_transform(D)
print(np.allclose(np.cov(Z.T), np.eye(10)))                         # True

# kernel PCA on two concentric circles: training accuracy of a linear classifier on the components
Xr, yr = make_circles(n_samples=400, factor=0.4, noise=0.05, random_state=0)
acc = lambda z: round(LogisticRegression().fit(z, yr).score(z, yr), 3)
z_lin = PCA(2).fit_transform(Xr)
z_ker = KernelPCA(3, kernel='poly', degree=2, gamma=1, coef0=0).fit_transform(Xr)   # k(x, y) = (x . y)^2
print(acc(z_lin), acc(z_ker), [acc(z_ker[:, [j]]) for j in range(3)])   # 0.502 1.0 [0.53, 0.535, 1.0]

# neighbourhoods: share of each image's 10 nearest neighbours in 64-D that stay among its 10 nearest in 2-D
def kept(A, B, k=10):
    ia = NearestNeighbors(n_neighbors=k + 1).fit(A).kneighbors(A, return_distance=False)[:, 1:]
    ib = NearestNeighbors(n_neighbors=k + 1).fit(B).kneighbors(B, return_distance=False)[:, 1:]
    return np.mean([len(set(a) & set(b)) / k for a, b in zip(ia, ib)])
for name, E in (('pca', PCA(2).fit_transform(D)), ('t-sne', TSNE(2, perplexity=30, random_state=0).fit_transform(D))):
    print(name, round(kept(D, E), 3))                               # pca 0.118 | t-sne 0.584

Next#

Probabilistic PCA and the Gaussian mixture are densities: they say not only where the data are, but how probable each point is. A point that the model finds very improbable, or that lies far from the subspace where the data live, is unusual. The next note turns that observation into methods for finding anomalies: density scores, distances to neighbours, random trees that isolate odd points quickly, and one-class support vector machines, and asks how to evaluate a detector when anomalies are rare.

References#