AI Knowledge Tree

Autoencoders

Train an encoder-decoder pair to reconstruct inputs through a bottleneck, including the denoising variant.

⏱ 26 minPrerequisites:Multi-layer perceptron

The simplest generative-style neural network: an autoencoder compresses inputs through a low-dimensional bottleneck and reconstructs them. The reconstruction loss forces the bottleneck to capture whatever structure is useful for reproducing the data[1]. This lesson grounds the generative-modeling track by establishing the encoder-decoder pattern that VAEs and diffusion models will build on[2].

The architecture

An autoencoder has two halves:

  • Encoder fϕ:X→Zf_\phi: \mathcal{X} \to \mathcal{Z} maps an input to a low-dimensional latent code z∈Rkz \in \mathbb{R}^k.
  • Decoder gθ:Z→Xg_\theta: \mathcal{Z} \to \mathcal{X} maps the code back to a reconstruction x^\hat x.

Train end-to-end on the reconstruction loss:

L=∑i∥xi−gθ(fϕ(xi))∥2.\mathcal{L} = \sum_i \lVert x_i - g_\theta(f_\phi(x_i))\rVert^2.

The bottleneck dimension kk is the central design choice: small enough that the network cannot just pass the input through, large enough that the structure of the data can be captured.

Linear autoencoder = PCA

When the encoder and decoder are linear and the loss is squared error, the autoencoder reduces to PCA. The optimal encoder is the projection onto the top-kk principal components, and the optimal decoder is its transpose. This was the anomaly-detection lesson's setup.

The autoencoder framing matters when the structure is nonlinear: encoder and decoder become MLPs (or CNNs for images), and the latent space can capture curved manifolds that PCA cannot.

What the latent encodes

After training, the latent code captures what the decoder needs to reconstruct xx:

  • Discarded: any input variation the decoder cannot exploit (pure noise, irrelevant detail).
  • Kept: anything that systematically helps reconstruction (object identity, pose, color, structural features).

This makes autoencoders useful for representation learning even when generation is not the goal. The latent space often has interpretable structure: similar inputs are nearby, distinct classes occupy distinct regions.

Denoising autoencoder (DAE)

Vincent et al. (2008). Train the autoencoder to reconstruct the clean input from a corrupted version:

L=∑i∥xi−gθ(fϕ(x~i))∥2,\mathcal{L} = \sum_i \lVert x_i - g_\theta(f_\phi(\tilde x_i))\rVert^2,

where x~i\tilde x_i is xix_i with noise added (Gaussian, masked pixels, etc.).

Why corrupt the input? Two reasons:

  • Prevents identity learning: an unconstrained autoencoder with a large enough bottleneck can learn the identity function, which is useless. Denoising forces the model to learn the underlying manifold.
  • Implicit manifold learning: the model must map corrupted inputs back to the data manifold. The reconstruction error is the "energy" of corruption, and minimizing it teaches the network the shape of the manifold.

The denoising recipe seeded modern generative models:

  • Diffusion models are sequences of denoising autoencoders at different noise levels (last lessons in D4).
  • Masked autoencoders (MAE) are denoising in the masked-token form for vision (D2 lesson).
  • BERT is essentially a masked denoising autoencoder for text.

What autoencoders cannot do

Plain autoencoders are not proper generative models:

  • The latent space is unstructured. Sampling a random zz and decoding it usually gives garbage, because the encoder has only populated some regions of the latent space.
  • There is no notion of likelihood. The model can reconstruct inputs but not generate new ones in a principled way.

The next lesson fixes both with the variational autoencoder, which adds a probabilistic structure to the latent space.

Practical uses (beyond stepping-stone to VAE)

  • Dimensionality reduction: replace PCA when nonlinear structure matters (e.g., t-SNE / UMAP precomputation).
  • Anomaly detection (C6 lesson): high reconstruction error flags out-of-distribution points.
  • Feature extraction: use the encoder's output as features for a downstream classifier.
  • Image compression: trained autoencoders can outperform JPEG at low bit rates.
Reconstruction error falls steeply until the bottleneck matches the data's intrinsic dimension (~4 here), then plateaus at the noise floor: the compression/fidelity tradeoff.
Reconstruction error falls steeply until the bottleneck matches the data's intrinsic dimension (~4 here), then plateaus at the noise floor: the compression/fidelity tradeoff.

Check it on arrays

python · numpy
import numpy as np

rng = np.random.default_rng(0)

# Synthetic 2D data on a curve in 10D ambient space
n = 500
t = rng.uniform(0, 2 * np.pi, n)
X = np.column_stack([
    np.cos(t), np.sin(t),
    0.5 * np.cos(2 * t), 0.5 * np.sin(2 * t),
    rng.normal(scale=0.05, size=(n, 6))               # noise dims
])
d = X.shape[1]

