ML Grind

End-to-End Implementations

Assemble full models from scratch: an MLP with hand-written backprop, a Transformer encoder, a VAE, a VQ-VAE, a DDPM, a CNN, a mini GPT and a tiny ViT, plus a Transformer bug hunt.

Watch the animated End-to-End Implementations lecture (4 min) →

End-to-End Implementations

Why it matters

Single formulas are only half the job: real bugs live in the glue between layers, in caches, shapes, masks and loss reductions. "Implement a small model from scratch" is a standard ML coding interview, and these capstones are the models interviewers ask for most: an MLP with backprop, a Transformer, a CNN, a GPT and the main generative models.

Core ideas

Think in a pipeline of shapes

Write the shape after every step before writing code. For a CNN on MNIST:

StepShape
input(batch, 1, 28, 28)
conv 3×3 → pool 2(batch, 32, 26, 26) → (batch, 32, 13, 13)
conv 3×3 → pool 2(batch, 64, 11, 11) → (batch, 64, 5, 5)
flatten → fc → fc(batch, 1600) → (batch, 128) → (batch, 10)

For sequence models the residual stream keeps one shape, (batch, seq_len, d_model), and attention scores are (batch, heads, seq_len, seq_len).

The recurring building blocks

BlockFormulaWhere it appears
Softmax cross-entropyloss = mean(−log softmax(z)[y]), ∂loss/∂z = (p − onehot(y)) / NMLP, CNN, GPT, ViT
Scaled dot-product attentionsoftmax(Q Kᵀ / sqrt(d_k) + mask) VTransformer, GPT, ViT, bug hunt
LayerNormγ · (x − mean) / sqrt(var + eps) + β over the last axisall Transformers
Residual blockpost-norm LN(x + f(x)) (2017 Transformer) or pre-norm x + f(LN(x)) (GPT, ViT)all Transformers
Reparameterizationz = μ + exp(0.5·log_var) ⊙ εVAE
Vector quantizationnearest code argmin_k ‖z − e_k‖², straight-through gradientVQ-VAE
Diffusionx_t = sqrt(ᾱ_t)·x_0 + sqrt(1 − ᾱ_t)·ε; reverse step removes predicted εDDPM

Contracts matter

Each problem spells out a method contract: which dict keys to return, what to cache, which values are floats. Treat it like an API: the tests call your methods exactly as documented. Losses follow one convention across the course: MSE over all elements; the VAE KL summed over latent dims and averaged over the batch.

Worked micro-example

Softmax cross-entropy and its gradient for one example with logits z = [2.0, 1.0, 0.1] and label 0:

  • p = softmax(z) = [0.659, 0.2424, 0.0986]
  • loss = −log 0.659 = 0.417
  • ∂loss/∂z = p − onehot(0) = [−0.341, 0.2424, 0.0986] (divide by N for a batch mean)

Parameter count of the MNIST CNN above: conv1 32·1·9 + 32 = 320, conv2 64·32·9 + 64 = 18,496, fc1 1600·128 + 128 = 204,928, fc2 1,290. The dense layer holds over 90% of the weights.

Common pitfalls

  • Loss reduction: a mean loss needs the / N in its gradient; averaging the KL over latent dims shrinks it.
  • Cache discipline: backward needs the pre-activation z1 for the ReLU gate, not just h.
  • Masks: apply the causal mask as −inf before the softmax; zeroing probabilities afterwards breaks normalisation.
  • Residuals: in pre-norm blocks add to x, not to LN(x).
  • Head reshapes: transpose heads back before merging, or features from different heads get interleaved.
  • Positions: they start at 0, and generation must crop the context to block_size.
  • Stop-gradients: in a VQ-VAE the codebook and commitment terms differ only in where .detach() goes.
  • Randomness: draw noise exactly where the contract says, so seeded tests can reproduce it.

Interview angle

  • Expect "implement it, then extend it": add a mask, a KV cache, weight decay, a second layer, or a training loop.
  • Be ready with complexity: attention is O(T²·d) time and O(T²) memory per head; a conv layer is O(B·C_out·C_in·k²·H·W).
  • Verify gradients with finite differences, and know the initial-loss sanity check: about log(num_classes).
  • Explain design choices: pre-norm vs post-norm, tied vs untied LM heads, why VAEs predict log_var, why DDPM predicts noise.

Problems in this section

  1. E2E: 2-Layer MLP with Backprop: forward, cross-entropy, hand-derived gradients, SGD and a training loop.
  2. E2E: Transformer Encoder: layer norm, sinusoidal positions, multi-head attention and a post-norm encoder stack.
  3. E2E: Variational Autoencoder: encoder, reparameterization, sigmoid decoder, negative ELBO and prior sampling.
  4. E2E: Vector Quantized VAE: codebook lookup, VQ losses, MLP encoder/decoder and codebook usage.
  5. E2E: Diffusion Model: schedule, forward noising, ε-MSE loss, reverse step and the sampling loop.
  6. E2E: Convolutional Neural Network: conv2d, max pooling, flatten and a two-block classifier.
  7. Mini GPT (Character-Level Language Model): causal pre-norm decoder, next-token loss, training and greedy generation.
  8. E2E: Tiny Vision Transformer: patches, [CLS] token, position embeddings and a linear-probe training step.
  9. Transformer Bug Hunt: Fix a Broken Mini-GPT: find four silent bugs in a working-looking forward pass.

The single-concept versions live in earlier sections: Neural Networks (backprop, init), Transformer Architecture (attention, layer norm, masks), CNNs & Computer Vision (convolution, pooling) and Generative Models (VAE, VQ, diffusion).

Practice exercises

  1. E2E: 2-Layer MLP with Backprop →
  2. E2E: Transformer Encoder →
  3. E2E: Variational Autoencoder →
  4. E2E: Vector Quantized VAE →
  5. E2E: Diffusion Model →
  6. E2E: Convolutional Neural Network →
  7. Mini GPT (Character-Level Language Model) →
  8. E2E: Tiny Vision Transformer →
  9. Transformer Bug Hunt: Fix a Broken Mini-GPT →