ml-models / Classical ML / Principal component analysis core

Principal component analysis

PCA finds the few directions along which a dataset varies most and re-expresses every point in those coordinates. This page derives it as variance maximization, computes it through the SVD, and works through explained variance, choosing how many components to keep, whitening and reconstruction, then explains what t-SNE and UMAP do when the structure is not linear and why their pictures are easy to misread. Every number is computed, and the links point at the scikit-learn and PyTorch code that does the same thing.

On this page
  1. Problem
  2. Intuition
  3. Mechanics
  4. Variance-maximizing directions
  5. PCA via the SVD
  6. Explained variance and choosing k
  7. Projection and reconstruction
  8. Whitening
  9. Scaling comes first
  10. Nonlinear alternatives: t-SNE and UMAP
  11. Worked example
  12. Components from the SVD
  13. Projecting, reconstructing and whitening
  14. Why scaling matters
  15. Choosing k on a survey
  16. Where a linear projection fails
  17. Implementation
  18. Tradeoffs
  19. Connections
  20. Read first
  21. Read next
  22. References

Problem

A dataset with 300 features is hard to plot, slow to search and full of redundancy: survey questions that measure the same attitude, pixels that move together, sensor channels that track each other. You want fewer numbers per row that keep most of the information, to visualize the data, to speed up a downstream model, to remove noise, or to make clustering and nearest-neighbour search work in fewer dimensions.

"Most of the information" needs a definition. PCA's answer: keep the k-dimensional linear subspace that preserves the most variance, which is the same as the subspace that reconstructs the points with the smallest squared error. That choice is cheap to compute and easy to invert, but it is linear and it is sensitive to the units of each feature, and both limits come up below.

Intuition

Picture a cloud of points shaped like a tilted cigar. Its long axis is the direction where points differ most; knowing only where a point sits along that axis already tells you most of where it is. That axis is the first principal component. The second is the longest axis at right angles to the first, and so on. PCA is a rotation of the coordinate system so that the axes line up with the cloud, sorted from widest to thinnest. Dropping the thin axes is the compression: every point is projected onto the wide ones.

Principal axes of 8 points and their projections onto the first one Left: 8 points in the plane spread along a diagonal. Their mean (3.19, 3.26) is marked with a cross. The first principal component, direction (0.70, 0.71), runs along the diagonal and carries 96.3% of the variance; the second, (0.71, −0.70), is perpendicular and carries 3.7%. Dashed segments drop each point onto the first axis; their squared lengths sum to the reconstruction error 0.937. Right: the same points as one number each, their coordinate along the first axis, from −2.79 to 2.72. PC1 PC2 x, y: the data one number per point: PC1 score −2 −1 0 1 2 keeps 96.3% of the variance squared error 0.937 = S₂²

Projecting onto PC1 turns each pair of numbers into one and loses only the short dashed offsets, the part of each point that lies along PC2.

Two things the picture hides. First, PCA only sees straight lines: if the data lies on a curve, no rotation lines an axis up with it, and points far apart along the curve can land on top of each other. Second, "widest" depends on units: measure one feature in grams and another as a fraction, and the grams axis wins whatever the data says. Both are worked through below.

Mechanics

Variance-maximizing directions

Stack n points of d features as rows of X and subtract the column means, giving the centred matrix Xc. The sample covariance is

C = Xcᵀ Xc / (n − 1)

The variance of the data projected onto a unit vector w is wᵀ C w. Maximizing it subject to ‖w‖ = 1 with a Lagrange multiplier λ gives C w = λ w: the best direction is an eigenvector of C, and the variance along it is its eigenvalue, so the first principal component is the eigenvector with the largest eigenvalue. Requiring the next direction to be orthogonal to the first gives the second eigenvector, and so on. Because C is symmetric, its eigenvectors are orthogonal and the eigenvalues are real and non-negative (see the linear algebra toolkit).