# Tiny MLP autoencoder: d -> 16 -> 2 -> 16 -> d
def init(in_d, out_d): return rng.normal(scale=0.5 / np.sqrt(in_d), size=(in_d, out_d))
W1 = init(d, 16); b1 = np.zeros(16)
W2 = init(16, 2); b2 = np.zeros(2)
W3 = init(2, 16); b3 = np.zeros(16)
W4 = init(16, d); b4 = np.zeros(d)

def relu(x): return np.maximum(0, x)
def encode(X): return relu(X @ W1 + b1) @ W2 + b2          # signed bottleneck (no ReLU on top)
def decode(z): return relu(z @ W3 + b3) @ W4 + b4
def forward(X): return decode(encode(X))

# Train with SGD on the reconstruction loss
lr = 0.1
for epoch in range(500):
    h_pre = X @ W1 + b1
    h_enc = relu(h_pre)                                    # encoder hidden (post-relu)
    Z = h_enc @ W2 + b2                                    # bottleneck (signed)
    d_pre = Z @ W3 + b3
    d_hid = relu(d_pre)                                    # decoder hidden
    Xhat = d_hid @ W4 + b4
    err = Xhat - X
    # Manual backprop through the chain
    grad_W4 = d_hid.T @ err / n
    grad_b4 = err.mean(axis=0)
    grad_d_pre = (err @ W4.T) * (d_pre > 0)
    grad_W3 = Z.T @ grad_d_pre / n
    grad_b3 = grad_d_pre.mean(axis=0)
    grad_z = grad_d_pre @ W3.T                             # no ReLU on Z, so this is grad wrt Z directly
    grad_W2 = h_enc.T @ grad_z / n
    grad_b2 = grad_z.mean(axis=0)
    grad_h = grad_z @ W2.T
    grad_W1 = X.T @ (grad_h * (h_pre > 0)) / n
    grad_b1 = (grad_h * (h_pre > 0)).mean(axis=0)
    W4 -= lr * grad_W4; b4 -= lr * grad_b4
    W3 -= lr * grad_W3; b3 -= lr * grad_b3
    W2 -= lr * grad_W2; b2 -= lr * grad_b2
    W1 -= lr * grad_W1; b1 -= lr * grad_b1

# Reconstruction quality
Xhat = forward(X)
rmse = np.sqrt(np.mean((Xhat - X)**2))
print(f"autoencoder RMSE on training data: {rmse:.4f}")

# Compare to PCA at the same bottleneck dimension
mean = X.mean(axis=0); Xc = X - mean
U, s, Vt = np.linalg.svd(Xc, full_matrices=False)
V_k = Vt[:2]
X_pca = (Xc @ V_k.T) @ V_k + mean
rmse_pca = np.sqrt(np.mean((X_pca - X)**2))
print(f"PCA k=2 RMSE on the same data:     {rmse_pca:.4f}")

The nonlinear autoencoder beats linear PCA at the same bottleneck because it can model the curved structure.

Exercises

Exercise 1. A linear autoencoder has encoder f(x)=Wxf(x) = W x and decoder g(z)=Uzg(z) = U z with W∈Rk×dW \in \mathbb{R}^{k \times d} and U∈Rd×kU \in \mathbb{R}^{d \times k}, trained on centered data with squared reconstruction error. Show that if WW is fixed to the top-kk PCA projection Vk⊤V_k^\top (rows orthonormal), the decoder that minimizes reconstruction error is U=VkU = V_k, so the optimum reconstructs by projecting onto the principal subspace.

Solution

Step 1. With W=Vk⊤W = V_k^\top, the code of an input is z=Vk⊤xz = V_k^\top x, a vector of kk coordinates in the principal basis. The reconstruction is x^=Uz=UVk⊤x\hat x = U z = U V_k^\top x.

Step 2. The per-sample error is ∥x−UVk⊤x∥2\lVert x - U V_k^\top x\rVert^2. Write xx in the full orthonormal eigenbasis V=[Vk V⊥]V = [V_k\ V_{\perp}], so x=Vka+V⊥bx = V_k a + V_\perp b with a=Vk⊤xa = V_k^\top x and b=V⊥⊤xb = V_\perp^\top x. Then z=az = a and x^=Ua\hat x = U a.

Step 3. The error splits because VkaV_k a and V⊥bV_\perp b are orthogonal:

∥x−Ua∥2=∥Vka−Ua∥2+∥V⊥b∥2.\lVert x - U a\rVert^2 = \lVert V_k a - U a\rVert^2 + \lVert V_\perp b\rVert^2.

The second term does not depend on UU. The first is minimized to zero by setting Ua=VkaU a = V_k a for all codes aa, i.e. U=VkU = V_k.

Step 4. At U=VkU = V_k the reconstruction is x^=VkVk⊤x\hat x = V_k V_k^\top x, the orthogonal projection onto the top-kk principal subspace, leaving only the residual energy ∥V⊥b∥2\lVert V_\perp b\rVert^2 that PCA also leaves. The linear autoencoder optimum is the PCA reconstruction.

Exercise 2. In the denoising autoencoder, the corruption x~=x+η\tilde x = x + \eta with η∼N(0,σ2I)\eta \sim \mathcal{N}(0, \sigma^2 I) is independent of xx. For a fixed map g∘fg \circ f, show that the expected denoising loss Ex,η∥x−(g∘f)(x+η)∥2\mathbb{E}_{x,\eta}\lVert x - (g \circ f)(x + \eta)\rVert^2 is minimized, over all measurable maps, by (g∘f)(x~)=E[x∣x~](g \circ f)(\tilde x) = \mathbb{E}[x \mid \tilde x], the posterior mean of the clean input given the corrupted one.

Solution

Step 1. Let r(x~)=(g∘f)(x~)r(\tilde x) = (g \circ f)(\tilde x) be the reconstruction as a function of the corrupted input. Condition on x~\tilde x and use the tower rule:

Ex,η∥x−r(x~)∥2=Ex~[E ⁣[∥x−r(x~)∥2∣x~]].\mathbb{E}_{x,\eta}\lVert x - r(\tilde x)\rVert^2 = \mathbb{E}_{\tilde x}\left[\mathbb{E}\!\left[\lVert x - r(\tilde x)\rVert^2 \mid \tilde x\right]\right].

Step 2. For each fixed x~\tilde x, the inner expectation is a squared error of the random clean input xx against a constant vector r(x~)r(\tilde x). The constant that minimizes E[∥x−c∥2∣x~]\mathbb{E}[\lVert x - c\rVert^2 \mid \tilde x] is the conditional mean c⋆=E[x∣x~]c^\star = \mathbb{E}[x \mid \tilde x], since E[∥x−c∥2∣x~]=∥E[x∣x~]−c∥2+Var⁡(x∣x~)\mathbb{E}[\lVert x - c\rVert^2 \mid \tilde x] = \lVert \mathbb{E}[x\mid\tilde x] - c\rVert^2 + \operatorname{Var}(x \mid \tilde x) and only the first term depends on cc.

Step 3. Minimizing pointwise in x~\tilde x minimizes the outer expectation, so the optimal reconstruction is r⋆(x~)=E[x∣x~]r^\star(\tilde x) = \mathbb{E}[x \mid \tilde x]. The denoising target is the posterior mean, which is why a trained DAE maps corrupted points back toward the data manifold rather than learning the identity.

Exercise 3 (code). Build a linear autoencoder with k=2k = 2 by hand on 3D data lying near a 2D plane. Confirm that closing the loop with the PCA projection U=VkU = V_k, W=Vk⊤W = V_k^\top gives exactly the PCA reconstruction error, matching Exercise 1.

Solution
python · numpy
import numpy as np

rng = np.random.default_rng(0)

# 3D data near a 2D plane: two latent directions plus small off-plane noise
n = 400
basis = np.array([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]])     # spanning plane
coeffs = rng.normal(size=(n, 2)) * np.array([3.0, 1.0])
X = coeffs @ basis + rng.normal(scale=0.05, size=(n, 3))  # off-plane noise on dim 3
X = X - X.mean(axis=0)                                     # center

# PCA: top-2 principal directions
U_svd, s, Vt = np.linalg.svd(X, full_matrices=False)
V_k = Vt[:2].T                                            # d x k, orthonormal cols

# Linear autoencoder with W = V_k^T (encoder), U = V_k (decoder)
W = V_k.T                                                 # k x d
U = V_k                                                   # d x k
Z = X @ W.T                                               # codes
Xhat_ae = Z @ U.T                                         # reconstruction

# Direct PCA reconstruction
Xhat_pca = (X @ V_k) @ V_k.T

err_ae = np.mean(np.sum((X - Xhat_ae)**2, axis=1))
err_pca = np.mean(np.sum((X - Xhat_pca)**2, axis=1))
print(f"linear AE error: {err_ae:.6f}   PCA error: {err_pca:.6f}")

assert np.allclose(Xhat_ae, Xhat_pca)
assert np.isclose(err_ae, err_pca)

The linear autoencoder closed with the PCA projection reconstructs identically to PCA, the equality Exercise 1 derived. The residual error is the small off-plane noise PCA also discards.

The next lesson upgrades this to a proper generative model: the variational autoencoder.


References

  1. [1]Goodfellow, I., Bengio, Y., & Courville, A. (2016). Deep Learning, §14 (Autoencoders). MIT Press. The encoder-decoder bottleneck and the denoising variant.↩
  2. [2]Kingma, D. P., & Welling, M. (2014). "Auto-encoding variational bayes". ICLR. arXiv:1312.6114. The variational autoencoder built on the encoder-decoder pattern this lesson establishes.↩