The same subspace also minimizes the squared reconstruction error. For any unit w, ‖x‖² = (wᵀx)² + ‖x − w wᵀx‖², so for centred data, maximizing the variance kept is the same as minimizing what is thrown away. That is why "PCA" can be introduced either way.

PCA via the SVD

Forming C squares the condition number, so libraries usually skip it. Take the singular value decomposition Xc = U S Vᵀ. Then

C = V S² Vᵀ / (n − 1)

so the rows of Vᵀ are the principal directions (scikit-learn's components_), the eigenvalues are λj = Sj² / (n − 1) (explained_variance_), and the coordinates of the points in the new basis, the scores, are Xc V = U S. Eigenvectors are only defined up to sign, so the same data could return +v or −v. scikit-learn's svd_flip fixes the sign so that the largest-magnitude entry of each row of Vᵀ is positive, which makes results reproducible across runs and LAPACK builds.

scikit-learn's PCA picks among four solvers when svd_solver="auto": "covariance_eigh" (form the d × d covariance and diagonalize it) when there are at most 1,000 features and at least ten times as many rows as features; otherwise "full" (an exact LAPACK SVD) when the larger dimension is at most 500 or n_components="mle"; otherwise "randomized" when 1 ≤ k < 0.8 · min(n, d); and "full" in the remaining cases. The covariance route is fast for tall, thin data and is safe there because the d × d matrix is small; it clips tiny negative eigenvalues caused by rounding to zero.

The randomized solver (Halko, Martinsson and Tropp) multiplies Xc by a random d × (k + p) Gaussian matrix, with oversampling p (n_oversamples=10), sharpens the result with a few power iterations XcXcᵀ, orthonormalizes it into a basis Q, and takes the exact SVD of the small matrix Qᵀ Xc. When the spectrum decays, the top k components come out accurate at a fraction of the cost. PyTorch's torch.pca_lowrank does the same: it centres the columns, uses q = min(6, m, n) by default and 2 power iterations, and returns U, S, V, leaving the S² / (m − 1) step to you.

Explained variance and choosing k

The share of the total variance that component j accounts for is

ratioj = λj / Σi λi

A scree plot shows these ratios in order; the cumulative sum tells you how much a k-component projection keeps. Common ways to pick k:

  • A variance threshold. Pass a float such as n_components=0.9, and scikit-learn keeps the smallest k whose cumulative ratio is strictly greater than 0.9 (a searchsorted(..., side="right") + 1 on the cumulative sum).
  • The elbow of the scree plot: the point where the ratios flatten into a floor of noise.
  • Minka's MLE (n_components="mle"): treat the data as a low-rank signal plus isotropic Gaussian noise and pick the rank with the highest approximate Bayesian evidence. It needs n ≥ d.
  • The downstream task. If a classifier on top is what matters, cross-validate k like any other hyperparameter.

After fitting with k components, scikit-learn stores noise_variance_, the mean of the dropped eigenvalues. It is the noise estimate of probabilistic PCA (Tipping and Bishop), which treats each point as W z + μ + ε with z ~ N(0, I) and ε ~ N(0, σ² I); that model is what score and get_covariance use.

Projection and reconstruction

Keeping the first k rows Vkᵀ of Vᵀ, a point x maps to k scores and back:

z = Vkᵀ (x − μ)     x̂ = μ + Vk z

Over the training set, the total squared reconstruction error Σ‖xi − x̂i‖² is exactly the sum of the dropped squared singular values, Sk+1² + … + Sr², or (n − 1) times the dropped eigenvalues: Eckart–Young says no other rank-k projection does better. Reconstruction is also how PCA denoises: noise spreads its variance over every direction, while signal concentrates in a few, so projecting onto the top components keeps most of the signal and removes most of the noise. A point whose reconstruction error is far above the training points' is unlike them, a cheap anomaly score.

Whitening

Whitening divides each score by the standard deviation along its component, so that the new features are uncorrelated and all have variance 1:

zj = vjᵀ (x − μ) / √λj

The covariance of whitened data is the identity. That helps models that assume isotropic, equally scaled inputs, such as RBF-kernel SVMs, k-means or ICA. The cost: low-variance directions, which are often mostly noise, get blown up to the same size as the signal, and inverse_transform has to undo the scaling. In scikit-learn, whiten=True changes transform but not components_; the division uses √λ clipped below at machine epsilon so a zero-variance direction does not divide by zero.

Scaling comes first

Variance has units. A feature in grams with a spread of tens has a variance thousands of times larger than a fraction that varies in the second decimal, and PC1 will simply be "the grams feature". Unless features already share a unit and a meaningful scale (pixel intensities, say), standardize them first with a StandardScaler. PCA on standardized data is PCA of the correlation matrix rather than the covariance matrix.

Nonlinear alternatives: t-SNE and UMAP

When the data lies on a curved surface, any linear projection flattens it by squashing, which can put far-apart points on top of each other. Neighbour-embedding methods drop the goal of a global linear map and instead try to keep each point's nearest neighbours near it in 2 or 3 dimensions.

t-SNE (van der Maaten and Hinton) turns distances in the original space into neighbour probabilities with a Gaussian around each point, whose width is tuned so that every point has the same perplexity (roughly, an effective number of neighbours, 30 by default), and symmetrizes them into pij. In the embedding it uses a heavy-tailed Student-t kernel, qij ∝ (1 + ‖yi − yj‖²)−1 for a 2-D output, and moves the points y by gradient descent to minimize KL(P ‖ Q). The heavy tail is the trick: a 2-D map has too little room for all the moderately distant points of a high-dimensional space (the "crowding problem"), and the t kernel lets them sit much farther apart than a Gaussian would allow. scikit-learn's TSNE runs the first 250 iterations with the pij multiplied by an early exaggeration of 12, which pulls clusters together; uses the Barnes-Hut approximation with about 3 × perplexity neighbours per point by default; starts from a PCA projection scaled down so PC1 has standard deviation 10⁻⁴; and sets the learning rate to max(n / 12 / 4, 50).

UMAP (McInnes, Healy and Melville) builds a weighted k-nearest-neighbour graph (n_neighbors, 15 by default in the reference implementation) whose edge weights fall off with distance past each point's nearest neighbour, then lays out a low-dimensional graph to minimize the cross-entropy between the two, with negative sampling. min_dist sets how tightly points may pack. It is usually faster than t-SNE, keeps somewhat more of the global arrangement, and can embed new points with transform; it is a separate package (umap-learn), not part of scikit-learn.

Caveats for both, which matter more than the differences:

  • Cluster sizes mean nothing. Each point's neighbourhood is rescaled to the same effective size, so dense and sparse clusters come out about equally wide.
  • Distances between clusters mean little. The loss barely penalizes moving far-apart groups, so the gap between two islands is not a measure of how different they are.
  • The hyperparameters change the picture. Different perplexities or n_neighbors values give different-looking maps, and random noise at low perplexity can show apparent clusters.
  • They are stochastic and, for t-SNE, non-parametric. Different seeds give different maps; scikit-learn's TSNE has no transform for new points.
  • They are for looking, not for features. Use the coordinates as model inputs only with care; PCA or a learned embedding is usually the better pipeline step, and running PCA to about 50 dimensions before t-SNE is standard to cut noise and cost.
ComponentShape/ParamsMeaningSource
PCA n_components None, whiten False, svd_solver "auto", tol 0, iterated_power "auto", n_oversamples 10; components_ (k, d), explained_variance_ (k,), mean_ (d,) The estimator. n_components may be an int, a float in (0, 1) for a variance threshold, or "mle". _pca.py 113-852, 399-421
PCA._fit X (n, d) Validates the input and picks the solver with the "auto" rule above. _pca.py 481-542
PCA._fit_full "full" or "covariance_eigh" Centres, runs an exact SVD (or eigh of the covariance), flips signs, sets explained_variance_ = S² / (n − 1), resolves a float or "mle" n_components, and sets noise_variance_ to the mean of the dropped eigenvalues. _pca.py 544-694
PCA._fit_truncated "arpack" or "randomized" Computes only k components; noise_variance_ comes from the total variance minus the kept part. _pca.py 696-788
PCA.fit_transform → (n, k) Returns U · S, or U · √(n − 1) when whitening, without a second pass over the data. _pca.py 444-479
_assess_dimension, _infer_dimension spectrum (d,), n Minka's evidence for each rank; the argmax is n_components="mle". _pca.py 25-96, 99-110
transform, inverse_transform (n, d) ↔ (n, k) (x − μ) Vₖ, divided by √λ when whitening; the inverse multiplies back and adds μ. _base.py 125-175, 177-214
get_covariance → (d, d) The probabilistic-PCA covariance Vₖ diag(λ − σ²) Vₖᵀ + σ² I. _base.py 34-60
svd_flip u_based_decision False for PCA Makes the largest-magnitude entry of each row of Vᵀ positive. extmath.py 924-982
randomized_svd, randomized_range_finder n_oversamples 10, n_iter "auto" Random projection, power iterations, QR, then an exact SVD of the small matrix. extmath.py 402-557, 241-310
torch.pca_lowrank, get_approximate_basis q = min(6, m, n), center True, niter 2; returns U, S, V Halko's randomized range finder and SVD on tensors, dense or sparse. _lowrank.py 183-293, 11-82
TSNE n_components 2, perplexity 30, early_exaggeration 12, learning_rate "auto", max_iter 1000, init "pca", method "barnes_hut", angle 0.5 The t-SNE estimator; fit_transform only. _t_sne.py 557-1174, 807-840
_joint_probabilities, _kl_divergence P (n·(n−1)/2,), embedding (n, 2) Symmetrized Gaussian neighbour probabilities; the KL objective with a Student-t kernel and its gradient. _t_sne.py 35-65, 125-199
The rows of Vᵀ from the SVD of the centred data are the principal directions. Why must you centre first?

Without centring, the SVD finds directions that maximize the mean of (wᵀx)², the second moment about the origin, not the variance. If the data sits far from the origin, the first "component" just points at the mean, and every later direction is forced orthogonal to it. Subtracting μ makes XᵀX / (n − 1) the covariance, which is what the variance argument needs.

Worked example

Components from the SVD

Data. 8 points: (1.0, 1.5), (2.0, 1.8), (3.0, 3.4), (4.0, 3.6), (5.0, 5.3), (2.5, 3.0), (3.5, 2.6), (4.5, 4.9). Their mean is (3.1875, 3.2625) and the sample covariance is

C = [[1.7812, 1.6652], [1.6652, 1.8170]]

The SVD of the centred 8 × 2 matrix gives singular values S = (4.9245, 0.9679) and directions v₁ = (0.7033, 0.7109), v₂ = (0.7109, −0.7033). Dividing S² by n − 1 = 7 gives the explained variances 3.4644 and 0.1338: the eigenvalues of C, and they sum to its trace, 3.5982. The ratios are 0.9628 and 0.0372. v₁ is almost the 45° diagonal because the two features have nearly equal variances and a strong covariance.

Power iteration on C from (1, 0) finds the same direction without an SVD, by repeatedly computing v ← C v / ‖C v‖ and reading off the variance vᵀ C v:

Stepvvᵀ C v
1(0.730507, 0.682905)3.4593111
2(0.704377, 0.709826)3.4643739
3(0.703347, 0.710847)3.4643814
4(0.703307, 0.710886)3.4643815
5(0.703305, 0.710888)3.4643815

The direction's error shrinks by about λ₂ / λ₁ = 0.0386 per step and the variance's by its square, so the variance is right to 7 digits after 4 steps. Randomized solvers use the same idea on blocks of vectors.

Projecting, reconstructing and whitening

The new point (3, 2) has scores z = Vᵀ(x − μ) = (−1.0294, 0.7546). Keeping only PC1 and mapping back gives μ + z₁ v₁ = (2.4635, 2.5307), the nearest point on the PC1 line. Doing that for all 8 training points loses a total squared error of 0.9368, which is exactly S₂² = 0.9368.

Whitening divides the scores by √λ = (1.8613, 0.3658). The first point's scores (−2.7914, −0.3155) become (−1.4997, −0.8624): its small PC2 offset is now more than half the size of its PC1 coordinate. The covariance of the 8 whitened points is [[1.0000, 0.0000], [0.0000, 1.0000]], the identity.

Why scaling matters

Six fruits with weight (g) and sugar fraction: (120, 0.10), (150, 0.12), (180, 0.11), (140, 0.14), (200, 0.13), (160, 0.09). The standard deviations are 26.09 g and 0.0171.

InputPC1Ratio PC1Ratio PC2
raw(1.00000, 0.00013)0.99999960.0000004
standardized(0.70711, 0.70711)0.60290.3971

On raw data PC1 is the weight axis, carrying all but 0.00004% of the variance: the sugar feature is invisible because its numbers are small, not because it is uninformative. After standardizing, both features count equally and PC1 is the 45° diagonal with 60.3% of the variance; with two standardized features the components are always ±45°, and only the ratio says how correlated they are.

Choosing k on a survey

30 answers to 6 questions, generated (seed 11) from two hidden factors: questions 1-3 load mostly on the first, 4-5 on the second, 6 on both, plus independent noise with standard deviation 0.25 (variance 0.0625). PCA does not know any of that.

kλkRatioCumulativeSquared error keeping knoise_variance_
0 — — 0 193.638 1.1129
1 4.9408 0.7400 0.7400 50.354 0.3473
2 1.4472 0.2167 0.9567 8.386 0.0723
3 0.1375 0.0206 0.9773 4.400 0.0506
4 0.0668 0.0100 0.9873 2.461 0.0424
5 0.0456 0.0068 0.9941 1.138 0.0392
6 0.0392 0.0059 1.0000 0.000 —

Two components keep 95.7% of the variance, so n_components=0.9 keeps k = 2: one component (74.0%) is not strictly above 0.9. The scree plot has a clear elbow at 2 as well, and the four dropped eigenvalues sit in a flat floor between 0.0392 and 0.1375. Their mean, the noise estimate at k = 2, is 0.0723, close to the true noise variance 0.0625. Each squared error equals the sum of the dropped S², and with all 6 kept it is zero.

The two kept directions are v₁ = (0.57, 0.54, 0.39, 0.26, 0.07, 0.38) and v₂ = (−0.21, −0.10, −0.32, 0.64, 0.62, 0.22). They span the same plane as the two factors but are not the factors themselves: PCA only promises orthogonal directions of decreasing variance, so v₁ mixes in every question. Recovering interpretable factors needs a rotation such as varimax (factor analysis) or a different method such as ICA.

Scree plot of the survey: explained variance ratio and cumulative share for 6 components Bars give each component's share of the variance: 0.740, 0.217, 0.021, 0.010, 0.007, 0.006. A line through the bar tops gives the cumulative share: 0.740, 0.957, 0.977, 0.987, 0.994, 1.000. A dashed horizontal line at 0.9 is first crossed by the cumulative line at component 2, which is highlighted. Components 3 to 6 form a flat floor. 0.0 0.5 1.0 threshold 0.9 PC1 0.74 PC2 0.22 PC3 0.02 PC4 0.01 PC5 0.01 PC6 0.01 cumulative bars: share per component

Two big components, then a flat floor of noise: the elbow and the 0.9 threshold agree on k = 2 here. On real data the floor is rarely this flat.

Where a linear projection fails

13 points evenly spaced along an arc of radius 2.0 from −45° to 225°, three quarters of a circle. The arc is symmetric about the vertical axis, so PCA's components are the coordinate axes, and PC1 (the horizontal axis) keeps 62.3% of the variance. Along PC1, the points at −45° and 45° both land at 1.414: a gap of 0.000, though they are 2.828 apart in the plane and 3.142 apart along the arc. Four such pairs collide. A neighbour embedding unrolls the arc instead, because it only asks that each point keep its two neighbours close.

The t-SNE kernel. How much affinity a pair keeps at distance d in the map, with a Gaussian kernel exp(−d²/2) and t-SNE's Student-t kernel 1 / (1 + d²):

dGaussianStudent-tt / Gaussian
0.01.0000001.00001.00
0.50.8824970.80000.91
1.00.6065310.50000.82
2.00.1353350.20001.48
3.00.0111090.10009.00
5.00.0000040.038510321

At d = 3 the t kernel still gives 0.10 where the Gaussian has fallen to 0.0111, 9.0 times more. So a modest affinity pij can be matched by points placed much farther apart in the map than a Gaussian would allow, which opens up the gaps between clusters. It is also why those gaps should not be read as distances.

PCA projection of an arc folds distant points onto each other Left: 13 points numbered 0 to 12 along a three-quarter circle from −45° to 225°, with the horizontal PC1 axis drawn through their mean. Points 0 and 4 are highlighted. Right: each point placed at its PC1 coordinate, one row per point. Points 0 and 4 share the coordinate 1.414, as do several other pairs from opposite sides of the arc, so the order along the arc is lost. PC1 0 1 2 3 4 5 6 7 8 9 10 11 12 PC1 coordinate, one row per point −2 −1 0 1 2 0 1 2 3 4 5 6 7 8 9 10 11 12

Points 0 and 4 are a quarter of the circle apart along the arc, yet PCA gives them the same coordinate; so do 1 and 3, 9 and 11, and 8 and 12.

Implementation

import numpy as np
from sklearn.decomposition import PCA
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

pca = make_pipeline(StandardScaler(), PCA(n_components=0.9))
Z = pca.fit_transform(X)                       # (n, k), k chosen by the 0.9 threshold
p = pca[-1]
p.components_, p.explained_variance_ratio_     # (k, d) rows of Vᵀ, shares of variance
X_back = pca.inverse_transform(Z)              # reconstruction in the original units

# The same thing from the SVD:
def pca_svd(X, k):
    mu = X.mean(0)
    U, S, Vt = np.linalg.svd(X - mu, full_matrices=False)
    flip = np.sign(Vt[np.arange(len(Vt)), np.abs(Vt).argmax(1)])
    U, Vt = U * flip, Vt * flip[:, None]       # svd_flip: largest entry of each row positive
    var = S**2 / (len(X) - 1)                  # explained_variance_
    return mu, Vt[:k], var[:k], (U * S)[:, :k]  # mean, components, variances, scores

# PyTorch, randomized, on a GPU tensor:
#   U, S, V = torch.pca_lowrank(A, q=k, center=True, niter=2)
#   scores = A_centred @ V[:, :k];  variances = S**2 / (A.shape[0] - 1)

Fit the scaler and PCA on the training split only and reuse them for validation and test data, or information leaks across the split; a pipeline does that automatically. For data that does not fit in memory, IncrementalPCA updates the components batch by batch; for sparse matrices such as TF-IDF, TruncatedSVD skips the centring, which would make the matrix dense. For a 2-D picture of high-dimensional data, reduce to about 50 dimensions with PCA first, then run t-SNE or UMAP on that, and try more than one perplexity or n_neighbors and seed before trusting any structure you see.

Tradeoffs

MethodMapPreservesNew pointsCostUse for
PCA (full SVD)linear, orthogonalglobal variance; exact best rank-k reconstructionyes, a matrix productO(n·d·min(n, d))compression, denoising, a preprocessing step, a first look
Randomized PCAlinear, approximatetop k components when the spectrum decaysyesabout O(n·d·k)large matrices, k ≪ d
Kernel PCAnonlinear via a kernelvariance in the kernel's feature spaceyes, through kernel evaluationsO(n²) memory, O(n³) eigensolvesmall curved datasets
t-SNEnonlinear, non-parametriclocal neighbourhoodsno (refit)O(n log n) per iteration with Barnes-Hut2-D or 3-D visualization
UMAPnonlinearlocal neighbourhoods, somewhat more global layoutyes, approximatelyapproximate k-NN graph, then fast SGDvisualization, sometimes as features
  • PCA is the default because it is deterministic, fast, invertible and has one knob. Its components are hard to name, since each mixes every feature; for interpretable parts, sparse PCA or non-negative matrix factorization trade some variance for sparser loadings.
  • Variance is not importance. The directions that separate classes can have small variance; PCA ignores labels, so a supervised method (LDA, or simply the model's own features) can beat it as preprocessing.
  • Outliers dominate variance. One extreme point can rotate PC1 toward itself; robust PCA variants or clipping help.
  • t-SNE and UMAP make persuasive pictures. Treat clusters in them as hypotheses to check in the original space, not as findings.
You whiten before k-means and the clusters get worse. Why might that happen?

Whitening scales every component to variance 1, including the low-variance ones that hold mostly noise. k-means uses Euclidean distance, so after whitening a noise direction counts as much as the direction that separated the clusters. Keeping only the top components first, or not whitening, keeps the noise from drowning the signal.

A t-SNE plot shows two tight clusters far apart and one large diffuse cluster. What can you conclude?

Only that the points within each blob are near neighbours of each other in the original space. The relative sizes are not trustworthy, because t-SNE equalizes neighbourhood sizes, and the gaps are not trustworthy, because the heavy-tailed kernel exaggerates them. Check the clusters' spread and separation in the original space or in a PCA projection, and rerun with other perplexities and seeds.

Connections

Read first

Read next

No page builds on this one yet.

The linear algebra toolkit supplies the eigen-decomposition, the SVD and power iteration this page is built from; feature preprocessing supplies the standardization PCA needs first; and PCA is the usual step before clustering or nearest-neighbour search in many dimensions. An autoencoder with linear layers and squared loss learns the same subspace as PCA, and with nonlinear layers it is a nonlinear generalization.

References

  • Pearson. On Lines and Planes of Closest Fit to Systems of Points in Space. Philosophical Magazine 1901. doi:10.1080/14786440109462720
  • Hotelling. Analysis of a Complex of Statistical Variables into Principal Components. Journal of Educational Psychology 1933. doi:10.1037/h0071325
  • Eckart and Young. The Approximation of One Matrix by Another of Lower Rank. Psychometrika 1936. doi:10.1007/BF02288367
  • Tipping and Bishop. Probabilistic Principal Component Analysis. Journal of the Royal Statistical Society B 1999.
  • Minka. Automatic Choice of Dimensionality for PCA. NIPS 2000.
  • Halko, Martinsson and Tropp. Finding Structure with Randomness: Probabilistic Algorithms for Constructing Approximate Matrix Decompositions. SIAM Review 2011. arXiv:0909.4061
  • van der Maaten and Hinton. Visualizing Data using t-SNE. Journal of Machine Learning Research 2008.
  • McInnes, Healy and Melville. UMAP: Uniform Manifold Approximation and Projection for Dimension Reduction. 2018. arXiv:1802.03426
  • Wattenberg, Viégas and Johnson. How to Use t-SNE Effectively. Distill 2016. doi:10.23915/distill.00002
  • scikit-learn 1.9.1: PCA, transform and inverse_transform, TSNE. PyTorch v2.15.0: pca_lowrank.