LLGA · Course notes

Deep Learning

CSC_51054_EP · Year 1 · Period 1

Contents

Introduction and overview

Module Deep Learning — SynapseS sheet 2026-2027: “Programme d’approfondissement — Apprentissage automatique et apprentissage profond” (occurrence: “Machine and Deep learning”, computer science department)
Code CSC_51054_EP
Team and workload Responsible (sheet): Michalis Vazirgiannis · responsible of the 2026-2027 occurrence: Davide Buscaldi · teaching team: Inès Benito, Davide Buscaldi, Iakovos Evdaimon, Xiao Fei, Niklas Kormann, Johannes Lutzeyer, Achilleas Tsimichodimos · 36 h in person (no lecture / small-class split displayed) · 4.5 ECTS · assessment: not displayed for LLGA · prerequisites: not displayed · teaching language: English · from perceptrons to transformers, with the “graph” reading that prepares the Graph ML module.
Status in the program Graph × LLM route one of the two ways to satisfy the “Deep Learning once” rule (the other is the P2 variant) · M1 · S1 / Period 1 — X-3A-P1 (fall semester, from 17/09/26 to 18/12/26)

Audience: an engineer who has already trained an MNIST-like MLP (or a toy CNN) and wants to understand why it converges — or blows up into NaNs — before opening Cybenko. Empirical first: a stuck loss, a vanishing gradient, 99% train / 70% test accuracy; only then do we name depth, activations, backprop.

From perceptrons to transformers: networks, backpropagation, CNNs, RNNs, attention, generative models. The program’s LLMs are transformers; without this module, architecture, training and fine-tuning stay opaque. Guiding thread: a network is a computation graph; attention is reread as a GNN in Period 2.

What you will be able to do

Official objectives and syllabus (2026-2027 catalog)

Official objectives: the SynapseS sheet publishes a description and a detailed programme but no separate “objectives” block. The description places the course in the era of large data sets, where the challenge is to extract knowledge from them and build intelligent services.

It presents deep learning as the paradigm that learns rich representations directly from raw data, through convolutional networks, transformers and large pretrained models, with applications in computer vision, natural language processing, data generation, bioinformatics and learning on graphs, and it announces both the theoretical foundations and concrete large-scale applications. The official keywords are machine learning, deep learning, artificial intelligence, graph neural networks.

The detailed programme (“minor changes may occur during the course”) has nine blocks, from a general introduction to machine learning to reinforcement learning. The table below gives one row per topic of that programme. The first five blocks, the machine-learning half, are taught in the twin module Machine Learning (MDC_51006_EP, mandatory in the same period); the rows give the exact sections there.

Topic of the official programme (block) Section that covers it
Machine learning paradigms (general introduction) ML module, ch. 1 (note “Learning paradigms and the pipeline”) and ch. 7; here 1.5 (reinforcement) and 6.5 (self-supervised pretraining)
Machine learning pipelines (general introduction) ML module, ch. 4 (4.2 nested cross-validation, 4.3 data leakage, 4.6 the reference pipeline); here 8.1 (the training recipe)
Generative and non-generative methods (supervised) ML module, 3.3 (generative versus discriminative classifiers, proposition 3.8), with 3.1 (logistic regression) and 3.2 (SVM) as discriminative methods
Naive Bayes classification (supervised) ML module, 3.3 (definition 3.7, example 3.9)
Decision trees (supervised) ML module, 6.1 (CART)
Dimensionality reduction (unsupervised) ML module, 7.3 (PCA, probabilistic PCA); here 9.1 (linear autoencoder = PCA, nonlinear autoencoders)
Clustering (unsupervised) ML module, 7.1 (k-means) and 7.2 (Gaussian mixtures and EM)
Regularization (advanced concepts) ML module, 2.2 (ridge, lasso); here 3.6 (dropout, weight decay, label smoothing, augmentation, early stopping)
Model selection (advanced concepts) ML module, 1.2 (cross-validation), 4.2 (nested cross-validation) and ch. 8 (capacity)
Feature selection (advanced concepts) ML module, 4.7 (filter, wrapper, embedded; the selection-bias trap)
Ensemble methods (advanced concepts) ML module, 6.2 (bagging, random forests) and 6.3 (boosting)
Introduction to kernels (kernels) ML module, ch. 5 (5.1 Moore-Aronszajn, 5.2 representer theorem)
Support vector machines (kernels) ML module, 3.2 (primal, dual, KKT conditions) and 5.2 (kernel SVM)
Introduction to neural networks ch. 1 (definition 1.1, 1.2 universal approximation, 1.3 initialization, 1.4 activations)
Perceptrons and backpropagation of the gradient 1.1 (perceptron rule, Novikoff’s theorem, XOR) and ch. 2 (backpropagation)
Convolutional networks¹ (deep learning I) ch. 4
Recurrent networks (deep learning I) ch. 5 (5.1 RNN and backpropagation through time, 5.2 LSTM and GRU)
Applications (deep learning I) 5.3 (classification, detection and IoU, segmentation, sequence tasks) and 4.4 (historical architectures)
Modern natural language processing (deep learning II) 6.5 (BPE tokenization, causal and masked language models, perplexity), ch. 6 (transformers) and ch. 7 (fine-tuning, LoRA)
Unsupervised deep learning (deep learning II) ch. 9 (autoencoders, VAE, GAN; 9.4 diffusion, self-study) and 6.5 (self-supervised pretraining)
Embeddings² (deep learning II) 6.5 (definition 6.5: embedding matrix, word2vec, contextual embeddings)
Autoencoders (deep learning II) 9.1 (deterministic and linear autoencoders) and 9.2 (VAE)
Generative adversarial networks (deep learning II) 9.3
Reinforcement learning 1.5 (MDP, policy, Bellman equation; introduction to the paradigm); the full treatment is in the Reinforcement Learning and Autonomous Agents module (Period 2)

¹ The official sheet writes “réseaux de neurones conventionnels” (“conventional neural networks”), to be read as “convolutional” in view of the listed applications. ² The French sheet writes “intégrations”, a literal translation of “embeddings”. Referring the machine-learning half to the twin module is an organizational choice of these notes, flagged here. In return, these materials go beyond the official programme on three points: the detailed treatment of attention and transformers (the programme names only “modern natural language processing”), transfer learning and fine-tuning (ch. 7), and training in practice (ch. 8: GPU, mixed precision, reproducibility).

Official field Published value (SynapseS 2026-2027)
Fields Computer Science
Workload 36 h in person (no lecture / small-class split displayed) · enrollment min. 1, max. 156
Period / language X-3A-P1, fall semester, period 1 (17/09/26 – 18/12/26) · English
Responsible Michalis Vazirgiannis · responsible of the 2026-2027 occurrence: Davide Buscaldi · teaching team: Inès Benito, Davide Buscaldi, Iakovos Evdaimon, Xiao Fei, Niklas Kormann, Johannes Lutzeyer, Achilleas Tsimichodimos
Prerequisites Not displayed (the prerequisites tab is empty)
Assessment Grade format: numeric out of 20 (and US letter grade) · the section for the LLGA track is empty: no assessment form, resit rule, pass threshold or ECTS is displayed for LLGA, and no breakdown of assessments is given for any degree · for the other degrees: unit acquired if final grade ≥ 10; resit allowed (max of the two grades for ViCAI, TRAI, X, exchange programs; resit grade kept, resit on request from an initial grade of 7, for the other masters); ECTS 5 (X, the masters, X-HEC, exchange programs, non-degree students), 4.5 (ViCAI, TRAI), 4 (Cybersecurity track) · these notes keep 4.5 ECTS for LLGA

Where this course fits in the program

Year 1 · Period 1 course (36 h, 4.5 ECTS) preselected by the Graph × LLM route. It is one of the two ways to satisfy the “Deep Learning once” rule; the other is the P2 variant, taken in Period 2. It comes right after the Machine Learning module, of which it is the natural continuation: the perceptron is reread there as a linear model, optimization as stochastic gradient descent.

The official 2026-2027 sheet displays no prerequisites (its tab is empty); the positioning below describes the foundations actually assumed. The two refreshers — Refresher in Statistics and Refresher in Computer Science — are assumed acquired, notably NumPy matrix computation for the labs.

Downstream, this module (or its P2 variant) is the gateway to the “large models” part of the program: Introduction to Text Mining and NLP and Large Language Models (M2) build directly on the “Attention and transformers” chapter; Multimodal Generative AI extends the generative models (VAE, GAN) and fine-tuning. The attention chapter is also reread as a GNN in the Graph Machine and Deep Learning for Generative AI module: a transformer processes a set of tokens as a complete graph, and this reading unifies the two sides of the program.

The “Training in practice” chapter finally prepares the large-scale projects of Period 2 and the internships: this is where research code becomes engineering code.

Course outline

Session plan

36 h in class = 12 sessions of 3 h. The volume is the one of the SynapseS 2026–2027 sheet (36 h in person).

The sheet gives no session-by-session timetable, but its detailed programme lists the topics in order, and the split below follows that order: its machine-learning blocks are taught first, in the Machine Learning module of the same period; then come neural networks and the perceptron (sessions 2–3), convolutional and recurrent networks with their applications (sessions 7–8), modern NLP (session 9), unsupervised deep learning (sessions 11–12), and reinforcement learning last (session 12), as on the sheet.

Optimization (ch. 3) and transfer learning and training practice (ch. 7–8) are additions of these notes, placed where the architectures need them.

Level 1 = foundations (reviews, definitions, first worked examples), 2 = core methods (backpropagation, optimization, the architectures), 3 = advanced material (generative models and their full derivations); the level never goes down from one session to the next.

Session Hours Level Content Practice
1 3 h 1 ch. 0 Review: matrix calculus, mini-batch noise, stable softmax lab: log-sum-exp and NaNs in PyTorch (sheet in 0.3)
2 3 h 1 ch. 1.1–1.2 Perceptron rule, Novikoff’s theorem and XOR; neuron and MLP, universal approximation Ex. 2
3 3 h 2 ch. 1.3–1.4 Xavier and He initialization, activations; ch. 2 Backpropagation, with a NumPy gradient check Ex. 1, 8
4 3 h 2 ch. 3.1–3.2 SGD on a quadratic, momentum Ex. 5
5 3 h 2 ch. 3.3–3.4 Adam, step scheduling and warmup Ex. 9
6 3 h 2 ch. 3.5–3.6 BatchNorm, LayerNorm, regularization lab: dropout and weight decay on an MLP (sheet in 3.6)
7 3 h 2 ch. 4 Convolutional networks, receptive field, residual blocks Ex. 6
8 3 h 2 ch. 5 RNN, backpropagation through time, LSTM; 5.3 applications: classification, detection and IoU, segmentation, sequence tasks lab: gradient norm through time (sheet in 5.2)
9 3 h 2 ch. 6 Attention and transformers: positional encodings, cross-attention, GPU memory and FlashAttention; 6.5 modern NLP: BPE tokenization, embeddings (word2vec stated, derived later in NLP), causal and masked language models, perplexity; 6.6 vision transformers (ViT) Ex. 3, 4
10 3 h 2 ch. 7 Transfer learning (linear probe, partial and full fine-tuning) and LoRA; ch. 8 Training in practice (mixed precision, tracking) Ex. 7; Ex. 12 (start the project)
11 3 h 3 ch. 9.1–9.2 Autoencoders (the linear autoencoder is PCA), VAE and the ELBO Ex. 10
12 3 h 3 ch. 9.3 GAN: minimax game and equilibrium; then ch. 1.5 the reinforcement learning framework (MDP, policy, Bellman equation), last as in the official programme Ex. 11; Ex. 12 (project)

Self-study, outside the 36 h (about 5 h in total): ch. 9.4 Diffusion in one section, with a box on normalizing flows (≈ 2 h) · ch. 10 Research at DaSciM — Graph of Words, GNN expressivity, steering diffusion models (≈ 3 h).

How to study this module

Notation Deviations from the site conventions (notation page). L is the depth of a network in ch. 1–2 (and in checkpointing), while in ch. 3 it is the smoothness constant of the gradient. Section 6.1 writes one token in column form, q_i = W_q x_i, next to the row form Q = XW_Q of the batch.

Everything else follows the site conventions:

  • data D_n = \{(x_i, y_i)\}_{i=1}^n drawn i.i.d. from a distribution P, mini-batches of size b, learning rate \eta;
  • a loss \ell(\hat y, y) with the prediction first, and a calligraphic \mathcal{L} that is always minimized;
  • a linear layer h = Wx + b whose matrix W has size d_{\mathrm{out}} \times d_{\mathrm{in}}, as nn.Linear.weight;
  • the VAE objective \mathrm{ELBO}(\theta, \phi; x), maximized (ch. 9); value functions V^\pi, Q^\pi with their superscript (1.5); vocabulary \mathcal{V}, while V is the value matrix (ch. 6).

independent note LLMs × graphs: this module is the hub — the transformer already reads as a network on a graph. Large-model engineering or a PhD: hands-on training mastery (shapes, LR, NaNs) matters as much as the architecture on the slide.

0. Review: matrix differential calculus and numerical stability

Deep learning is, 90% of it, differential calculus applied to functions with billions of parameters. This chapter consolidates the L3 tools we will need every week: differentiation of matrix functions, the central limit theorem (to understand why SGD works) and a bit of numerical stability (to understand why naive implementations produce NaNs).

L3 review — the basic mechanics

  • Chain rule: (g \circ f)'(x) = g'(f(x))\, f'(x); in dimension: \nabla (g \circ f)(x) = J_f(x)^\top \nabla g(f(x)), where J_f is the Jacobian matrix (J_f)_{ij} = \partial f_i / \partial x_j.
  • Gradient of quadratic forms: \nabla_x \tfrac12\|Ax - b\|^2 = A^\top(Ax - b) and \nabla_x x^\top A x = (A + A^\top)x.
  • Derivatives of the usual activations: \sigma' = \sigma(1-\sigma), \tanh' = 1 - \tanh^2, \mathrm{ReLU}' = \mathbf{1}_{x>0}, and for the softmax \hat y = \mathrm{softmax}(z): \partial \hat y_k / \partial z_j = \hat y_k(\delta_{kj} - \hat y_j).
  • CLT: the mean of m iid variables with variance \sigma^2 fluctuates around its expectation with standard deviation \sigma/\sqrt m (cf. Refresher Statistics module).

0.1. Differentiation of matrix functions

A network’s parameters are matrices; one must know how to differentiate with respect to a matrix. The most convenient convention: the gradient of a function f : \mathbb{R}^{m \times n} \to \mathbb{R} is the matrix G such that f(X + H) = f(X) + \langle G, H\rangle + o(\|H\|) with the Frobenius inner product \langle G, H \rangle = \mathrm{tr}(G^\top H). Three identities suffice for this whole course.

Proposition 0.1 — Useful differential identities For X \in \mathbb{R}^{m \times n}, a \in \mathbb{R}^m, b \in \mathbb{R}^n, A of compatible size: \frac{\partial}{\partial X}\,\mathrm{tr}(A^\top X) = A, \qquad \frac{\partial}{\partial X}\, a^\top X b = a\, b^\top, \qquad \frac{\partial}{\partial X}\, \tfrac12\|X A - B\|_F^2 = (XA - B)A^\top.

Proof. We prove the second (the others are identical). Write a^\top X b = \sum_{i,j} a_i X_{ij} b_j = \mathrm{tr}(b a^\top X). The differential is d(a^\top X b) = a^\top H b = \langle a b^\top, H\rangle for any perturbation H, so the gradient (in the Frobenius sense) is ab^\top.

For the third: f(X) = \tfrac12\|XA - B\|_F^2, df = \langle XA - B,\; HA\rangle = \langle (XA-B)A^\top,\; H\rangle, using the cyclicity of the trace \mathrm{tr}(M^\top H A) = \mathrm{tr}((M A^\top)^\top H) with M = XA - B. ∎

Practical rule In a network, “the gradient climbs back through the transposes”: if the forward pass contains z = Wx, the backward pass contains \nabla_x = W^\top \nabla_z and \nabla_W = \nabla_z\, x^\top.

Always check the dimensions: \nabla_W has the size of W, \nabla_x that of x. This is the most effective quality control against backprop bugs.

0.2. CLT and mini-batch noise

Why optimize on mini-batches rather than on the whole dataset? Let D_n = (\xi_1, \dots, \xi_n) be n iid samples from a distribution P, and the empirical risk \hat R_n(\theta) = \frac1n \sum_{i=1}^n \ell(\theta; \xi_i). For a mini-batch B = (\xi_{i_1}, \dots, \xi_{i_b}) of b indices drawn uniformly and independently, the mini-batch gradient is \hat g(\theta) = \frac1b \sum_{j=1}^b \nabla_\theta \ell(\theta; \xi_{i_j}).

Proposition 0.2 — The stochastic gradient is an unbiased estimator with 1/\sqrt b noise \mathbb{E}[\hat g] = \nabla \hat R_n and, if the individual gradients have finite covariance \Sigma = \mathrm{Cov}(\nabla \ell(\theta; \xi)), then \mathrm{Cov}(\hat g) = \frac{1}{b}\,\Sigma.

Proof. Write G_j = \nabla \ell(\theta; \xi_{i_j}). By uniform sampling, \mathbb{E}[G_j] = \nabla \hat R_n, and linearity of expectation gives the mean of \hat g. For the covariance, the draws are independent: \mathrm{Cov}(\tfrac1b\sum_j G_j) = \tfrac{1}{b^2}\sum_j \mathrm{Cov}(G_j) = \tfrac1b \Sigma.

The standard deviation of the estimation therefore decreases as \|\Sigma\|^{1/2}/\sqrt b: multiplying the batch size by 100 divides the noise by only 10 — an economic argument for small batches, completed in chapter 3 (the noise also has a beneficial exploration effect). ∎

0.3. Numerical stability: softmax and the log-sum-exp trick

The softmax \hat y_i = e^{z_i} / \sum_j e^{z_j} appears in all classification losses and in attention. In floating-point precision (float32, max \approx 3.4 \times 10^{38}), the naive implementation overflows: e^{1000} = \infty, and \infty/\infty = \mathrm{NaN}. The universal trick is to subtract the maximum before exponentiating.

Proposition 0.3 — log-sum-exp identity and stable softmax Set \mathrm{LSE}(z) = \log \sum_{j=1}^n e^{z_j} and m = \max_j z_j. Then: \mathrm{LSE}(z) = m + \log \sum_{j=1}^n e^{z_j - m}, \qquad m \;\le\; \mathrm{LSE}(z) \;\le\; m + \log n, \mathrm{softmax}(z)_i = \frac{e^{z_i - m}}{\sum_j e^{z_j - m}}, and this last form never overflows.

Proof. Factorization: \sum_j e^{z_j} = e^{m} \sum_j e^{z_j - m}, hence \mathrm{LSE}(z) = m + \log \sum_j e^{z_j - m}. Since 0 < e^{z_j - m} \le e^0 = 1 and at least one term equals exactly 1 (that of the maximum), the sum lives in [1, n], giving the bracket.

For the softmax, dividing numerator and denominator by e^m > 0 does not change the value.

Stability: all the exponents z_i - m \le 0, so each e^{z_i - m} \in (0, 1]: no overflow, and the dominant term of the denominator equals exactly 1, so the result stays accurate even when the other terms underflow toward 0 (which is harmless: the softmax simply tends toward a one-hot). ∎

import torch

z = torch.tensor([1000., 1001., 999.])
# print(torch.softmax(z, 0))          # NaN with a naive implementation
print(torch.logsumexp(z, 0))          # 1001.4076: stable (PyTorch always applies it)
print(torch.softmax(z - z.max(), 0))  # stable form: [0.2447, 0.6652, 0.0900]

Numerical culture This section also prepares chapter 8: most NaNs during training come from an unprotected softmax/exponent, from the log of a zero probability (one minimizes -\log p via log-softmax, never -\log(\mathrm{softmax})), or from a too-large learning rate. The rule: when a NaN appears, walk back up the forward pass layer by layer until you find the first infinite activation.

Lab sheet — session 1: log-sum-exp and NaNs in PyTorch Goal: make a NaN appear on purpose, find where it comes from, and remove it.

  1. Code a naive softmax and a naive cross-entropy in float32. Feed them logits scaled by 10, 100 and 1000, and note the first scale that gives inf or nan.
  2. Code the stable versions of proposition 0.3 and compare them with torch.logsumexp and F.cross_entropy (agreement to 10^{-6}).
  3. Redo step 2 in float16. Find the scale at which a probability underflows to 0 and its log becomes -inf, then fix it with F.log_softmax.
  4. Train a small MLP with a learning rate 100 times too large. Register a forward hook that stops at the first non-finite activation, and name the layer.

Deliverable: the notebook and five lines saying where each NaN came from.

1. From the neuron to the deep network

Concrete scene: 2-layer MLP on MNIST-like data, ReLU, SGD. After 5 epochs, train loss ~0.08 and test ~0.35; you add an 8th layer and the loss freezes (or becomes NaN). This is not “deep learning is magic”: it is mis-calibrated initialization, activation and depth. This chapter names these objects — neuron, MLP, non-linearity — before the universal approximation theorem.

What breaks Stacking linear layers with no activation (the net collapses to one affine map); skipping initialization (vanishing); judging a run on train alone. All three sabotage an LLM fine-tune as badly as a toy MLP.

Definition 1.1 — Artificial neuron and MLP A neuron computes h = \sigma(\langle w, x\rangle + b) with \sigma a nonlinear activation function. A multilayer perceptron of depth L stacks: h^{(0)} = x, \qquad h^{(\ell)} = \sigma\big(W^{(\ell)} h^{(\ell-1)} + b^{(\ell)}\big), \qquad f_\theta(x) = W^{(L)} h^{(L-1)} + b^{(L)}. Without \sigma, the network would reduce to a single affine map (exercise 2): nonlinearity is the price of expressivity. Number of parameters: |\theta| = \sum_\ell (n_{\ell} n_{\ell-1} + n_\ell).

1.1. The perceptron: the first learning rule

The official syllabus opens its neural-network block with “perceptrons and backpropagation”. Rosenblatt’s perceptron (1958) is a single neuron with a sign activation, \hat y = \mathrm{sign}(\langle w, x\rangle), for labels y \in \{-1, +1\}; a bias is absorbed by appending a constant coordinate 1 to x. Its interest today is twofold: it is the simplest learning rule with a convergence proof, and its failure on non-separable data explains why hidden layers, and therefore backpropagation, are needed.

Definition 1.2 — Perceptron algorithm Start from w_0 = 0. Visit the examples in turn; whenever example i is misclassified, that is y_i\langle w, x_i\rangle \leq 0, update w \leftarrow w + y_i\, x_i, and leave w unchanged otherwise. Stop after a full pass without mistakes.

The update is a stochastic gradient step with step size 1 on the loss \max(0, -y\langle w, x\rangle), whose gradient is -y\,x on a mistake and 0 otherwise.

Theorem 1.3 — Convergence of the perceptron (Novikoff, 1962) Assume \|x_i\| \leq R for all i, and that the data are linearly separable with margin \gamma > 0: some unit vector u satisfies y_i\langle u, x_i\rangle \geq \gamma for all i. Then the perceptron makes at most (R/\gamma)^2 mistakes, whatever the order of the examples.

Proof. Let w_k be the weight vector after k mistakes. Each mistake on (x_i, y_i) increases the alignment with u: \langle u, w_k\rangle = \langle u, w_{k-1}\rangle + y_i\langle u, x_i\rangle \geq \langle u, w_{k-1}\rangle + \gamma, so \langle u, w_k\rangle \geq k\gamma.

The norm grows slowly, because a mistake means y_i\langle w_{k-1}, x_i\rangle \leq 0: \|w_k\|^2 = \|w_{k-1}\|^2 + 2y_i\langle w_{k-1}, x_i\rangle + \|x_i\|^2 \leq \|w_{k-1}\|^2 + R^2, so \|w_k\|^2 \leq kR^2.

By Cauchy–Schwarz, k\gamma \leq \langle u, w_k\rangle \leq \|w_k\| \leq \sqrt{k}\,R, hence k \leq (R/\gamma)^2.∎

Example 1.4 — XOR: where one neuron stops and two layers succeed Label the four points of \{0,1\}^2 by exclusive or: (1,0) and (0,1) are positive, (0,0) and (1,1) negative. No line separates them.

If w_1x_1 + w_2x_2 + b were positive exactly on the positive points, we would have b < 0, \qquad w_1 + w_2 + b < 0, \qquad w_1 + b > 0, \qquad w_2 + b > 0; adding the last two gives w_1 + w_2 + 2b > 0, so w_1 + w_2 + b > -b > 0, a contradiction. The perceptron therefore never stops on these data.

One hidden layer with two threshold units solves the problem:

  • h_1 = \mathbf{1}[x_1 + x_2 - 0.5 > 0] computes OR;
  • h_2 = \mathbf{1}[x_1 + x_2 - 1.5 > 0] computes AND;
  • the output \mathbf{1}[h_1 - h_2 - 0.5 > 0] is XOR.

The perceptron rule cannot train h_1 and h_2, because no target is given for hidden units. Replacing thresholds by differentiable activations and propagating the error backwards through the layers is exactly what chapter 2 does.

1.2. The universal approximation theorem

Theorem 1.5 — Universal approximation (Cybenko, 1989; Hornik, 1991) Let \sigma be continuous and discriminatory: for a finite signed measure \mu on the compact K below, \int_K \sigma(w^\top x + b)\, d\mu(x) = 0 \ \text{ for all } w, b \quad\Longrightarrow\quad \mu = 0. Every continuous sigmoidal function (limits 0 at -\infty and 1 at +\infty) is discriminatory, so the sigmoid qualifies, and so does \tanh, an affine image of it. Then for every f \in \mathcal{C}(K) on a compact K \subset \mathbb{R}^d and every \varepsilon > 0, there exists g(x) = \sum_{j=1}^{J} \alpha_j\, \sigma(w_j^\top x + b_j) (a single hidden layer) such that \sup_{x \in K} |f(x) - g(x)| \le \varepsilon.

Proof. Guided sketch, in two parts — part (ii) is the technical core of Cybenko, part (i) gives its constructive intuition.

(i) Construction by sigmoidal “gates” (one-dimensional case). Let f be continuous on [0,1]. A steep sigmoid \sigma(k(x-a)) tends toward the indicator \mathbf{1}_{x > a} as k \to \infty; by taking differences, \pi_{k}(x) = \sigma(k(x - a)) - \sigma(k(x - b)), \qquad a < b, tends toward \mathbf{1}_{[a,b]}(x) (a gate).

Since f is uniformly continuous, the step function s_n = \sum_{i} f(t_i)\,\mathbf{1}_{[t_i, t_{i+1})} on a subdivision of step 1/n satisfies \|f - s_n\|_\infty \le \omega_f(1/n) \to 0.

Replacing each indicator by a steep gate \pi_k with k large enough (the error is concentrated on two intervals of width \sim 1/k around a and b, where it is bounded), one obtains \|f - g\|_\infty \le \varepsilon with g = \sum_i f(t_i)\,\pi_{k,i} — a finite linear combination of sigmoids.

(ii) Density by Hahn–Banach (the general argument). Let \mathcal{G} be the vector space spanned by the x \mapsto \sigma(w^\top x + b). Suppose the closure of \mathcal{G} for \|\cdot\|_\infty is not \mathcal{C}(K).

By the Hahn–Banach theorem (separation of a missing dense subspace), there exists a nonzero finite signed measure \mu on K such that \int g \,d\mu = 0 for every g \in \mathcal{G}, hence in particular \int_K \sigma(w^\top x + b)\, d\mu(x) = 0 \qquad \forall\, w \in \mathbb{R}^d,\ b \in \mathbb{R}.

Cybenko’s technical lemma (every continuous sigmoidal \sigma is discriminatory) then gives \mu = 0 — a contradiction. The idea of the lemma: if \hat\mu(t) = \int e^{t^\top x} d\mu(x) denotes the Fourier–Laplace transform of \mu, the vanishing above for a “rich enough” sigmoid forces \hat\mu \equiv 0 on an open set, hence \mu = 0 by injectivity of the Fourier transform on finite measures.

∎

ReLU and GELU too: Leshno et al. (1993) Leshno, Lin, Pinkus and Schocken proved the general form: for a continuous activation (more generally, locally bounded and piecewise continuous), one-hidden-layer networks are dense in \mathcal{C}(K) if and only if \sigma is not a polynomial. ReLU, GELU and every activation of section 1.4 are covered; a polynomial \sigma of degree k only spans polynomials of degree at most k.

What the theorem does not say It guarantees the existence of good weights, not that one can find them by optimization: the proof constructs sigmoids of slope k \to \infty, a regime gradient descent does not explore; and the required number of neurons J can be exponential in d. Depth partially corrects this last point.

Why depth? Some functions require an exponential number of neurons on one layer but a polynomial number on several (e.g. parity, products). Depth factorizes representations: edges → textures → objects (vision), characters → words → syntax (NLP), neighbors → communities → graphs (Graph ML).

1.3. Initialization: derivation of Xavier and He

A bad initialization produces activations that explode or vanish from the forward pass onward. Analyze a linear layer a = Wh with h \in \mathbb{R}^{n_{\mathrm{in}}}, \qquad W \in \mathbb{R}^{n_{\mathrm{out}} \times n_{\mathrm{in}}}, iid centered weights of variance v_W, iid centered inputs of variance v_h, independent of the weights.

Proposition 1.6 — Forward and backward variance \mathrm{Var}(a_i) = n_{\mathrm{in}}\, v_W\, v_h \quad\text{(forward pass)}, \qquad \mathrm{Var}\big[(\nabla_h)_j\big] = n_{\mathrm{out}}\, v_W\, v_\delta \quad\text{(backward pass, } \nabla_h = W^\top \delta\text{)}. To preserve the variance in both directions, one needs v_W = 1/n_{\mathrm{in}} and v_W = 1/n_{\mathrm{out}} simultaneously; the harmonic compromise of Glorot & Bengio (2010) is v_W = 2/(n_{\mathrm{in}} + n_{\mathrm{out}}).

Proof. Forward pass: a_i = \sum_{j=1}^{n_{\mathrm{in}}} W_{ij} h_j. The terms are independent and centered, \mathbb{E}[W_{ij}h_j] = \mathbb{E}[W_{ij}]\mathbb{E}[h_j] = 0, so the variance of the sum is the sum of the variances: \mathrm{Var}(a_i) = \sum_j \mathrm{Var}(W_{ij})\,\mathrm{Var}(h_j) = n_{\mathrm{in}} v_W v_h. Backward pass: symmetric computation on the columns, (\nabla_h)_j = \sum_{i=1}^{n_{\mathrm{out}}} W_{ij} \delta_i. Wanting n_{\mathrm{in}} v_W \approx 1 and n_{\mathrm{out}} v_W \approx 1, the harmonic mean 2/(n_{\mathrm{in}}+n_{\mathrm{out}}) approximates both constraints. ∎

He (2015) for ReLU ReLU zeroes half of its inputs: if h = \mathrm{ReLU}(a) with a centered, then \mathbb{E}[h^2] = \tfrac12 \mathrm{Var}(a). To compensate this \tfrac12 factor, He et al. (2015) propose v_W = 2/n_{\mathrm{in}} (or v_W = 2/n_{\mathrm{out}} if one preserves the backward variance instead). In practice: nn.init.kaiming_normal_ for ReLU/GELU, xavier_normal_ for tanh. Finally, zero initialization breaks the symmetry forever: all the neurons of a layer would receive the same gradient and remain identical.

1.4. Activation functions, compared

Activation Definition / derivative Advantages Pathologies
Sigmoid \sigma' = \sigma(1-\sigma) \le \tfrac14 probabilities, LSTM gates saturation, max gradient 1/4, not centered
tanh 1 - \tanh^2 centered (Xavier) saturation at the extremes
ReLU \max(0,x), derivative \mathbf{1}_{x>0} constant derivative 1, sparse, fast dying ReLU: neuron stuck at 0 if pre-activation always negative
Leaky ReLU \max(\alpha x, x) nonzero gradient for x<0 hyperparameter \alpha
GELU x\,\Phi(x) = \tfrac{x}{2}\big(1 + \mathrm{erf}(x/\sqrt2)\big) smooth, self-gated, transformers standard cost of erf (usual tanh approximation)

The usual approximation of GELU, used in BERT/GPT, is 0.5x\big(1 + \tanh\big(\sqrt{2/\pi}\,(x + 0.044715\,x^3)\big)\big). Note the kinship with ReLU — GELU weights x by the probability \Phi(x) that x is “activated” according to a standard Gaussian: it is a soft gate, differentiable at 0.

1.5. Beyond supervised: reinforcement learning

Everything in this chapter assumes labeled pairs (x, y). The official syllabus of the course lists a third paradigm, reinforcement learning: an agent interacts with an environment through actions and observes no label, but a scalar reward. This section sets up the framework; the full treatment (algorithms, theory, autonomous agents) is in the Reinforcement Learning and Autonomous Agents module (Period 2).

Definition 1.7 — Markov decision process (MDP) and policy An MDP is a tuple (\mathcal{S}, \mathcal{A}, P, r, \gamma): states, actions, transition kernel P(s' \mid s, a), reward r(s, a), discount factor \gamma \in [0,1). A policy \pi(a \mid s) chooses the action as a function of the state; the objective of the agent is to maximize the expectation of the discounted return J(\pi) = \mathbb{E}_\pi\Big[\sum_{t \geq 0} \gamma^t\, r(S_t, A_t)\Big]. The value function V^\pi(s) = \mathbb{E}_\pi[\sum_t \gamma^t r_t \mid S_0 = s] satisfies the Bellman equation V^\pi(s) = \mathbb{E}_{a \sim \pi}\big[r(s,a) + \gamma\,\mathbb{E}_{s'} V^\pi(s')\big]. This is the recursion that underlies all the algorithms: evaluate V^\pi (prediction), then improve \pi (control).

Why this paradigm concludes the chapter

  • Reinforcement meets depth when V^\pi, \pi or the action-value function Q^\pi(s,a) are approximated by a network (this chapter): this is deep RL — DQN for the optimal Q^*, parametrized policies \pi_\theta optimized by policy gradient. The training machinery (ch. 2 and 3) applies as is, with in addition non-stationarity: the data depend on the current policy.
  • Sequence link: an agent produces a trajectory — the exposure bias of RNNs (ch. 5) is a degenerate special case of it, where the “reward” is the true past output.
  • Program side: multi-agent RL meets the collaborative learning of the Emerging Subjects in ML module; RL is also the framework for LLM alignment (RLHF) covered in M2.

2. Backpropagation

Training a network by gradient descent needs, at every step, the gradient of the loss with respect to every weight — millions of partial derivatives. Backpropagation computes all of them for the price of about two forward passes, by applying the chain rule backward on the computation graph; this chapter proves it, runs it by hand on a small example, and counts its memory cost.

2.1. The theorem and its proof

Theorem 2.1 — Backpropagation (chain rule on a computation graph) Consider an acyclic computation graph whose nodes v carry tensors, each node being a differentiable function of its predecessors, and \mathcal{L} the loss node. For every node v other than \mathcal{L}, with successors s_1, \dots, s_k: \frac{\partial \mathcal{L}}{\partial v} = \sum_{i=1}^{k} \frac{\partial \mathcal{L}}{\partial s_i}\, \frac{\partial s_i}{\partial v}, with base case \partial \mathcal{L}/\partial \mathcal{L} = 1. In particular, for an MLP where a^{(\ell)} = W^{(\ell)} h^{(\ell-1)} + b^{(\ell)} and \delta^{(\ell)} = \partial \mathcal{L}/\partial a^{(\ell)}:

Key result — the backpropagation recursion \delta^{(L)} = \nabla_{a^{(L)}} \mathcal{L}, \qquad \delta^{(\ell)} = \big(W^{(\ell+1)\top} \delta^{(\ell+1)}\big) \odot \sigma'(a^{(\ell)}), \frac{\partial \mathcal{L}}{\partial W^{(\ell)}} = \delta^{(\ell)} \, h^{(\ell-1)\top}, \qquad \frac{\partial \mathcal{L}}{\partial b^{(\ell)}} = \delta^{(\ell)}.

Proof. By descending induction along a reverse topological order (from outputs toward inputs).

Base. \partial \mathcal{L}/\partial \mathcal{L} = 1.

Heredity. Fix v and assume the formula true for all its successors s_i. Since the graph is acyclic, \mathcal{L} depends on v only through the values of s_1, \dots, s_k; writing \mathcal{L} = \Psi(s_1(v), \dots, s_k(v)) locally, the multivariate chain rule gives exactly \frac{d\mathcal{L}}{dv} = \sum_i \frac{\partial \mathcal{L}}{\partial s_i}\frac{ds_i}{dv}.

The MLP matrix formulas are merely the coordinate-by-coordinate application of this identity; the product W^{(\ell+1)\top}\delta^{(\ell+1)} transposes the linearity a^{(\ell+1)} = W^{(\ell+1)}h^{(\ell)} + \dots, and the term \odot\,\sigma'(a^{(\ell)}) comes from h^{(\ell)} = \sigma(a^{(\ell)}) applied component by component. ∎

Example 2.2 — Backprop by hand on a 5-node graph Take f(w_1, w_2) = \tfrac12\big(w_2 \tanh(w_1 x) - t\big)^2 with x = 1.5, w_1 = 0.3, w_2 = -0.8, t = 1.

Intermediate nodes: a = w_1 x, h = \tanh(a), y = w_2 h, \mathcal{L} = \tfrac12(y-t)^2.

Forward pass value Local derivatives
a = w_1 x 0.45 \partial a/\partial w_1 = x = 1.5, \partial a/\partial x = w_1
h = \tanh(a) 0.4219 \partial h/\partial a = 1-h^2 = 0.8220
y = w_2 h -0.3375 \partial y/\partial w_2 = h, \partial y/\partial h = w_2
\mathcal{L} = \tfrac12(y-t)^2 0.8944 \partial \mathcal{L}/\partial y = y - t = -1.3375

Backward pass (products of local derivatives): \frac{\partial \mathcal{L}}{\partial w_2} = -1.3375 \times 0.4219 = -0.5643, \qquad \frac{\partial \mathcal{L}}{\partial w_1} = \underbrace{(-1.3375)(-0.8)}_{\partial\mathcal{L}/\partial h = 1.0700} \times\; 0.8220 \times 1.5 = 1.3193. The message \partial\mathcal{L}/\partial h = 1.07 is literally the gradient “passing backward” through the multiplication — same structure as the message passing of GNNs, but on the computation graph.

The same recursion, written for a batch, fits in a few lines of NumPy. The code below trains nothing: it computes the forward pass and the backward pass of a two-layer ReLU network with a softmax cross-entropy, then checks every partial derivative against a central finite difference in float64. Do this check once for every hand-written backward pass.

import numpy as np

rng = np.random.default_rng(0)
n, d_in, d_h, K = 16, 4, 32, 3                        # batch, input, hidden, classes
X, y = rng.normal(size=(n, d_in)), rng.integers(0, K, n)
p = {"W1": rng.normal(0, np.sqrt(2 / d_in), (d_h, d_in)), "b1": np.zeros(d_h),   # He init (1.3)
     "W2": rng.normal(0, np.sqrt(2 / d_h), (K, d_h)), "b2": np.zeros(K)}

def forward(p):
    A1 = X @ p["W1"].T + p["b1"]                      # a = W x + b, one example per row
    H1 = np.maximum(A1, 0)                            # ReLU
    Z = H1 @ p["W2"].T + p["b2"]                      # logits
    Z = Z - Z.max(1, keepdims=True)                   # stable softmax (prop. 0.3)
    P = np.exp(Z) / np.exp(Z).sum(1, keepdims=True)
    return -np.log(P[np.arange(n), y]).mean(), (A1, H1, P)

def backward(p, cache):
    A1, H1, P = cache
    D2 = P.copy(); D2[np.arange(n), y] -= 1; D2 /= n  # delta at the logits: softmax - one-hot
    D1 = (D2 @ p["W2"]) * (A1 > 0)                    # (W2^T delta2) * sigma'(a1), theorem 2.1
    return {"W1": D1.T @ X, "b1": D1.sum(0), "W2": D2.T @ H1, "b2": D2.sum(0)}

loss, cache = forward(p)
g = backward(p, cache)

eps = 1e-6                                            # central differences, in float64
for name, W in p.items():
    num = np.zeros_like(W)
    for idx in np.ndindex(W.shape):
        old = W[idx]
        W[idx] = old + eps; lp = forward(p)[0]
        W[idx] = old - eps; lm = forward(p)[0]
        W[idx] = old
        num[idx] = (lp - lm) / (2 * eps)
    rel = np.linalg.norm(num - g[name]) / (np.linalg.norm(num) + np.linalg.norm(g[name]))
    print(name, f"relative error {rel:.0e}")         # below 1e-9: the backward pass is right

The error is measured on the whole tensor, because an entry-by-entry relative error blows up on entries whose gradient is nearly zero.

2.2. Cost: memory, compute, and how to reduce them

import torch

torch.manual_seed(0)
x, y = torch.randn(8, dtype=torch.float64), torch.randn(2, dtype=torch.float64)
W1 = torch.randn(64, 8, dtype=torch.float64, requires_grad=True)   # autograd tracks the operations
W2 = torch.randn(2, 64, dtype=torch.float64, requires_grad=True)

def f(W1, W2): return 0.5 * ((y - W2 @ torch.relu(W1 @ x))**2).sum()
loss = f(W1, W2)
loss.backward()          # automatic backprop
print(W1.grad.shape, W2.grad.shape)           # same shapes as W1, W2

# Check one entry by central finite difference, in float64 (do it for every new custom gradient)
j = int(W2.grad[0].abs().argmax())            # an entry with a nonzero gradient
eps = 1e-6
with torch.no_grad():
    W2[0, j] += eps;     lp = f(W1, W2)
    W2[0, j] -= 2 * eps; lm = f(W1, W2)
    W2[0, j] += eps
print((lp - lm).item() / (2 * eps), W2.grad[0, j].item())   # the two agree to about 9 digits

In float32, a loss of order 10 to 100 carries a rounding error of about 10^{-6} to 10^{-5}, as large as the change that a perturbation of 10^{-6} produces: the two numbers then disagree. Use float64, or accept a rough check with \epsilon \approx 10^{-3} in float32.

Computation graph autograd dynamically builds the graph of operations (vertices = tensors, edges = operations) and applies the chain rule in reverse-mode differentiation. Deep learning is computation on a graph — the same idea will be applied to the data themselves in the Graph ML module. And since the graph is acyclic by construction, the induction of theorem 2.1 always runs without deadlock; RNNs (chapter 5) are “unrolled” in time precisely to recover an acyclic graph.

3. Optimization in practice

Training minimizes the risk R(\theta) = \mathbb{E}_{(x,y)\sim P}\big[\ell(f_\theta(x), y)\big] through its empirical version \hat R_n, by stochastic gradient descent: at each step, the gradient is estimated on a mini-batch (proposition 0.2). This chapter analyzes why each ingredient of the modern recipe (momentum, Adam, warmup, normalization, regularization) is there.

3.1. Convergence of SGD: the quadratic case, fully proved

The generic model of non-convexity is hard; the quadratic case, for its part, is solved exactly and illuminates all the others. Let f(\theta) = \tfrac12 \theta^\top A \theta - b^\top \theta with A symmetric positive definite, eigenvalues \lambda_1 \ge \dots \ge \lambda_d > 0, \lambda_1 = L (Lipschitz constant of the gradient), \lambda_d = \mu, condition number \kappa = L/\mu.

The minimizer \theta^\star = A^{-1}b satisfies \nabla f(\theta^\star) = 0, and the (for now deterministic) descent reads \theta_{k+1} = \theta_k - \eta (A\theta_k - b).

Theorem 3.1 — Guaranteed decrease if and only if \eta < 2/L Setting e_k = \theta_k - \theta^\star: e_{k+1} = (I - \eta A)\, e_k, \qquad \|e_k\| \le \rho^k \|e_0\|, \quad \rho = \max_i |1 - \eta\lambda_i|. Convergence takes place for every e_0 if and only if 0 < \eta < 2/L; the optimal rate is reached for \eta = 2/(L+\mu) and equals \rho = \frac{\kappa - 1}{\kappa + 1} \approx 1 - \frac{2}{\kappa}.

Proof. Since \theta^\star is a fixed point of the iteration, e_{k+1} = \theta_{k+1} - \theta^\star = (I - \eta A)(\theta_k - \theta^\star) = (I-\eta A)e_k, hence e_k = (I - \eta A)^k e_0. Diagonalize A = Q \Lambda Q^\top (orthonormal): I - \eta A = Q\,\mathrm{diag}(1 - \eta\lambda_i)\,Q^\top, so in the eigenbasis \|e_k\|^2 = \sum_i (1-\eta\lambda_i)^{2k} (q_i^\top e_0)^2. Each eigenmode contracts by |1-\eta\lambda_i| per step.

The maximum \rho of the |1 - \eta\lambda_i| is reached at \lambda_1 = L or \lambda_d = \mu.

One wants 1 - \eta L > -1 (i.e. \eta < 2/L) to avoid expanding the stiff modes, and \eta large to contract the flat modes.

The minimal \rho(\eta) = \max(|1-\eta L|,\, |1-\eta\mu|) equalizes the two terms, 1 - \eta\mu = \eta L - 1, i.e. \eta = 2/(L+\mu) and \rho = (L-\mu)/(L+\mu).

For \kappa \gg 1, \rho \approx 1 - 2/\kappa: \mathcal{O}(\kappa) iterations are needed to divide the error by a constant. ∎

Reading for what follows

  1. The step is bounded by the stiff directions but the slowness comes from the flat directions: the whole art of deep optimization consists in decoupling these scales — momentum (3.2), adaptive preconditioning (3.3), activation normalization (3.5).
  2. In noisy SGD, the same step 2/L remains the stability condition, and the residual noise fluctuates around the minimum at a distance proportional to \eta: decreasing \eta at the end of training (scheduling) amounts to “annealing”.
  3. On real networks, the landscape is non-convex, but saddle points — not local minima — are the main obstacle, and SGD noise helps precisely to escape them.

3.2. Momentum: convergence rates

Definition 3.2 — SGD with momentum (Polyak’s heavy ball) v_{k+1} = \beta v_k + \hat g_k, \qquad \theta_{k+1} = \theta_k - \eta\, v_{k+1}, \qquad \beta \in [0,1), with \hat g_k the mini-batch estimator of the gradient. The velocity v exponentially averages the past gradients: in a narrow valley where the gradient alternates +g, -g, +g, \dots (zigzag), the average cancels in the direction of the oscillation and accumulates in the direction of progress.

Proposition 3.3 — What momentum changes, mode by mode On the quadratic case of theorem 3.1 (exact gradient), the error follows e_{k+1} = (1 + \beta - \eta\lambda)\, e_k - \beta\, e_{k-1}. With the step \eta = (1-\beta)/L:

  • (i) the stiff mode \lambda = L converges like (\sqrt\beta)^k;
  • (ii) on a flat mode, the effective rate is 1 - \frac{\eta}{1-\beta}\lambda — everything happens as if the step were multiplied by 1/(1-\beta) on the slow directions without destabilizing the stiff ones.

The complete analysis (Polyak, 1964) gives a global rate in \mathcal{O}(1 - c/\sqrt\kappa) instead of \mathcal{O}(1 - c/\kappa), a rate optimally reached by Nesterov’s variant.

Proof. Inject the iteration into e_k: e_{k+1} = e_k - \eta A e_k + \beta(e_k - e_{k-1}), giving the announced difference equation. Per eigenmode (e_k collinear with the eigenvector q_i), the solutions are of the form e_k = z^k q_i where z solves the characteristic polynomial z^2 - (1 + \beta - \eta\lambda)\, z + \beta = 0. Write s = 1 + \beta - \eta\lambda. The product of the two roots equals \beta.

(i) Stiff modes. The roots are complex conjugates as soon as s^2 \le 4\beta, and their modulus then equals \sqrt{\beta} (product = \beta, equal modulus for two conjugates).

With \eta = (1-\beta)/L and \lambda \le L, one has \eta\lambda \le 1-\beta, so s \in [\,2\beta,\; 1+\beta).

For \lambda = L: s = 2\beta, and 2\beta \le 2\sqrt\beta whenever \beta \le 1 — condition satisfied, the stiff mode is damped by \sqrt\beta per step.

(ii) Flat modes. For \eta\lambda \ll 1, look for the root close to 1 in the form z = 1 - c\,\eta\lambda + o(\eta\lambda). Expanding z^2 - sz + \beta = 0 to first order with s = 1 + \beta - \eta\lambda: (1 - 2c\eta\lambda) - (1 + \beta - \eta\lambda)(1 - c\eta\lambda) + \beta = \eta\lambda\big(1 - c(1-\beta)\big) + o(\eta\lambda) = 0, hence c = 1/(1-\beta) and z_+ \approx 1 - \frac{\eta\lambda}{1-\beta}: the effective step of the flat modes is \eta/(1-\beta) — the moving average amplifies the displacement in the directions where the gradient is coherent, whereas (i) shows that it bounds the oscillation in the directions where it alternates.

∎

3.3. Adam, derived step by step

Algorithm 3.4 — Adam (Kingma & Ba, 2014) At each step t, with stochastic gradient g_t, \beta_1 = 0.9, \beta_2 = 0.999: m_t = \beta_1 m_{t-1} + (1-\beta_1) g_t \quad\text{(moving average of the gradient)}, v_t = \beta_2 v_{t-1} + (1-\beta_2) g_t^2 \quad\text{(moving average of squares, element-wise)}, \hat m_t = \frac{m_t}{1 - \beta_1^t}, \qquad \hat v_t = \frac{v_t}{1 - \beta_2^t} \quad\text{(bias correction)}, \theta_{t+1} = \theta_t - \eta\, \frac{\hat m_t}{\sqrt{\hat v_t} + \varepsilon}.

Proposition 3.5 — Why the bias correction is exact If the gradient is constant g_t \equiv g, then m_t = (1 - \beta_1^t)\, g and v_t = (1-\beta_2^t)\, g^2 (element by element). The correction therefore divides precisely by the underestimation factor: \hat m_t = g, \hat v_t = g^2.

Proof. By induction. m_t = (1-\beta_1)\sum_{k=1}^{t} \beta_1^{t-k} g_k. If g_k \equiv g: m_t = (1-\beta_1)\, g \sum_{j=0}^{t-1} \beta_1^{j} = (1-\beta_1)\, g\, \frac{1 - \beta_1^t}{1 - \beta_1} = (1-\beta_1^t)\, g (geometric sum).

Without correction, the moving average would start from m_0 = 0 and underestimate the gradient by a factor 1-\beta_1^t — by 90% at step 1 with \beta_1 = 0.9.

Adam’s first step would be ~10× too small (and for v, with \beta_2 = 0.999, ~1000× too small, which would make the update \hat m/\sqrt{\hat v} explode). The computation is identical for v_t with g^2. ∎

What Adam does, intuitively If the gradient is constant, \hat m_t / \sqrt{\hat v_t} \approx g/|g| = \mathrm{sign}(g): Adam moves by a step of size ~\eta per coordinate, independent of the gradient scale — a diagonal preconditioner that solves the decoupling \lambda_{\max}/\lambda_{\min} of theorem 3.1.

It is also its weakness: the weight of “calm” coordinates is relatively amplified, and the \varepsilon in the denominator must stay small (10^{-8}) so as not to crush small gradients.

Optimizer Update Notes
SGD \theta \leftarrow \theta - \eta\, \hat g useful noise (escapes saddle points); \eta < 2/L
Momentum v \leftarrow \beta v + \hat g; \theta \leftarrow \theta - \eta v inertia, damps zigzags (prop. 3.3)
RMSProp adaptive normalization by v alone per-coordinate step, no momentum
Adam bias-corrected first and second moments universal default; tune lr first
AdamW Adam + decoupled decay see 3.6 — the transformers standard

3.4. Step scheduling and warmup

3.5. BatchNorm and LayerNorm

Definition 3.6 — BatchNorm (Ioffe & Szegedy, 2015) and LayerNorm (Ba et al., 2016) BatchNorm normalizes each feature over the batch (the j-th coordinate of the activations, for a batch of size b): \hat h_{ij} = \frac{h_{ij} - \mu_j}{\sqrt{\sigma_j^2 + \varepsilon}}, \qquad \mu_j = \frac1b \sum_i h_{ij}, \quad \sigma_j^2 = \frac1b \sum_i (h_{ij} - \mu_j)^2, \qquad y_{ij} = \gamma_j \hat h_{ij} + \beta_j. At inference, \mu, \sigma are frozen moving averages. LayerNorm normalizes each example over its features (same formulas, but the means are taken over j at fixed i): independent of the batch, it is the norm of transformers.

Proposition 3.7 — Reparameterization invariance BatchNorm makes the layer output invariant when one replaces W by cW and b by cb (c > 0).

The statistics become c\mu, c^2\sigma^2, and \hat h is unchanged. The scaling effect is absorbed by \gamma.

Proof. If h' = cWh + cb = c\,h, then \mu' = c\mu, {\sigma^2}' = c^2\sigma^2 and \hat h' = (ch - c\mu)/\sqrt{c^2\sigma^2 + \varepsilon}. For \varepsilon small compared to \sigma^2, \hat h' \approx c(h-\mu)/(c\sigma) = \hat h.

The gradient of the loss with respect to the scale c of the weights is then ~zero: the optimization no longer “wastes” capacity correcting the scale of the activations, only their direction. ∎

The ICS controversy The historical explanation — “reducing internal covariate shift” — was largely debunked by Santurkar et al. (2018): BatchNorm works mainly because it reparameterizes the problem in a Lipschitz way (smoother loss landscape, better-conditioned gradients), not because it stabilizes distributions. Practical consequences: BatchNorm requires fairly large batches (the statistics become noisy otherwise) and behaves badly at inference with batch size 1; LayerNorm has no such dependencies. In transformers, LayerNorm is applied before each sub-block (pre-norm, chapter 6).

3.6. Regularization: the complete tour

Overfitting, order of profitability Diagnosis: a train/validation gap that opens while the train loss decreases. Remedies in order: 1) more data, 2) augmentation, 3) dropout, 4) weight decay, 5) shrink the model as a last resort. A model that cannot overfit a small batch (chapter 8) is often too small or badly wired — the capacity to overfit is first and foremost a health check.

Lab sheet — session 6: dropout and weight decay on an MLP Goal: open a train/validation gap on purpose, then close it with the tools of this section.

  1. Train an MLP 784–512–512–10 on 1,000 MNIST images (validation: 10,000 others) with no regularization, until the gap is clear.
  2. Add inverted dropout with p \in \{0.1, 0.3, 0.5\}. Check numerically that the mean activation is the same in model.train() and model.eval().
  3. Add weight decay with \lambda \in \{10^{-5}, 10^{-4}, 10^{-3}\}, once as an L2 term inside Adam and once with AdamW. Log the weight norm.
  4. Add label smoothing \varepsilon = 0.1 and compare the confidence of the wrong predictions.

Deliverable: a table of final gaps and validation accuracies, and one paragraph explaining the ranking.

4. Convolutional networks (CNN)

A fully connected layer on a 224 × 224 colour image needs about 150,000 weights per neuron, and it ignores that neighbouring pixels are related and that an object means the same thing wherever it appears. A convolutional layer solves both problems by sliding one small filter over the whole image. This chapter defines the convolution, computes output sizes and receptive fields, and explains why residual connections let these networks grow deep.

4.1. Discrete convolution: general definition and dimension arithmetic

Definition 4.1 — 1D convolution with stride, padding, dilation For

  • an input x of length I, a kernel k of length K,
  • stride s \in \mathbb{N}^*, padding p (zeros added on each side) and dilation d (gap between the kernel taps):

(x * k)(u) = \sum_{j=0}^{K-1} x(u + d j - d(K-1))\; k(K{-}1{-}j), the output being sampled at positions u = u_0 + s\,t, t = 0, 1, 2, \dots In 2D, the same formula applies on each axis (kernel K_1 \times K_2).

The same filter is applied at every point: shared weights ⇒ translation equivariance (shifting the input shifts the feature map by the same amount, for stride 1) and a parameter count independent of the image size. Invariance only comes after a global pooling. The count is \#\text{params} = C_{\mathrm{in}} K_1 K_2 C_{\mathrm{out}} + C_{\mathrm{out}}.

Theorem 4.2 — Output dimension formula (and proof) O = \left\lfloor \frac{I + 2p - d(K-1) - 1}{s} \right\rfloor + 1.

Proof. With padding p, the effective input has valid indices in [-p,\, I - 1 + p]. An output position t places the kernel’s first tap at u = s\,t - p and the last at u + d(K-1).

The convolution is defined only if all the taps fall within the valid indices of the padded input: s t - p \ge -p and s t - p + d(K-1) \le I - 1 + p. The number of allowed steps t is therefore the number of integers in \big[0,\; \frac{I - 1 + 2p - d(K-1)}{s}\big], that is \lfloor (I + 2p - d(K-1) - 1)/s \rfloor + 1 (length of a discrete interval [0, n] = n + 1, with a floor for stride alignment). ∎

Example 4.3 — Immediate checks

  • Conv 3×3, padding 1, stride 1 (I=I'): \lfloor (I + 2 - 2 - 1)/1 \rfloor + 1 = I.
  • Downsampling /2 with 3×3, padding 1: \lfloor (I + 2 - 2 - 1)/2 \rfloor + 1 = \lfloor (I-1)/2 \rfloor + 1 (for I = 224 (ImageNet): 112).
  • Two 3×3 convs (stride 1) have the same receptive field as a 5×5 conv for 28% fewer parameters (2 \times 9 = 18 weights per channel pair instead of 25) and one more nonlinearity — VGG’s argument.

4.2. Receptive field

Proposition 4.4 — Receptive field, formula by induction For convolutional layers with kernels k_\ell, strides s_\ell (dilation 1), the receptive field of a unit of layer \ell equals \mathrm{RF}_1 = k_1, \qquad \mathrm{RF}_\ell = \mathrm{RF}_{\ell-1} + (k_\ell - 1)\prod_{i < \ell} s_i.

Proof. By induction. Initialization: a unit of layer 1 reads k_1 pixels.

Heredity: let \Delta_{\ell} be the distance (in input pixels) between the receptive fields of two adjacent units of layer \ell. Clearly \Delta_1 = s_1 (shifting the output by one unit shifts the window by s_1), and \Delta_\ell = s_\ell \Delta_{\ell-1} = \prod_{i \le \ell} s_i.

A unit of layer \ell aggregates k_\ell contiguous units of layer \ell - 1: its field covers the field of one unit (RF_{\ell-1}) plus the gap between the centers of the first and last aggregated neighbors, (k_\ell - 1)\Delta_{\ell-1}. Hence the formula. ∎

Two stacked 3×3 convs RF = 3 + 2\times 1 = 5: stacking beats a large kernel — depth again. In practice the theoretical receptive fields quickly exceed the image, but the effective field (measured by occlusion, or by the gradient analysis of Luo et al., 2016) grows much more slowly; low layers encode edges, high layers concepts.

4.3. Transposed conv (deconvolution)

The gradient of the convolution with respect to the input is itself a convolution, by the flipped kernel and with adjusted padding — called transposed convolution, and used as is to upsample (GANs, segmentation, autoencoders).

Case with no stride, no dilation: a “valid” conv (p=0) maps I \to O = I - K + 1; its transpose maps O \to I = O + K - 1 by inserting K-1 border zeros and convolving with the flipped kernel.

Proof: write \mathrm{conv} as a linear map y = Cx by blocks; then \nabla_x = C^\top \nabla_y, and C^\top is the matrix of the transposed conv.

With stride and padding, the general formula: I' = s\,(O - 1) - 2p + d(K-1) + \mathrm{op} + 1, where \mathrm{op} is an optional extra padding — the exact mirror image of theorem 4.2. Tap overlap when s > 1 creates “checkerboard” artifacts, fixed by upsample + conv.

4.4. Historical architectures, parameters on the counter

Network Year Key idea Parameters
LeNet-5 1998 conv + pooling + FC, gradient by backpropagation ≈ 60 k
AlexNet 2012 ReLU, dropout, GPU, augmentation — wins ImageNet ≈ 60 M
VGG-16 2014 uniformity: only 3×3, depth 16-19 ≈ 138 M
ResNet-50 2015 residual connections, 50 layers and more ≈ 25.6 M

Example 4.5 — Counting VGG-16 parameters (a computation to redo) A 3×3 block from C_{\mathrm{in}} \to C_{\mathrm{out}} channels costs 9\,C_{\mathrm{in}} C_{\mathrm{out}} + C_{\mathrm{out}}. The first (3 → 64): 9 \times 3 \times 64 + 64 = 1\,792. A 64 → 64 block: 9 \times 64^2 + 64 = 36\,928.

Summing the five stages (64, 128, 256, 512, 512), the convolutions total \approx 14.7 M — but the three fully connected layers (after flattening 7 \times 7 \times 512 = 25\,088 features) weigh 25\,088 \times 4\,096 + 4\,096 \times 4\,096 + 4\,096 \times 1\,000 \approx 123\ \text{M}: 90% of the parameters live in the classifier!

Hence global pooling (GoogLeNet) then residual blocks: ResNet-50 does better with 25.6 M — of which ~23 M in 1×1/3×3 bottleneck convs.

Proposition 4.6 — Why residuals allow depth A residual block learns h^{(\ell+1)} = h^{(\ell)} + F(h^{(\ell)}) (then activation/normalization).

  1. Trivial identity: the choice F \equiv 0 realizes the exact identity — with standard initialization, F is small, so every added block starts “close to the identity”: adding layers no longer degrades the initial model.
  2. Gradient without attenuation: by the chain rule, \frac{\partial \mathcal{L}}{\partial h^{(\ell)}} = \frac{\partial \mathcal{L}}{\partial h^{(\ell+1)}}\Big( I + \frac{\partial F}{\partial h^{(\ell)}} \Big), the I term transports the gradient as is, whatever the depth — direct contrast with the product of L factors <1 of RNNs (chapter 5).
  3. Landscape: loss-function visualizations (Li et al., 2018) show markedly smoother valleys along the residual directions.

Residuals everywhere We will find exactly these skip connections in transformers (x + \mathrm{Attention}(\mathrm{LN}(x)), chapter 6) and GNNs (skip connections of message passing, Graph ML module): everywhere one stacks blocks, the identity path protects the gradient.

import torch.nn as nn

conv = nn.Conv2d(in_channels=3, out_channels=32, kernel_size=3, padding=1)
# parameters: 3*3*3*32 + 32 = 896, independent of the H x W size
x = torch.randn(16, 3, 64, 64)        # batch of 16 RGB 64x64 images
print(conv(x).shape)                   # [16, 32, 64, 64]  (theorem 4.2 formula)

class ResBlock(nn.Module):
    def __init__(self, c):
        super().__init__()
        self.conv1 = nn.Conv2d(c, c, 3, padding=1)
        self.conv2 = nn.Conv2d(c, c, 3, padding=1)
        self.norm1, self.norm2 = nn.BatchNorm2d(c), nn.BatchNorm2d(c)
    def forward(self, x):
        h = torch.relu(self.norm1(self.conv1(x)))
        return torch.relu(x + self.norm2(self.conv2(h)))   # skip connection

CNNs and graphs A convolution is a filter applied to a regular neighborhood (a grid). The GNNs of the Graph ML module generalize this idea to irregular neighborhoods: the filter becomes a function of the aggregated neighbors. Same DNA: weight sharing + local aggregation. For the full link with signal processing (circular convolution, convolution theorem, spectral graph filters), cf. the Signal Processing module and the Graph ML module.

5. Sequences: RNN, LSTM

Text, audio and time series have a variable length and an order that matters, so the network must carry information from one step to the next. A recurrent network does this with a hidden state updated at every step. This chapter shows why its gradient vanishes or explodes over long sequences, and how the gates of the LSTM mitigate the problem.

5.1. RNN and backpropagation through time

Definition 5.1 — RNN (Elman) h_t = \tanh(W_h h_{t-1} + W_x x_t + b), \qquad y_t = W_y h_t A hidden state that summarizes the past; weights shared across time (like the CNN kernel across space). The sequence (x_1, \dots, x_T) is processed by unrolling the network: T copies of the same block — the computation graph becomes acyclic again (chapter 2), and backprop applies to it “through time” (BPTT). Memory cost: \mathcal{O}(T) activations.

Theorem 5.2 — Exponential explosion or vanishing of the gradient For a loss at the final step T, writing D_t = \mathrm{diag}\big(\tanh'(a_t)\big): \frac{\partial \mathcal{L}_T}{\partial h_0} = \Big( \prod_{t=1}^{T} W_h^\top D_t \Big) \frac{\partial \mathcal{L}_T}{\partial h_T} \quad\Rightarrow\quad \Big\| \frac{\partial \mathcal{L}_T}{\partial h_0} \Big\| \;\le\; \|W_h\|^{T}\, \prod_{t=1}^{T}\|\tanh'(a_t)\|_\infty\, \Big\| \frac{\partial \mathcal{L}_T}{\partial h_T} \Big\|. If \|W_h\| \cdot \max_t \|\tanh'(a_t)\|_\infty < 1, the gradient decays exponentially with T (vanishing). Since \|\tanh'\|_\infty \le 1, explosion requires \|W_h\| > 1; this condition is necessary, not sufficient (Pascanu, Mikolov & Bengio, 2013).

Proof. By theorem 2.1 applied to the unrolled graph, since h_{t-1} has only one successor h_t, \partial \mathcal{L}_T/\partial h_{t-1} = (\partial h_t / \partial h_{t-1})^\top \partial \mathcal{L}_T/\partial h_t. Now h_t = \tanh(W_h h_{t-1} + W_x x_t + b), so the Jacobian equals J_t = D_t W_h with D_t diagonal (derivative of tanh component by component).

Composing from t = T down to t = 1 and using sub-multiplicativity \|ABC\| \le \|A\|\,\|B\|\,\|C\| (operator norm): \|\prod_t J_t^\top\| \le \prod_t \|J_t^\top\| \le \prod_t \|D_t\|\,\|W_h\|, and \|D_t\| = \max_i \tanh'(a_{t,i}) \le 1. The maximum is below 1 only when every coordinate is away from 0, and it becomes small only when every coordinate saturates: one saturated coordinate does not lower it.

Hence the bound: exponential decay in T as soon as the per-step factor is < 1. If \|W_h\| \le 1, every factor is at most 1 and the gradient cannot grow, so \|W_h\| > 1 is necessary for explosion. It is not sufficient: the bound is only an upper bound, and growth depends on the actual product of Jacobians. Experiments show both regimes. Explosion is handled by gradient clipping (truncating \|\hat g\| at a threshold); vanishing, for its part, is structural. ∎

5.2. LSTM: complete equations and the role of the gates

Definition 5.3 — LSTM (Hochreiter & Schmidhuber, 1997) Four “gates” (vectors of [0,1]^d via sigmoids, acting as multiplicative valves) and a cell state c_t that transitions additively: f_t = \sigma(W_f h_{t-1} + U_f x_t), \qquad i_t = \sigma(W_i h_{t-1} + U_i x_t), o_t = \sigma(W_o h_{t-1} + U_o x_t), \qquad \tilde c_t = \tanh(W_c h_{t-1} + U_c x_t), c_t = f_t \odot c_{t-1} + i_t \odot \tilde c_t, \qquad h_t = o_t \odot \tanh(c_t).

GRU (Cho et al., 2014) and comparison z_t = \sigma(W_z h_{t-1} + U_z x_t), \quad r_t = \sigma(W_r h_{t-1} + U_r x_t), \quad h_t = (1 - z_t) \odot h_{t-1} + z_t \odot \tanh\big(W h_{t-1} \odot r_t + U x_t\big). Two gates instead of three, no separate cell state (z_t merges forget and write, r_t filters the read past): ~25% fewer parameters, generally comparable performance. In practice LSTM for long series, GRU for small datasets.

Teacher forcing and exposure bias When training a sequence-to-sequence model, the decoder is fed the true tokens (teacher forcing): the loss is computed in a single parallel pass, but the model only learns to predict given the exact past. At inference, it feeds on its own (possibly wrong) outputs — an exposure mismatch that makes long generations drift; mitigated by scheduled sampling or reinforcement learning (cf. RL module, and NLP module).

Lab sheet — session 8: gradient norm through time Goal: see theorem 5.2 on a plot, then see the LSTM escape it.

  1. Build an Elman RNN (nn.RNN, tanh, 64 hidden units) on random sequences of length T \in \{10, 50, 100, 200\}, with a loss on the last step only.
  2. Call retain_grad() on every hidden state and plot \log \|\partial \mathcal{L}_T / \partial h_t\| against t. Initialize W_h as an orthogonal matrix times c \in \{0.5, 1, 1.5\}.
  3. Read the slope of each curve as the log of a per-step factor, and compare it with the per-step bound of theorem 5.2, \log c plus the mean over t of \log \max_i \tanh'(a_{t,i}).
  4. Redo step 2 with an LSTM whose forget bias is 1, then clip the gradient norm at 1.0 in the exploding case.

Deliverable: two plots and a paragraph linking each slope to \|W_h\| and to saturation.

5.3. Applications: matching the architecture to the task

The official syllabus closes its “Deep learning I” block with applications. The same backbones (a CNN for grids, an RNN for sequences) serve very different tasks; what changes is the output head, the loss and the evaluation.

Definition 5.4 — Output heads for the main tasks

  • Image classification: the feature maps are averaged over space (global average pooling), then a linear layer and a softmax give K class probabilities; loss: cross-entropy.
  • Object detection: for each candidate region the network outputs K class scores and 4 box coordinates; loss: cross-entropy plus a regression loss (L1 or smooth L1) on the coordinates. A predicted box counts as correct when its intersection over union \mathrm{IoU}(A, B) = |A \cap B| / |A \cup B| with a true box reaches a threshold, usually 0.5.
  • Semantic segmentation: one class per pixel; an encoder reduces the resolution, a decoder restores it with transposed convolutions (section 4.3), and skip connections carry fine detail from encoder to decoder (U-Net); loss: the per-pixel cross-entropy averaged over the image.
  • Sequences (RNN, LSTM): many-to-one tasks such as sentiment classification read the last state h_T; many-to-many tasks such as tagging apply a softmax to every h_t; sequence-to-sequence tasks such as translation or speech recognition use an encoder that summarizes the input and a decoder that generates the output, trained with teacher forcing (note above). Squeezing the whole input into one vector is the bottleneck that attention removes (chapter 6).

Example 5.5 — IoU and segmentation shapes A predicted box with corners (0, 0) and (4, 4) and a true box with corners (2, 2) and (6, 6) both have area 16.

Their intersection is the square from (2, 2) to (4, 4), of area 4, and their union has area 16 + 16 - 4 = 28. The IoU is 4/28 \approx 0.14, far below 0.5: the detection is a miss, although the boxes overlap.

For segmentation, take an input of shape 3 \times 256 \times 256 and an encoder of four stride-2 stages ending at 512 \times 16 \times 16. Four transposed convolutions with kernel 2 and stride 2 each double the side, since (n - 1) \times 2 + 2 = 2n, giving back 256 \times 256.

A final 1 \times 1 convolution to K channels yields logits of shape K \times 256 \times 256, and the loss averages 65{,}536 pixel-wise cross-entropies.

6. Attention and transformers

A recurrent network reads a sequence one step at a time: training cannot be parallelized over positions, and information from far back must survive many multiplications. Attention lets every token look directly at every other token in a single matrix operation. This chapter defines it, explains the \sqrt{d_k} scaling, and assembles the transformer block on which every LLM is built.

6.1. Scaled dot-product: why \sqrt{d_k}

Definition 6.1 — Scaled dot-product attention (Vaswani et al., 2017) For tokens i = 1..n with embeddings x_i, one computes q_i = W_q x_i,\quad k_i = W_k x_i,\quad v_i = W_v x_i, with W_q, W_k \in \mathbb{R}^{d_k \times d} in the column form of a linear layer. Code stacks the n tokens as the rows of X \in \mathbb{R}^{n \times d} and uses the row form Q = X W_Q, \qquad W_Q = W_q^\top \in \mathbb{R}^{d \times d_k}, so that row i of Q is q_i^\top; likewise K = XW_K and V = XW_V. Then:

Key result — scaled dot-product attention \mathrm{Attention}(Q, K, V) = \mathrm{softmax}\!\Big(\frac{Q K^\top}{\sqrt{d_k}}\Big) V, \qquad \alpha_{ij} = \frac{\exp(q_i^\top k_j / \sqrt{d_k})}{\sum_{j'} \exp(q_i^\top k_{j'} / \sqrt{d_k})}.

Multi-head: h attention heads in parallel (projections W^q_m, W^k_m, W^v_m, dimensions d_k = d/h), concatenated then projected — several independent “communication channels”.

Proposition 6.2 — The variance of the scores grows like d_k If the components of q and k are iid, centered, with variance 1, then q^\top k = \sum_{\ell=1}^{d_k} q_\ell k_\ell is centered with variance exactly d_k.

Without normalization, the standard deviation of the scores is \sqrt{d_k} (32 for d_k = 1024): the softmax becomes a one-hot argmax, its gradients vanish — saturation, cf. chapter 0, proposition 0.3. Dividing by \sqrt{d_k} brings the variance back to 1, independent of the dimension.

Proof. Centering (independence): \mathbb{E}[q_\ell k_\ell] = \mathbb{E}[q_\ell]\,\mathbb{E}[k_\ell] = 0. Variance: the terms q_\ell k_\ell are mutually iid, so \mathrm{Var}(\sum_\ell q_\ell k_\ell) = \sum_\ell \mathrm{Var}(q_\ell k_\ell) = d_k\, \mathbb{E}[q_\ell^2]\mathbb{E}[k_\ell^2] = d_k. After dividing by \sqrt{d_k}, the variance of the normalized score equals 1.

Consequence for saturation: the typical gaps between normalized scores are \mathcal{O}(1), the exponential ratios \exp(s_i - s_j) stay in a reasonable range and the softmax keeps a nonzero gradient; without scaling, gaps of order \sqrt{d_k} \gg 1 produce a near one-hot distribution, whose gradient (\hat y_k(\delta_{kj} - \hat y_j)) tends to 0. ∎

6.2. Block architecture: pre-norm, positions, RoPE

6.3. Costs and efficient attention

“Graph” reading (Karpathy, “Let’s build GPT” lecture, 2023) — the bridge to the Graph ML module A transformer is a neural network on a dynamic graph:

  • each token = a node carrying a vector;
  • attention computes weighted edges \alpha_{ij} from the data (dynamic graph, unlike GNNs);
  • \sum_j \alpha_{ij} v_j is a message passing: the nodes exchange their “values”;
  • the MLP block = the local computation of each node; residual + LayerNorm = plumbing so that the “graph” stays trainable;
  • a transformer layer = communication (attention) then computation (MLP), exactly the alternation of a GNN.

The LLGA program exploits this double reading: the same mathematical object seen “from inside the LLM” (M2 module) and “on structured data” (Graph ML module).

6.4. A complete block in PyTorch

import torch, torch.nn as nn, torch.nn.functional as F

class Block(nn.Module):
    """A pre-norm transformer block: multi-head attention (causal by default) + MLP."""
    def __init__(self, d, n_heads, p_drop=0.1, causal=True):
        super().__init__()
        self.ln1, self.ln2 = nn.LayerNorm(d), nn.LayerNorm(d)
        self.n_heads, self.d_head, self.causal = n_heads, d // n_heads, causal
        self.qkv = nn.Linear(d, 3 * d)
        self.proj = nn.Linear(d, d)
        self.mlp = nn.Sequential(nn.Linear(d, 4 * d), nn.GELU(), nn.Linear(4 * d, d))
        self.drop = nn.Dropout(p_drop)

    def forward(self, x):                          # x: (B, n, d)
        B, n, d = x.shape
        q, k, v = self.qkv(self.ln1(x)).chunk(3, -1)
        # multi-head: (B, n, d) -> (B, n, h, d_head) -> (B, h, n, d_head)
        def split(t): return t.view(B, n, self.n_heads, self.d_head).transpose(1, 2)
        q, k, v = split(q), split(k), split(v)
        mask = (torch.tril(torch.ones(n, n, dtype=torch.bool, device=x.device))  # causal mask:
                if self.causal else None)                                     # True = may attend
        att = F.scaled_dot_product_attention(q, k, v,            # softmax(QK^T/sqrt(d_head))V
                                             attn_mask=mask, dropout_p=0.0)
        att = att.transpose(1, 2).reshape(B, n, d)               # merge the heads back
        x = x + self.drop(self.proj(att))                        # residual 1 (pre-norm)
        x = x + self.drop(self.mlp(self.ln2(x)))                 # residual 2
        return x

torch.manual_seed(0)
x = torch.randn(2, 16, 128)
model = nn.Sequential(*[Block(128, 4) for _ in range(4)])
print(model(x).shape)                              # [2, 16, 128]

# Without positions (and without the causal mask), self-attention is permutation-
# equivariant: permuting the tokens permutes the outputs. Positions are added to x (6.2).
max_n, d = 512, 128
pos = torch.arange(max_n).unsqueeze(1)                        # (max_n, 1)
freq = 10000 ** (-torch.arange(0, d, 2) / d)                  # (d/2,) frequencies omega_i
pe = torch.zeros(max_n, d)
pe[:, 0::2], pe[:, 1::2] = torch.sin(pos * freq), torch.cos(pos * freq)   # sinusoidal
learned = nn.Embedding(max_n, d)                              # or a learned table
n = x.shape[1]
print(model(x + pe[:n]).shape, model(x + learned(torch.arange(n))).shape)

6.5. Modern NLP: tokens, embeddings and pretraining objectives

The official syllabus lists “modern natural language processing” and “embeddings”. A transformer consumes vectors, not text: this section explains how a sentence becomes a sequence of vectors, and what a language model is trained to predict before any task-specific adaptation (chapter 7).

Definition 6.3 — Subword tokenization by byte-pair encoding (BPE) Start from a vocabulary of characters (or bytes) and split every word of the training corpus into them, with an end-of-word marker _. Repeatedly count all pairs of adjacent symbols, weighted by word frequency, and merge the most frequent pair into a new symbol, until the vocabulary \mathcal{V} reaches a chosen size |\mathcal{V}| (about 3 \times 10^4 to 2 \times 10^5 in current models). A new text is tokenized by applying the learned merges in the same order. No word is ever out of vocabulary, frequent words become single tokens and rare words are split into reusable pieces.

Example 6.4 — Five merges by hand Corpus: “low” ×5, “lower” ×2, “newest” ×6, “widest” ×3. The pairs e s, s t and t _ each occur 6 + 3 = 9 times, more than any other pair, so the first merges are es, then est, then est_ (ties broken by order of appearance). Next come l o and lo w, with 7 occurrences each. After these five merges the unseen word “lowest” is encoded as two tokens, low and est_: the tokenizer has discovered a stem and a suffix from frequencies alone.

Definition 6.5 — Embedding matrix, static and contextual embeddings Each token w \in \mathcal{V} is mapped to a row E_w of a learned matrix E \in \mathbb{R}^{|\mathcal{V}| \times d}; this is the product of the one-hot vector of w with E, so the gradient touches only the rows of tokens present in the batch.

Many language models reuse E at the output, computing the next-token logits as E\,h (weight tying).

A static embedding gives one vector per word whatever its context. Word2vec’s skip-gram with negative sampling learns it without labels; its loss, its gradient and its matrix-factorization reading are derived later, in theorem 4.1 of the NLP module (Period 2); here it is only stated. With a center vector v_w, a context vector u_c and k negatives n_i drawn in proportion to frequency^{3/4}, one minimizes \mathcal{L} = -\log\sigma(u_c^\top v_w) - \sum_{i=1}^{k}\log\sigma(-u_{n_i}^\top v_w). At the optimum, u_c^\top v_w is the pointwise mutual information of (w, c) minus \log k (Levy & Goldberg, 2014): words that share contexts get similar vectors.

The output of a transformer layer is a contextual embedding: the vector of “bank” differs in “river bank” and in “bank account”, because attention has mixed in the neighbouring tokens.

Definition 6.6 — Pretraining objectives A causal (autoregressive) language model factorizes \mathbb{P}(x_1, \dots, x_n) = \prod_t \mathbb{P}(x_t \mid x_{<t}) and minimizes the average negative log-likelihood \mathcal{L} = -\frac1n\sum_t \log p_\theta(x_t \mid x_{<t}), using the causal mask of section 6.4; this is the decoder-only family (GPT and most current LLMs).

A masked language model (BERT, an encoder) hides about 15% of the tokens and predicts them from the context on both sides; its representations serve classification and retrieval. Encoder–decoder models (T5) reconstruct masked spans of the input. In each case the labels come from the text itself: this is self-supervised learning.

The quality of a causal model is reported as its perplexity \exp(\mathcal{L}).

Example 6.7 — Perplexity by hand A model assigns probabilities \tfrac12, \tfrac14, \tfrac18, \tfrac12 to the four tokens of a sentence. Its loss, in nats, is \mathcal{L} = \tfrac14(\ln 2 + \ln 4 + \ln 8 + \ln 2) = \tfrac74\ln 2 \approx 1.21, and its perplexity is 2^{7/4} \approx 3.4: at each step the model is as uncertain as a uniform choice among about 3.4 tokens.

A uniform model over a vocabulary of 50{,}000 tokens has perplexity 50{,}000.

import torch, torch.nn.functional as F
vocab, n = 50_000, 4                            # |V| tokens, n positions
logits = torch.randn(n, vocab)                  # one row of scores per position
targets = torch.randint(0, vocab, (n,))         # the next tokens
loss = F.cross_entropy(logits, targets)          # average NLL over positions
print(loss.item(), torch.exp(loss).item())       # loss in nats, perplexity

6.6. Transformers beyond text: the vision transformer (ViT)

A transformer only needs a sequence of vectors. The vision transformer of Dosovitskiy et al. (2021) turns an image into such a sequence by cutting it into patches, and then uses the encoder of 6.4 unchanged, without the causal mask.

Definition 6.8 — Vision transformer (ViT) For an image of size H \times W with C channels and a patch side P:

  1. Patchify: cut the image into N = HW/P^2 patches and flatten each into a vector of size P^2C.
  2. Linear projection: map each patch to \mathbb{R}^d with one shared matrix E \in \mathbb{R}^{P^2C \times d}, the same for every patch.
  3. [CLS] token: prepend one learned vector, whose final state serves as the summary of the image.
  4. Learned positions: add a learned table of N + 1 positions (6.2), so that the model knows where each patch was.
  5. Encoder and head: apply a stack of bidirectional blocks (12 in ViT-B), then a linear classifier on the final [CLS] state.
import torch, torch.nn as nn

B, C, H, W, P, d = 8, 3, 224, 224, 16, 768
x = torch.randn(B, C, H, W)
patchify = nn.Conv2d(C, d, kernel_size=P, stride=P)      # = one shared linear map per patch
tok = patchify(x).flatten(2).transpose(1, 2)               # (B, 196, 768)
cls = nn.Parameter(torch.zeros(1, 1, d))                   # [CLS] token
pos = nn.Parameter(torch.zeros(1, 1 + tok.shape[1], d))    # learned positions
z = torch.cat([cls.expand(B, -1, -1), tok], 1) + pos       # (B, 197, 768): ready for the blocks
print(z.shape)

Example 6.9 — ViT-B/16 on the counter A 224 \times 224 RGB image with P = 16 gives N = 196 patches of 16 \cdot 16 \cdot 3 = 768 numbers, plus [CLS]: n = 197 tokens of width d = 768, through 12 blocks with 12 heads.

  • Parameters: about 12d^2 = 7.1 M per block, hence about 86 M in all; the 12 encoder blocks of BERT-base have the same size.
  • Compute per block: the projections and the MLP cost 12 n d^2 multiply–adds, the scores and their product with V cost 2n^2d.
  • Total: about 17.5 \times 10^9 multiply–adds per image (the figure often quoted as “17.6 GFLOPs” counts multiply–adds). The n^2 term is only 4% of it at n = 197.

With P = 8 the image gives n = 785 tokens and the cost rises to about 78 \times 10^9 multiply–adds: a smaller patch buys resolution with compute, and the quadratic term starts to count.

Inductive bias, and where ViT goes next A CNN builds locality and translation equivariance into its weights (ch. 4); a ViT has neither, apart from the patch grid, and must learn them from data. This is why ViT matches or beats ResNets only after pretraining on large datasets (ImageNet-21k, JFT-300M), and why Exercise 12 compares the two on MNIST. ViT is the image encoder of CLIP and of most vision-language models (Multimodal Generative AI module); DeiT and Swin are treated in Advanced DL, ch. 4.

7. Transfer learning and fine-tuning

Training “from scratch” has become the exception: the dominant practice consists in starting from a model pre-trained on immense corpora (ImageNet for vision, terabytes of text for LLMs) and adapting it. This chapter gives the framework and the vocabulary; the LLMs module (M2) applies it at scale.

7.1. The three adaptation regimes

Why it works The low layers learn quasi-universal features (chapter 4: edges and textures in vision; in a pretrained text transformer, parts of speech and local syntax, as the probing study of Tenney, Das & Pavlick, 2019, shows); task specificity lives in the high layers. The performance versus number of labeled domain examples curve shifts massively to the left — this is the most measurable practical benefit of modern deep learning.

Worked example — linear probe, partial and full fine-tuning of a ResNet-18 Adapt an ImageNet-pretrained ResNet-18 (11.18 M weights without its 1000-class head) to a 10-class task. The new head has 512 \times 10 + 10 = 5{,}130 weights.

Regime Trainable weights The backward pass goes through Learning rate
Linear probe 5,130 (0.05%) the head only large (fresh head)
Partial: last stage + head 8.40 M (75%) layer4 and the head small for layer4, large for the head
Full fine-tuning 11.18 M (100%) the whole network 10 to 100 × below pretraining
  • Partial fine-tuning is cheap in compute, not in parameters. The last stage holds three quarters of the weights of a ResNet-18, but the backward pass stops there and the frozen stages store no activations.
  • The domain decides. On a target close to ImageNet with a few hundred labels per class, the probe often comes within a few points of full fine-tuning and barely overfits. The further the domain moves (medical images, sketches), the more of the high layers must be retrained.
  • Run the probe first. It is the floor that any fine-tuning must beat, and it measures the quality of the features (exercise 12, step (d)). Keep frozen BatchNorm layers in eval() mode, or their running statistics still move.
def set_regime(model, regime):          # a torchvision resnet18 whose model.fc = nn.Linear(512, 10)
    for p in model.parameters():
        p.requires_grad = (regime == "full")
    if regime == "partial":
        for p in model.layer4.parameters():
            p.requires_grad = True
    for p in model.fc.parameters():     # the new head is always trained
        p.requires_grad = True
    return sum(p.numel() for p in model.parameters() if p.requires_grad)   # 5130 / 8398858 / 11181642

7.2. Domain adaptation, in a nutshell

When the target has no labels (labeled source domain → unlabeled target domain), domain adaptation aligns the distributions: adversarial (a discriminator tries to guess the domain from the features: they must become non-discriminative), or via distribution distances (MMD, Wasserstein distances — a preview: they are defined in the Optimal Transport module and in Graph ML 9.6). The guiding idea: learn representations invariant to the domain but discriminative for the task.

7.3. LoRA: a mathematical sketch

Definition 7.1 — LoRA (Hu et al., 2021) For a pre-trained matrix W_0 \in \mathbb{R}^{d_{\mathrm{out}} \times d_{\mathrm{in}}}, one freezes W_0 and learns a low-rank update: W = W_0 + \Delta W = W_0 + \frac{\alpha}{r}\, B A, \qquad B \in \mathbb{R}^{d_{\mathrm{out}} \times r},\ A \in \mathbb{R}^{r \times d_{\mathrm{in}}},\ r \ll \min(d_{\mathrm{in}}, d_{\mathrm{out}}), with B initialized to zero and A Gaussian: \Delta W = 0 at the start, training departs exactly from the pre-trained model. \alpha/r sets the effective scale of the update.

Why a low rank suffices The working hypothesis, backed by Aghajanyan et al. (ACL 2021), is that weight updates during adaptation have a low “intrinsic rank”: \Delta W is well approximated by a product BA of small rank r (4 to 64 in practice).

Let us count: adapting a d \times d layer with r = 8 costs 2dr = 16d parameters instead of d^2 — for d = 4096, a factor of d/16 = 256.

Other benefits: a single W_0 kept in memory + per-task interchangeable (A, B) pairs (instant swap); and for inference latency, one can merge W = W_0 + BA at no extra cost. The generalizations (QLoRA: quantized backbone + LoRA) are covered in the LLMs module (M2), section 4.3.

import torch, torch.nn as nn

class LoRALinear(nn.Module):
    def __init__(self, base: nn.Linear, r=8, alpha=16):
        super().__init__()
        self.base = base                       # W0 frozen
        for p in self.base.parameters(): p.requires_grad = False
        self.A = nn.Parameter(torch.randn(r, base.in_features) * 0.01)
        self.B = nn.Parameter(torch.zeros(base.out_features, r))
        self.scale = alpha / r
    def forward(self, x):
        return self.base(x) + self.scale * (x @ self.A.T @ self.B.T)
# trainable params: r*(in+out) instead of in*out

8. Training in practice

A network that does not learn almost always has a mundane bug. This chapter is the diagnostic protocol we will apply in the labs, in the order in which it must be applied.

8.1. The training recipe, in order

  1. Shape sanity check: one forward pass without errors, the dimensions of every activation verified (rule of ch. 0: gradients have the shape of their variable).
  2. Overfit a single batch: 32 examples, train until the loss is \approx 0. If the model cannot do it, the bug is structural (data, loss, initialization, wild learning rate) — no point training longer. A model unable to memorize 32 examples is broken or too small; this is THE test that avoids weeks of silent error.
  3. LR range test (Smith, 2015): \eta increasing geometrically from 10^{-7} to 10^{0}, plot the loss: plateau → descent → divergence. Take the \eta at the dip (or slightly before): it sets AdamW/SGD in one epoch.
  4. Small full training run on ~10% of the data, with the train/val curves: check that the dynamics are healthy before scaling up.
  5. Scaling + regularization: larger batch (or gradient accumulation), mixed precision, data augmentation, dropout, weight decay — in that order of payoff (ch. 3).
  6. Checkpointing: save regularly (weights + optimizer state + current step + data generator seed), to resume exactly.

8.2. Monitoring: reading your curves

Symptom Likely cause Response
loss = NaN lr too large, log(0), unprotected softmax (ch. 0), fp16 overflow divide lr by 10, log-softmax, loss scaling
one-off loss spikes aberrant batch, lr near the edge of 2/L (thm. 3.1) inspect the data, lower the lr, gradient clipping
exploding gradient norm poorly conditioned recurrences (ch. 5) clip \|\hat g\| to a threshold (standard: 1.0)
train goes down, val stagnates overfitting ch. 3.6, in order of payoff
neither train nor val moves lr too small, blockage (dead ReLUs), loss mis-wired LR range test, per-layer activation statistics

Tools: tensorboard/wandb; systematically log the loss, accuracy, gradient norm, weight norm, the fraction of zero activations (dying ReLU), and the activation histograms of a few layers.

8.3. GPU: the levers that matter

The loop below puts the first and last of these levers together with experiment tracking. Autocast runs the matrix products in 16 bits and keeps reductions in float32; the gradient scaler is needed only for float16, whose narrow exponent range lets small gradients underflow (ch. 2.2). It runs as is on a CPU, in bfloat16.

import torch, torch.nn as nn
from torch.utils.tensorboard import SummaryWriter        # pip install tensorboard

device = "cuda" if torch.cuda.is_available() else "cpu"
model = nn.Sequential(nn.Linear(32, 256), nn.GELU(), nn.Linear(256, 10)).to(device)
opt = torch.optim.AdamW(model.parameters(), lr=3e-4, weight_decay=1e-2)
use_fp16 = device == "cuda" and not torch.cuda.is_bf16_supported()  # old GPUs: fp16
dtype = torch.float16 if use_fp16 else torch.bfloat16
scaler = torch.amp.GradScaler(device, enabled=use_fp16)  # loss scaling, needed for fp16 only
writer = SummaryWriter("runs/exp1")                      # then: tensorboard --logdir runs

for step in range(100):
    x = torch.randn(64, 32, device=device)
    y = torch.randint(0, 10, (64,), device=device)
    with torch.autocast(device_type=device, dtype=dtype):  # matmuls in 16 bits, reductions in fp32
        loss = nn.functional.cross_entropy(model(x), y)
    opt.zero_grad(set_to_none=True)
    scaler.scale(loss).backward()          # multiply the loss so that small fp16 gradients survive
    scaler.unscale_(opt)                   # true gradients again, before clipping
    gnorm = nn.utils.clip_grad_norm_(model.parameters(), 1.0)
    scaler.step(opt)                       # skips the step if an inf or NaN appeared
    scaler.update()
    if step % 10 == 0:                     # .item() waits for the GPU: log every k steps only
        writer.add_scalar("train/loss", loss.item(), step)
        writer.add_scalar("train/grad_norm", gnorm.item(), step)
writer.close()

8.4. Reproducibility

import torch, numpy as np, random

def seed_everything(seed=42):
    random.seed(seed); np.random.seed(seed); torch.manual_seed(seed)
    torch.cuda.manual_seed_all(seed)

seed_everything(42)
torch.backends.cudnn.deterministic = True    # determinism (speed cost)
torch.backends.cudnn.benchmark = False       # disable non-deterministic autotuning

Set the seeds (python/numpy/torch/cuda), the data shuffling, and record the library versions; some CUDA kernels remain non-deterministic by nature (atomics); aim for reproducibility up to a tolerance rather than bit-for-bit, and document configurations and seeds in every experiment. Reproducibility is also a debugging tool: a random bug you know how to reproduce is half fixed.

9. Generative models: AE, VAE, GAN

The networks of the previous chapters predict a label y from an input x; a generative model learns the distribution of x itself, so that it can produce new images or sentences. This chapter compares three ways of doing so — autoencoders, variational autoencoders and generative adversarial networks — and derives the training objective of each.

9.1. Deterministic autoencoder and its pathology

An autoencoder compresses x into a code z = E_\phi(x) and reconstructs \hat x = D_\theta(z), trained on \|x - \hat x\|^2. Useful for representation learning though it is, it is not a generative model: the latent space has no imposed structure — sampling z at random gives aberrant reconstructions. The VAE corrects precisely this.

Linear autoencoder and PCA (Baldi & Hornik, 1989) Take centred data, a linear encoder z = W_e x with W_e \in \mathbb{R}^{k \times d}, a linear decoder \hat x = W_d z and the squared reconstruction error.

The map x \mapsto W_d W_e x has rank at most k, so by the Eckart–Young theorem the best achievable error is that of the projection onto the top k principal directions, and every global minimizer reconstructs through exactly that projection (when \lambda_k > \lambda_{k+1}). The weights themselves are identified only up to an invertible k \times k change of basis of the code.

A linear autoencoder is thus PCA (Machine Learning module, section 7.3) learned by gradient descent; nonlinear encoders and decoders extend dimensionality reduction to curved data, and a denoising autoencoder, which reconstructs x from a corrupted copy, learns features that are robust to that corruption.

9.2. VAE: the ELBO fully derived

Sections 9.2 and 9.3 are the site’s reference derivations of the ELBO and of the GAN equilibrium: the Multimodal Generative AI module and others recall them and link here.

Definition 9.1 — VAE (Kingma & Welling, 2013) Diagonal Gaussian encoder q_\phi(z \mid x) = \mathcal{N}\big(\mu_\phi(x), \mathrm{diag}(\sigma_\phi^2(x))\big), decoder p_\theta(x \mid z), prior z \sim \mathcal{N}(0, I). One maximizes the ELBO (evidence lower bound): \mathrm{ELBO}(\theta, \phi; x) = \mathbb{E}_{q_\phi}\big[\log p_\theta(x \mid z)\big] - \mathrm{KL}\big(q_\phi(z \mid x)\,\|\,\mathcal{N}(0, I)\big), that is, one minimizes the training loss \mathcal{L}_{\mathrm{VAE}} = -\mathrm{ELBO}, averaged over the data.

Theorem 9.2 — Exact decomposition of the log-likelihood \log p_\theta(x) = \mathrm{ELBO}(\theta, \phi; x) + \mathrm{KL}\big(q_\phi(z\mid x)\,\big\|\,p_\theta(z \mid x)\big) \;\ge\; \mathrm{ELBO}(\theta, \phi; x). Maximizing the ELBO = maximizing a lower bound of \log p_\theta(x) and bringing q_\phi(z \mid x) closer to the exact posterior p_\theta(z \mid x).

Proof. Start from the definition of the KL between q_\phi(\cdot \mid x) and the posterior p_\theta(\cdot \mid x), both over z (at fixed x), and expand with p_\theta(x, z) = p_\theta(x \mid z)\, p(z) (Bayes): \mathrm{KL}\big(q_\phi \| p_\theta(\cdot \mid x)\big) = \mathbb{E}_q[\log q] - \mathbb{E}_q[\log p_\theta(x \mid z)] - \mathbb{E}_q[\log p(z)] + \log p_\theta(x). Now, by definition of the KL against the prior, \mathbb{E}_q[\log q] - \mathbb{E}_q[\log p(z)] = \mathrm{KL}(q_\phi \| \mathcal{N}(0,I)). Substituting: \mathrm{KL}\big(q_\phi \| p_\theta(\cdot \mid x)\big) = \mathrm{KL}(q_\phi \| \mathcal{N}) - \mathbb{E}_q[\log p_\theta(x \mid z)] + \log p_\theta(x), that is, rearranging: \log p_\theta(x) = \underbrace{\mathbb{E}_q[\log p_\theta(x \mid z)] - \mathrm{KL}(q_\phi \| \mathcal{N})}_{=\ \mathrm{ELBO}(\theta,\phi;x)} + \mathrm{KL}\big(q_\phi \| p_\theta(\cdot \mid x)\big). The KL is nonnegative (Jensen applied to \mathbb{E}_q[\log(q/p)] \ge -\log \mathbb{E}_q[p/q] = 0), giving the bound. Equality iff q_\phi(z \mid x) = p_\theta(z \mid x) exactly. ∎

Reading the proof One cannot optimize \log p_\theta(x) = \log \int p_\theta(x \mid z)\, p(z)\, dz directly: the integral is intractable. The ELBO is the only quantity that requires only (i) samples from q_\phi (Monte Carlo: a single one suffices in practice) and (ii) a closed-form KL. It is the same trade-off as in EM (Machine Learning module): the residual KL measures exactly what the bound loses.

Proposition 9.3 — Reparameterization: why z = \mu + \sigma \odot \varepsilon works If \varepsilon \sim \mathcal{N}(0, I) and z = \mu_\phi(x) + \sigma_\phi(x) \odot \varepsilon, then z \sim \mathcal{N}(\mu_\phi(x), \mathrm{diag}(\sigma_\phi^2(x))) and \nabla_\phi \mathbb{E}_{q_\phi}[f(z)] = \mathbb{E}_\varepsilon\big[\nabla_\phi f(\mu_\phi + \sigma_\phi \odot \varepsilon)\big]: the gradient gets through. Sampling z \sim q_\phi directly blocks the gradient (the sampling operation has no derivative with respect to its parameters).

Proof. The law of z is obtained by an affine change of variable, whose Jacobian \partial z/\partial \varepsilon = \mathrm{diag}(\sigma) is constant: the density of z is that of a Gaussian with mean \mu, covariance \mathrm{diag}(\sigma^2).

For the gradient: the expectation under q_\phi rewrites as an expectation under the fixed law of \varepsilon, \mathbb{E}_{q_\phi}[f(z)] = \mathbb{E}_{\varepsilon}[f(\mu_\phi + \sigma_\phi \odot \varepsilon)]; the distribution is no longer parameterized by \phi — \phi now appears only in the integrand, which gradient descent knows how to differentiate (theorem 2.1). ∎

Proposition 9.4 — Closed-form KL between diagonal Gaussians For q = \mathcal{N}(\mu, \mathrm{diag}(\sigma^2)) and p = \mathcal{N}(0, I) in dimension k: \mathrm{KL}(q \| p) = \frac12 \sum_{i=1}^{k} \big(\mu_i^2 + \sigma_i^2 - 1 - \log \sigma_i^2\big).

Proof. \mathrm{KL}(q\|p) = \mathbb{E}_q[\log q(z) - \log p(z)]. Coordinate by coordinate (the densities factorize), with \varphi the standard Gaussian density: \log q(z_i) - \log \varphi(z_i) = -\tfrac12\log\sigma_i^2 - \frac{(z_i - \mu_i)^2}{2\sigma_i^2} + \frac{z_i^2}{2}. Under q, z_i \sim \mathcal{N}(\mu_i, \sigma_i^2), so \mathbb{E}_q[(z_i - \mu_i)^2] = \sigma_i^2 and \mathbb{E}_q[z_i^2] = \mu_i^2 + \sigma_i^2. Taking the expectation: -\tfrac12\log\sigma_i^2 - \tfrac12 + \tfrac12(\mu_i^2 + \sigma_i^2), that is \tfrac12(\mu_i^2 + \sigma_i^2 - 1 - \log \sigma_i^2).

Along the way one checks that each term is \ge 0, zero iff \mu_i = 0, \sigma_i = 1 (study of u - 1 - \log u \ge 0). ∎

The ELBO trade-off in practice First term: reconstruction. Second: a regularizer that keeps the codes close to the prior — too strong, everything blurs together (codes ignored, blurry sampling); too weak, the latent space develops “holes” (partial posterior collapse). The connection with variational inference and EM is taken up again in the Machine Learning module; the extensions (\beta-VAE, VQ-VAE) are in Multimodal Generative AI.

9.3. GAN: the minimax game and its equilibrium

Definition 9.5 — GAN (Goodfellow et al., 2014) Minimax game between a generator G (often G(z) with z \sim \mathcal{N}(0, I)) and a discriminator D : \mathcal{X} \to (0,1): \min_G \max_D \ V(G, D), \qquad V(G, D) = \mathbb{E}_{x \sim p_{\mathrm{data}}}\big[\log D(x)\big] + \mathbb{E}_{z \sim p(z)}\big[\log(1 - D(G(z)))\big].

Theorem 9.6 — Optimal discriminator and global equilibrium

  1. For fixed G, the maximand reads (with p_G the law of G(z)) V(G,D) = \int \big[p_{\mathrm{data}}(x)\log D(x) + p_G(x) \log(1 - D(x))\big] dx, maximized pointwise at D^\star(x) = \frac{p_{\mathrm{data}}(x)}{p_{\mathrm{data}}(x) + p_G(x)}.
  2. The value of the game is then V(G, D^\star) = -\log 4 + 2\,\mathrm{JSD}(p_{\mathrm{data}} \| p_G), minimized over G exactly when p_G = p_{\mathrm{data}}: the global equilibrium exists and coincides with the law of the data.

Proof. 1) For each fixed x, maximize a\log D + b\log(1-D) with a = p_{\mathrm{data}}(x), b = p_G(x).

The derivative a/D - b/(1-D) = 0 gives D^\star = a/(a+b) (strict concavity in D \in (0,1)).

2) Rewrite \log D^\star = \log \frac{p_{\mathrm{data}}}{p_{\mathrm{data}}+p_G} = \log\frac{p_{\mathrm{data}}}{m} - \log 2 and \log(1 - D^\star) = \log \frac{p_G}{m} - \log 2, where m = \frac12(p_{\mathrm{data}} + p_G). Then V(G, D^\star) = \int p_{\mathrm{data}} \log\frac{p_{\mathrm{data}}}{m} + \int p_G \log\frac{p_G}{m} - 2\log 2 = 2\,\mathrm{JSD}(p_{\mathrm{data}} \| p_G) - \log 4, by definition of the Jensen–Shannon divergence (average of the KLs toward m).

Since \mathrm{JSD} \ge 0 with equality if and only if p_{\mathrm{data}} = p_G almost everywhere (each KL toward the mixture m vanishes only when the law equals m), the minimum -\log 4 is reached exactly at equilibrium.

Gaussian case: if p_{\mathrm{data}} = \mathcal{N}(\mu_d, \sigma_d^2) and G(z) = \mu + \sigma z, then p_G = \mathcal{N}(\mu, \sigma^2) and the equilibrium corresponds to the exact parameters (\mu, \sigma) = (\mu_d, \sigma_d).

The logit of D^\star, \log p_{\mathrm{data}} - \log p_G, is a degree-2 polynomial in x — a quadratic discriminator suffices. ∎

Why it is unstable nonetheless The theorem describes the equilibrium, not the dynamics: the two players descend their gradients simultaneously, and if D becomes too good too fast, the gradient of G vanishes (\log(1 - D) saturates). Emblematic pathology: mode collapse — G concentrates on the “easiest” modes of p_{\mathrm{data}} (a few very credible faces instead of all of them).

Historical remedies: Wasserstein-GAN (transportation distance instead of JSD — a preview: the distance is defined in the Optimal Transport module and Graph ML 9.6), gradient penalization, feature matching. In imaging, the family was superseded by diffusion (iterative denoising): section 9.4 gives its loss and sampler, Ye Zhu’s models (chapter 10) are recent developments of it, and the Multimodal Generative AI module treats them in detail.

LLMs, for their part, remain autoregressive: \mathbb{P}(x_t \mid x_{<t}), cross-entropy loss over tokens (LLMs module, M2).

Family Principle Sampling In the program
VAE ELBO, Gaussian latent decoder from z \sim \mathcal{N} Multimodal GenAI
GAN adversarial min-max implicit Multimodal GenAI
Diffusion iterative noising/denoising (reverse) process 9.4 (self-study); Multimodal GenAI
Autoregressive modeling \mathbb{P}(x_t \mid x_{<t}) token by token LLMs (M2), Graph GenAI

9.4. Diffusion in one section (self-study)

A VAE samples in one decoder pass but blurs; a GAN is sharp but unstable. Diffusion models (Sohl-Dickstein et al., 2015; Ho, Jain & Abbeel, 2020) take a third route: a fixed process turns data into noise in many small Gaussian steps, and a network learns to undo one step at a time, with a plain regression as its loss.

This section gives the formulas that the rest of the program uses: discrete diffusion on graphs (Graph ML 9.5), Geometric Data ch. 7–8 and Advanced DL 10.4.

Definition 9.7 — Forward (noising) process Choose a schedule 0 < \beta_1 < \dots < \beta_T < 1 (Ho et al.: linear from 10^{-4} to 0.02, with T = 1000), and set \alpha_t = 1 - \beta_t, \qquad \bar\alpha_t = \prod_{s=1}^{t} \alpha_s . The forward chain adds a little Gaussian noise at each step: q(x_t \mid x_{t-1}) = \mathcal{N}\big(\sqrt{\alpha_t}\, x_{t-1},\ \beta_t I\big). Step t = 0 is the data. At t = T, \bar\alpha_T \approx 0 and x_T is almost pure noise.

Proposition 9.8 — Closed form at any step For every t, q(x_t \mid x_0) = \mathcal{N}\big(\sqrt{\bar\alpha_t}\, x_0,\ (1 - \bar\alpha_t)\, I\big), \qquad\text{that is}\qquad x_t = \sqrt{\bar\alpha_t}\, x_0 + \sqrt{1 - \bar\alpha_t}\; \varepsilon, \quad \varepsilon \sim \mathcal{N}(0, I). Training can therefore draw any step t directly, without running the chain.

Proof. By induction on t, with \bar\alpha_0 = 1. Assume x_{t-1} = \sqrt{\bar\alpha_{t-1}}\, x_0 + \sqrt{1 - \bar\alpha_{t-1}}\, \varepsilon'. Then x_t = \sqrt{\alpha_t}\, x_{t-1} + \sqrt{\beta_t}\, \varepsilon_t = \sqrt{\bar\alpha_t}\, x_0 + \Big(\sqrt{\alpha_t(1 - \bar\alpha_{t-1})}\, \varepsilon' + \sqrt{\beta_t}\, \varepsilon_t\Big). The bracket is a sum of two independent centred Gaussians, whose variances add up to \alpha_t(1 - \bar\alpha_{t-1}) + 1 - \alpha_t = 1 - \bar\alpha_t. ∎

From the ELBO to a regression. The reverse model is a Gaussian chain started from p(x_T) = \mathcal{N}(0, I): p_\theta(x_{t-1} \mid x_t) = \mathcal{N}\big(\mu_\theta(x_t, t),\ \sigma_t^2 I\big). Treating x_{1:T} as the latent variables of a VAE, theorem 9.2 gives \log p_\theta(x_0) \ge \mathrm{ELBO}, and the negative ELBO splits into one term per step: -\mathrm{ELBO} = L_T + \sum_{t=2}^{T} L_{t-1} + L_0, \qquad L_{t-1} = \mathbb{E}_q\, \mathrm{KL}\big(q(x_{t-1} \mid x_t, x_0)\,\big\|\,p_\theta(x_{t-1} \mid x_t)\big). L_T has no parameter, since the prior is fixed, and L_0 is a decoder term.

Proposition 9.9 — The ε-prediction loss (Ho et al., 2020) By Bayes’ rule on Gaussians, the forward posterior q(x_{t-1} \mid x_t, x_0) is Gaussian with variance \tilde\beta_t I and mean \tilde\mu_t: \tilde\beta_t = \frac{1 - \bar\alpha_{t-1}}{1 - \bar\alpha_t}\,\beta_t, \qquad \tilde\mu_t = \frac{1}{\sqrt{\alpha_t}}\Big(x_t - \frac{\beta_t}{\sqrt{1 - \bar\alpha_t}}\, \varepsilon\Big), where x_0 was replaced by its expression from proposition 9.8. Give the reverse mean the same form, with a network \varepsilon_\theta(x_t, t) in place of the unknown \varepsilon. Then, up to a constant C_t free of \theta, L_{t-1} = \frac{\beta_t^2}{2\sigma_t^2\, \alpha_t\, (1 - \bar\alpha_t)}\; \mathbb{E}\,\big\|\varepsilon - \varepsilon_\theta(x_t, t)\big\|^2 + C_t .

Proof. The KL between two Gaussians with covariances \tilde\beta_t I and \sigma_t^2 I is \|\tilde\mu_t - \mu_\theta\|^2 / (2\sigma_t^2) plus a term that depends on the variances only. The two means differ only through the noise: \tilde\mu_t - \mu_\theta = \frac{\beta_t}{\sqrt{\alpha_t}\,\sqrt{1 - \bar\alpha_t}}\,\big(\varepsilon_\theta(x_t, t) - \varepsilon\big), and squaring gives the weight. ∎

Key result — the diffusion training loss \mathcal{L}_{\mathrm{simple}}(\theta) = \mathbb{E}_{t,\, x_0,\, \varepsilon}\, \Big\| \varepsilon - \varepsilon_\theta\big(\sqrt{\bar\alpha_t}\, x_0 + \sqrt{1 - \bar\alpha_t}\, \varepsilon,\ t\big) \Big\|^2, \qquad t \sim \mathcal{U}\{1, \dots, T\}.

Ho et al. drop the weight of proposition 9.9: the samples get better, at the price of a looser bound.

Algorithm 9.10 — Ancestral sampling Draw x_T \sim \mathcal{N}(0, I). For t = T, \dots, 1, draw z \sim \mathcal{N}(0, I) (and set z = 0 at the last step), then x_{t-1} = \frac{1}{\sqrt{\alpha_t}}\Big(x_t - \frac{\beta_t}{\sqrt{1 - \bar\alpha_t}}\, \varepsilon_\theta(x_t, t)\Big) + \sigma_t z, \qquad \sigma_t^2 = \beta_t \ \text{ or } \ \tilde\beta_t . Output x_0. A sample costs T network evaluations, against one for a VAE or a GAN; DDIM and distillation cut this number (Multimodal GenAI, ch. 4).

Proposition 9.11 — Predicting the noise is estimating the score Let p_t be the law of x_t. The best predictor in \mathcal{L}_{\mathrm{simple}} is the conditional mean \varepsilon^\star(x, t) = \mathbb{E}[\varepsilon \mid x_t = x], and \nabla_x \log p_t(x) = -\frac{\varepsilon^\star(x, t)}{\sqrt{1 - \bar\alpha_t}} . A trained \varepsilon_\theta is thus an estimate of the score \nabla \log p_t, which links DDPM to score-based models and Langevin sampling (Song et al., 2021).

Proof. The minimizer of a squared error is the conditional expectation. Next, p_t(x) = \int q(x \mid x_0)\, p(x_0)\, dx_0; differentiating under the integral, \nabla p_t(x) = \int q(x \mid x_0)\, \nabla_x \log q(x \mid x_0)\; p(x_0)\, dx_0 . Dividing by p_t(x) and using Bayes’ rule, \nabla \log p_t(x) is the conditional mean of \nabla_x \log q(x \mid x_0) given x_t = x. By proposition 9.8, \nabla_x \log q(x \mid x_0) = -\frac{x - \sqrt{\bar\alpha_t}\, x_0}{1 - \bar\alpha_t} = -\frac{\varepsilon}{\sqrt{1 - \bar\alpha_t}}, and taking the conditional mean ends the proof. ∎

Classifier-free guidance (Ho & Salimans, 2022). To condition on a prompt c (a class, a caption), train one network \varepsilon_\theta(x_t, t, c) and replace c by an empty token \varnothing on about 10% of the examples. At sampling time, extrapolate from the unconditional prediction toward the conditional one: \tilde\varepsilon_w = \varepsilon_\theta(x_t, \varnothing) + w\,\big(\varepsilon_\theta(x_t, c) - \varepsilon_\theta(x_t, \varnothing)\big). With w = 1 this is the plain conditional model; w > 1 over-guides, giving samples more faithful to the prompt and less diverse. By proposition 9.11, it samples with the score \nabla \log p_t(x) + w\, \nabla \log p_t(c \mid x). This is the site’s convention for the guidance weight (notation page); the original paper writes w' = w - 1.

The toy below trains \varepsilon_\theta on a mixture of eight Gaussians in the plane and samples with algorithm 9.10. It runs on a CPU in under a minute.

import math, torch, torch.nn as nn

torch.manual_seed(0)
def data(m):                                          # 8 Gaussians on a circle of radius 2
    k = torch.randint(0, 8, (m,)) * (math.pi / 4)
    return 2 * torch.stack([k.cos(), k.sin()], 1) + 0.1 * torch.randn(m, 2)

T = 100
beta = torch.linspace(1e-4, 0.2, T)                   # beta_1, ..., beta_T (index t - 1)
alpha, abar = 1 - beta, torch.cumprod(1 - beta, 0)    # abar_t = alpha_1 ... alpha_t
net = nn.Sequential(nn.Linear(2 + 16, 256), nn.SiLU(), nn.Linear(256, 256), nn.SiLU(),
                    nn.Linear(256, 2))
def eps_theta(x, t):                                  # the step t enters as sin/cos features
    f = t.float().unsqueeze(1) / T * torch.arange(1, 9) * math.pi
    return net(torch.cat([x, f.sin(), f.cos()], 1))
opt = torch.optim.Adam(net.parameters(), lr=1e-3)

for step in range(5000):                              # training: regress the noise
    x0, t = data(256), torch.randint(0, T, (256,))
    eps = torch.randn_like(x0)
    xt = abar[t, None].sqrt() * x0 + (1 - abar[t, None]).sqrt() * eps   # prop. 9.8
    loss = ((eps - eps_theta(xt, t)) ** 2).mean()     # L_simple
    opt.zero_grad(); loss.backward(); opt.step()

with torch.no_grad():                                 # ancestral sampling (algorithm 9.10)
    x = torch.randn(2000, 2)
    for t in reversed(range(T)):
        e = eps_theta(x, torch.full((2000,), t))
        x = (x - beta[t] / (1 - abar[t]).sqrt() * e) / alpha[t].sqrt()
        if t > 0:
            x = x + beta[t].sqrt() * torch.randn_like(x)   # sigma_t^2 = beta_t
ang = torch.arange(8) * (math.pi / 4)
dist = torch.cdist(x, 2 * torch.stack([ang.cos(), ang.sin()], 1)).min(1)
print(f"{(dist.values < 0.4).float().mean():.0%} of samples within 0.4 of a mode, "
      f"{dist.indices.unique().numel()} modes out of 8")   # about 90%, 8 modes

Normalizing flows: an exact likelihood A flow is an invertible network g_\theta that maps the data x to a latent z = g_\theta(x) of the same dimension, with z \sim \mathcal{N}(0, I). The change of variables gives the exact log-likelihood: \log p_\theta(x) = \log \mathcal{N}\big(g_\theta(x);\, 0, I\big) + \log \big|\det J_{g_\theta}(x)\big| .

  • Affine coupling (RealNVP: Dinh, Sohl-Dickstein & Bengio, 2017). Split x = (x_a, x_b), keep x_a, and transform x_b with scales and shifts computed from x_a: z_a = x_a, \qquad z_b = x_b \odot \exp\big(s(x_a)\big) + t(x_a).
  • Cheap determinant, explicit inverse. The Jacobian is triangular, so its log-determinant is the sum of the entries of s(x_a). The inverse is x_b = (z_b - t(z_a)) \odot \exp(-s(z_a)), and the networks s and t can be anything.
  • Trade-off. Stacking couplings that alternate the split gives a model trained by plain maximum likelihood, with no bound and no adversary. The price: the latent has the dimension of the data, and invertibility constrains the architecture.

From the VAE decoder to text-to-image. Latent diffusion (Rombach et al., 2022) runs the whole process of this section in the latent space of a pretrained autoencoder trained with a light KL penalty, as in 9.2. A 512 \times 512 \times 3 image becomes a 64 \times 64 \times 4 latent, the denoiser works there, and the decoder maps the final latent back to pixels.

The prompt enters through the cross-attention of 6.4, with keys and values computed by a frozen text encoder (in Stable Diffusion, the public release of latent diffusion, the CLIP text encoder of Radford et al., 2021), and classifier-free guidance sets how strongly it is followed. The families of this chapter thus end up in one pipeline, built in full in the Multimodal Generative AI module.

10. Research at DaSciM (Vazirgiannis, Lutzeyer, Zhu)

This chapter connects the course to the current research of three researchers of the DaSciM group: Michalis Vazirgiannis (responsible of the course on the sheet), Johannes Lutzeyer (in its teaching team) and Ye Zhu (not listed on the 2026–2027 sheet). All three topics are rooted in the previous chapters. The arXiv references are in the bibliography.

10.1. Michalis Vazirgiannis: from the “Graph of Words” to textual GNNs

Worked example — Graph of Words (Rousseau & Vazirgiannis, CIKM 2013) The bag-of-words representation throws away syntactic and order structure; the word graph restores it at almost no cost:

  • Construction: each distinct term of the document = one node; a sliding window of size w over the text creates an edge between two terms if they co-occur in the window (weighted by the co-occurrence frequency). The document becomes a small, sparse weighted graph.
  • From TF-IDF to TW-IDF: in a bag of words, the weight of a term is its frequency; in a word graph, it is its centrality. TW-IDF weights the term by its in-degree in that document’s own graph-of-words (the unweighted version worked best), with a pivoted normalization by document length, times the usual collection-level IDF. The group then used the k-core decomposition of the word graph for keyword extraction (Rousseau, Kiagias & Vazirgiannis, ECIR 2015). “Ad hoc” retrieval gains over TF-IDF: structurally important words emerge even when they are rare.
  • Positioning with respect to the course: this is the first building block of the program — the data itself becomes a graph. The NLP module reuses these representations; the Graph ML module generalizes them.

GNN lineage: texts, expressivity, generation

  • One graph per document (MPAD: Nikolentzos, Tixier & Vazirgiannis, AAAI 2020): each document is its own word graph, read by a message-passing network with attention. A new document is processed without rebuilding anything, whereas TextGCN (Yao et al., AAAI 2019) built a single transductive corpus graph. TextING (Zhang et al., ACL 2020) applies the same per-document idea to inductive text classification.
  • DGoW-GNN (Abbahaddou, Lutzeyer & Vazirgiannis, NeurIPS 2023): discriminative word graphs — the edges are no longer fixed co-occurrences but are filtered/learned for the task, then a GNN propagates the representations. The loop is closed with chapter 2: the computation graph (backprop) and the data graph (message passing) answer each other.
  • PathNN (Michel, Nikolentzos & Vazirgiannis, ICML 2023): going beyond classical message passing by aggregating paths (shortest paths, simple paths) emanating from each node; two variants are strictly more expressive than the Weisfeiler–Lehman test. This is the language of the Graph ML module, expanded in Advanced GNN (M2).

10.2. Johannes Lutzeyer: where are the parameters? Expressivity and position

Sparsifying the update step (Lutzeyer, Wu & Vazirgiannis, 2021) An MPNN (message passing neural network) chains two phases: the aggregation of neighbor messages and an “update step” — precisely the linear transformation + non-linearity of chapter 1, applied node by node. The paper’s question: what remains if one sparsifies this trainable step, initializing it directly sparse (ExpanderGNN, with a tunable sparsity rate) or even reducing it to a non-linearity alone (Activation-Only GNN)?

Key result: performance comparable to dense models with far fewer parameters — a direct empirical insight into how expressivity is distributed between aggregation (structure, not parametrized) and the update (parametrized), in the spirit of ch. 1 (what, in a network, carries the expressivity?).

Positional encodings on graphs (SHAKE-GNN, Cui, Lutzeyer et al., 2025) Chapter 6 showed that a transformer needs a positional encoding because, without positions, self-attention is permutation-equivariant: permuting the tokens only permutes the outputs, so the order is lost (invariance only comes after a symmetric pooling).

On a graph, the problem is deeper: there is no canonical ordering of the nodes — the “position” of a node is a structural notion (Laplacian spectrum, distances, spanning forests). SHAKE-GNN proposes hierarchical encodings based on Kirchhoff forests, computed in a scalable way. The parallel to remember: RoPE (relative absolute position, suited to sequences) vs spectral/structural encodings (position defined by the geometry of the graph); both answer the same fundamental symmetry (exercise 4). The Advanced GNN module (M2) systematizes this question.

10.3. Ye Zhu: steering diffusion models

From the loss to semantic control Section 9.4 gives the loss, the sampler and the guidance of diffusion models; the work of Ye Zhu shows how to steer them further:

  • BoundaryDiffusion (Zhu et al., NeurIPS 2023): control the semantics of generation without retraining (learning-free), by warping the decision boundaries of the model’s latent space in the guidance plane — an elegant point of contact with theorem 9.6: where the GAN sought an equilibrium, here already-learned latent regions are moved geometrically.
  • Discrete contrastive diffusion (ICLR 2023) and Diffusion in Diffusion (ICLR 2024): discrete cross-modal music-image diffusion and cyclic text-vision conditioning — the argument of the ch. 9 table (“multimodal”) made concrete.
  • NoiseQuery (ICCV 2025): the initial noise z_T as an implicit control signal for goal-guided generation — it reverses the perspective: the “noise” becomes a structured input, which connects to the sensitivity to the initial noise studied for GANs/diffusion.

This work directly extends ch. 9 and is expanded in the Multimodal Generative AI module.

Exercises

  1. ★ Backprop on paper. Redo example 2.2 by hand (a scalar tanh network). Then write the backward pass of a two-layer ReLU network with the recursion of theorem 2.1, and check it numerically, as in the NumPy code of section 2.1, by central finite differences \frac{\mathcal{L}(\theta + \epsilon e_j) - \mathcal{L}(\theta - \epsilon e_j)}{2\epsilon} on a small network.

  2. ★ Activation. Show that if \sigma is linear, an MLP of arbitrary depth represents exactly the affine maps. Deduce why linear cascades are useless.

  3. ★ 1D attention by hand. For n = 3 tokens, d_k = 2, compute by hand QK^\top/\sqrt{d_k}, the row-wise softmax, and the result \mathrm{softmax}(\cdot)V. Implement it, compare, and check that the rows of the attention matrix sum to 1.

  4. ★ Permutation-equivariance. Take the block of 6.4 with causal=False, in eval() mode and without positional encoding. Check experimentally that permuting the tokens permutes the output rows in the same way (equivariance), that the mean over the tokens is then invariant, and that adding a positional encoding breaks both properties. The proof is proposition 4.4 of Advanced DL.

  5. ★ Quadratic SGD. Take A = \mathrm{diag}(4, 1), b = 0, \theta_0 = (1, 1).

    Write the sequence (\theta_k) for \eta = 1/2 then \eta = 1, and verify theorem 3.1 (per-mode contraction, bound 2/L).

  6. ★ Convolution arithmetic. A 32 \times 32 input, three successive convs (5×5, stride 2, padding 2; 3×3, stride 1, padding 1; 3×3, stride 1, padding 1, dilation 2): intermediate output sizes and final receptive field (proposition 4.4).

  7. ★ LoRA. Count the trainable parameters of a GPT-like model with 12 blocks (d = 768, attention + MLP) adapted by LoRA r = 8 on the attention projections only, and compare with full fine-tuning.

  8. ★★ He initialization. Recover the factor 2 of He initialization: for h = \mathrm{ReLU}(a) with a symmetric zero-mean, compute \mathbb{E}[h^2] then the variance of the next layer.

  9. ★★ Adam bias correction. Prove proposition 3.5 for v_t (second moment), then compute the update \hat m_1/\sqrt{\hat v_1} at the first step and explain why the warmup protects.

  10. ★★ Gaussian KL. Derive the formula of proposition 9.4 in dimension 1, plot \mathrm{KL}(\mathcal{N}(\mu, \sigma^2) \| \mathcal{N}(0,1)) as a function of \mu and \sigma, and locate the minimum.

  11. ★★ Gaussian GAN. For p_{\mathrm{data}} = \mathcal{N}(0, 1) and p_G = \mathcal{N}(\mu, \sigma^2), write the logit of D^\star (difference of the log-densities) and check that it is a trinomial in x. Where does the equilibrium lie?

  12. ★★★ Project. In PyTorch: classify MNIST with (a) an MLP, (b) a small CNN, (c) a mini-transformer on patches (16 patches of 7×7, a 4 × 4 grid, as in the ViT of 6.6). Plot the train/val curves, compare the parameter counts and accuracy, comment with the bias-variance decomposition, then apply the protocol of chapter 8 (overfitting a batch, LR range test). (d) Train the CNN of (b) on the digits 0–4, freeze it, and train a linear classifier on its features for the digits 5–9 (linear probing, section 7.1); compare with the CNN trained from scratch on 5–9.

Solutions (Ex. 2, 3, 5, 6, 11) and hints (the others)

  • Ex. 1. Take \epsilon \sim 10^{-6} in float64 for the check; the error of the central finite difference is \mathcal{O}(\epsilon^2). In float32, rounding swamps such a small \epsilon (section 2.2).

  • Ex. 2. By induction: if h^{(\ell)} = \sigma(W^{(\ell)} h^{(\ell-1)} + b^{(\ell)}) with \sigma(t) = \alpha t, then h^{(\ell)} = \alpha W^{(\ell)} h^{(\ell-1)} + \alpha b^{(\ell)} remains affine in h^{(0)} = x — composing, f_\theta(x) = W x + c with W = \prod_\ell \alpha W^{(\ell)}: a single matrix, all the deep layers collapse into one.

  • Ex. 3. The row-wise softmax makes each row a vector of positive weights of sum 1; the multiplication by V makes each output a weighted average of the values: this is a convex interpolation, exactly the message passing of chapter 6.

  • Ex. 4. Permute the token dimension, x[:, perm]; x[perm] would permute whole sequences of the batch, a trivial check. Without the mask the property is exact up to rounding: the softmax over the keys is unchanged by re-indexing, and LayerNorm and the MLP act token by token. With the causal mask, token i sees only the tokens j \le i, and the check fails. Dropout must be off.

  • Ex. 5. Decoupled modes: (\theta_k)_1 = (1 - 4\eta)^k, (\theta_k)_2 = (1-\eta)^k.

    For \eta = 1/2: factors -1 (oscillation without decay: exactly \eta = 2/L, limiting case) and 1/2.

    For \eta = 1: factors -3 (divergence: \eta > 2/L) and 0 (convergence in one step: \eta = 1/\lambda_2).

  • Ex. 6. Theorem 4.2: 32 \to 16 \to 16 \to 14 (the third one: \lfloor (16 + 2 - 2\cdot 2 - 1)/1 \rfloor + 1 = 14).

    Receptive field (proposition 4.4, with dilation: replace (k-1) by d(k-1)): \mathrm{RF}_1 = 5, \mathrm{RF}_2 = 5 + 2 \cdot 2 = 9, \mathrm{RF}_3 = 9 + (2 \cdot 2)\cdot 2 = 17 pixels, for a product of strides \prod s = 2.

  • Ex. 7. Per block: 4 attention matrices d \times d, i.e. 4 \times 2 \times 8 \times 768 = 49{,}152 LoRA parameters, versus 4 \times 768^2 \approx 2.36 M for full — and the MLP is not adapted; LoRA total ~0.6 M for ~85 M full.

  • Ex. 8. By symmetry of a, \mathbb{E}[h^2] = \mathbb{E}[a^2 \mathbf{1}_{a>0}] = \tfrac12\mathbb{E}[a^2]: the variance of the next layer is halved, hence \mathrm{Var}(W) = 2/n_{\mathrm{in}}.

  • Ex. 9. Re-derive the geometric sum with g_k^2 constant; at step 1, \hat m_1 = g_1 and \hat v_1 = g_1^2.

    The ratio is \mathrm{sign}(g_1), a step of size \eta whether g_1 is tiny or huge: hence the caution of the warmup when g_1 is atypical.

  • Ex. 10. The minimum of \tfrac12(\mu^2 + \sigma^2 - 1 - \log\sigma^2) is reached at \mu = 0, \sigma = 1 (derivative with respect to \sigma^2: \tfrac12(1 - 1/\sigma^2) = 0).

  • Ex. 11. Unique global minimum at (\mu, \sigma) = (0, 1); the logit of D^\star is \frac{x^2}{2}\big(\frac{1}{\sigma^2} - 1\big) - \frac{x\mu}{\sigma^2} + \frac{\mu^2}{2\sigma^2} + \log\sigma: a trinomial in x, identically zero at the equilibrium (\mu, \sigma) = (0, 1).

Projects and assessment

The SynapseS sheet gives the grade format (numeric out of 20, with a US letter grade), but “the section for the LLGA track is empty”: no assessment form, resit rule, pass threshold or ECTS is displayed for LLGA, and no breakdown of assessments is given for any degree. The subjects, the exam and the rubric below are therefore proposals of these notes, for instance half project and half written exam. They exercise objectives O1 (ML and DL foundations), O2 (generative modelling) and O3 (large language models), with O7 (hands-on practice) throughout.

Group projects 1 and 3 are the Deep Learning building blocks of two Year 1 end-of-year projects of the Graph × LLM route: a GraphRAG question-answering system (Y1.1) and the generation of molecules from text (Y1.4).

Solo projects

  1. [Solo · ≈ 20 h] Backprop from scratch and an initialization study. Does a hand-written backward pass match autograd, and which initializations keep the signal alive through depth?
    • Implement: extend the NumPy MLP of 2.1 to any depth and to sigmoid, tanh, ReLU and GELU, and check it against PyTorch autograd and a float64 central-difference checker. Measure activation and gradient variances layer by layer in a 20-layer network for zero, Xavier and He initializations (1.3), and reproduce XOR (example 1.4).
    • Data: synthetic Gaussian inputs, XOR, and MNIST (LeCun et al., 1998).
    • Deliverables: code with tests, a 4-page report with one variance-versus-depth plot per activation, a 10-minute talk.
    • Chapters: ch. 0, 1, 2.
    • Success: a relative gradient error below 10^{-7} on every tensor. With width 256 and ReLU, He keeps the activation variance after 20 layers within a factor 2 of the input, while Xavier divides it by about 2^{20} \approx 10^6, as proposition 1.6 predicts.
  2. [Solo · ≈ 25 h] Paper study: decoupled weight decay (Loshchilov & Hutter, ICLR 2019). Is Adam with an L2 penalty really worse than AdamW, and does the best weight decay depend less on the learning rate once decoupled?
    • Warm-up: check theorem 3.1 and proposition 3.3 on quadratics with \kappa \in \{10, 100, 1000\}. The number of iterations to reach an error of 10^{-6} should grow like \kappa for gradient descent and like \sqrt\kappa for tuned momentum.
    • Implement: train a ResNet-20 (He et al., 2016) on CIFAR-10 (Krizhevsky, 2009) with Adam + L2 and with AdamW, over a 4 × 4 grid of learning rates and weight decays, with warmup and a cosine schedule (3.4). A 30-epoch run takes a few minutes on a free GPU tier.
    • Paper: I. Loshchilov, F. Hutter, Decoupled Weight Decay Regularization, ICLR 2019 (its figures 1 and 2: test error over a grid of learning rates and weight decays).
    • Deliverables: code, the two heatmaps, a 5-page report, a 10-minute talk.
    • Chapters: ch. 3, 4, 8.
    • Success: log-log slopes of 1 and 1/2 on the quadratics, within 10%; heatmaps that reproduce the paper’s finding (a basin less tilted for AdamW), or a report that explains why not.

Group projects

  1. [Group of 3 · ≈ 30 h each] Mini-GPT: tokenizer, pretraining and LoRA adaptation. What does each ingredient of a small decoder-only transformer buy, and how close does LoRA come to full fine-tuning?
    • Implement: a BPE tokenizer (definition 6.3); a pre-norm causal transformer with RoPE (6.2–6.4) of about 10 M parameters, pretrained on TinyStories (Eldan & Li, 2023); ablations of the \sqrt{d_k} scaling and of pre- versus post-norm; then adaptation to SST-2 sentiment classification (Socher et al., 2013) by linear probe, LoRA with r \in \{4, 8, 16\} and full fine-tuning (ch. 7).
    • Split: member A builds the tokenizer, the data pipeline and the perplexity evaluation; member B the model, the pretraining and the ablations; member C the adaptation and the attention maps drawn as graphs (6.3).
    • Deliverables: a repository, a 6-page report, a 15-minute talk.
    • Chapters: ch. 6, 7, 8; it leads to NLP, ch. 7 and Large Language Models, section 4.3.
    • Success: a validation perplexity far below that of a unigram model; LoRA with r = 8 within 2 points of full fine-tuning with under 2% of the weights trained, or an explanation of the gap.
  2. [Group of 3 · ≈ 30 h each] Inductive biases on images: MLP, CNN, ViT and a graph network. How much data does each architecture need, and what does each one build in? This extends exercise 12 and bridges to Graph ML.
    • Implement: on CIFAR-10, an MLP, a small ResNet (ch. 4), a ViT with 4 × 4 patches (6.6), and a GNN on SLIC superpixel graphs (Achanta et al., 2012). Train each on 1k, 5k and 50k images with the protocol of ch. 8, with tracked and seeded runs (8.4).
    • Paper: the CIFAR10 superpixel benchmark of V. P. Dwivedi et al., Benchmarking Graph Neural Networks, JMLR 2023, whose GNN results the group reproduces for one model.
    • Split: member A the MLP and the CNN; member B the ViT; member C the superpixel graphs and the GNN (Graph ML, ch. 5); shared training and logging code.
    • Deliverables: a repository, a 6-page report, a 15-minute talk. Chapters: ch. 4, 6, 8, and Graph ML ch. 5.
    • Success: a table of parameters, accuracy and data efficiency for the four models over three seeds; the reproduced GNN within the range reported by Dwivedi et al. for its size; a paragraph that explains the ranking at 1k images by the inductive biases of 4.1 and 6.6.
  3. [Group of 2 · ≈ 25 h each] VAE, GAN and diffusion: theory checks and failure modes. Do the generative families reach their theoretical optimum where it is known, and how do they fail?
    • Implement: a linear autoencoder compared with PCA (9.1); a VAE with the reparameterization trick (9.2); a GAN on the eight-Gaussian ring and on MNIST, with the Gaussian check of theorem 9.6 and exercise 11; the diffusion toy of 9.4 on the same ring.
    • Split: member A the autoencoders and the VAE (ELBO curves, posterior collapse); member B the GAN and diffusion (mode counts, samples, latent interpolations).
    • Data: synthetic rings and MNIST. Deliverables: code, a 5-page report, a 10-minute talk.
    • Chapters: ch. 9; it prepares the Multimodal Generative AI module.
    • Success: principal angles between the autoencoder subspace and the PCA subspace below 1°; the number of ring modes covered by the GAN and by diffusion over five seeds; a fitted logit of D^\star that matches the trinomial of exercise 11.

Written exam (proposal)

Format: 2 hours, one handwritten A4 sheet allowed, no electronic device; four exercises, 20 points.

  1. Question (5 points). Scaled dot-product attention. (a) Write single-head attention for n tokens of width d and give its cost. (b) If the components of a query and a key are i.i.d., centred, with variance 1, show that their dot product has variance d_k, and say what goes wrong without the factor 1/\sqrt{d_k}. (c) Without positions and without mask, show that self-attention is permutation-equivariant. How does one obtain an invariant output?

    Expected:

    • (a) The formula of definition 6.1, with time \mathcal{O}(n^2 d) and memory \mathcal{O}(n^2) (1 point).
    • (b) The proof of proposition 6.2; without the factor, the softmax saturates into a one-hot vector and its gradient vanishes (2 points).
    • (c) For a permutation matrix \Pi, the scores become \Pi QK^\top \Pi^\top, the row softmax commutes with this re-indexing, and the output becomes \Pi times the old one; a mean over the tokens is invariant (2 points).
  2. Question (6 points). Take f(w_1, w_2) = \tfrac12\big(w_2\,\mathrm{ReLU}(w_1 x) - t\big)^2, \qquad x = 2,\ w_1 = 0.5,\ w_2 = -1,\ t = 1. Then: (a) compute the forward values. (b) Compute both partial derivatives by backpropagation. (c) Take one gradient step with \eta = 0.1 and give the new loss. (d) A 3 × 3 convolution from 3 to 16 channels, stride 2, padding 1, reads a 64 × 64 image: give the output size and the number of parameters.

    Expected:

    • (a) a = w_1 x = 1, h = 1, y = -1, loss 2 (1 point).
    • (b) \partial\mathcal{L}/\partial y = y - t = -2, so \partial\mathcal{L}/\partial w_2 = -2 \cdot h = -2 and \partial\mathcal{L}/\partial w_1 = (-2)(-1)(1)(2) = 4 (2 points).
    • (c) w_1 = 0.1 and w_2 = -0.8; the new loss is \tfrac12(1.16)^2 = 0.6728 (1.5 points).
    • (d) Theorem 4.2 gives \lfloor 63/2 \rfloor + 1 = 32, so a 32 \times 32 output, with 3 \cdot 9 \cdot 16 + 16 = 448 parameters (1.5 points).
  3. Question (5 points). For a fixed generator of density p_G, (a) prove that the discriminator maximizing V(G, D) is D^\star = p_{\mathrm{data}}/(p_{\mathrm{data}} + p_G); (b) deduce V(G, D^\star) = -\log 4 + 2\,\mathrm{JSD}(p_{\mathrm{data}} \,\|\, p_G), and say for which G the minimum is reached.

    Expected: (a) pointwise maximization of a \log D + b \log(1 - D), concave, with zero derivative at D = a/(a+b) (2 points). (b) Substitute, introduce the mixture m = \tfrac12(p_{\mathrm{data}} + p_G) and recognize the two KLs toward m (2 points); the JSD is non-negative and vanishes only when p_G = p_{\mathrm{data}} (1 point).

  4. Question (4 points). Gradient descent on f(\theta) = \tfrac12 \theta^\top A \theta with A = \mathrm{diag}(10, 1). (a) For which step sizes does it converge from every start? (b) Give the best constant step and its rate. (c) About how many iterations divide the error by 10^3?

    Expected: (a) 0 < \eta < 2/L = 0.2 (1 point). (b) \eta^\star = 2/(L + \mu) = 2/11 \approx 0.18, rate \rho = (\kappa - 1)/(\kappa + 1) = 9/11 \approx 0.82, by theorem 3.1 (2 points). (c) k \ge \ln 1000 / \ln(11/9) \approx 34.4, so 35 iterations (1 point).

Grading rubric (proposal)

Criterion Weight What earns the marks
Correctness and checks 25 % gradient checks and unit tests pass; numbers agree with the results of the chapters used
Experimental protocol 20 % baselines, several seeds with error bars, the recipe of ch. 8, tracked and reproducible runs
Analysis 25 % each result is explained by a proposition or theorem of the course; failures are diagnosed, not hidden
Report and figures 15 % 4–6 pages, plots with axes, units and legends, stated limitations
Talk and questions 15 % 10–15 minutes; every member can explain every part of the work

References

Textbooks and courses

Founding papers (course chronology)

Research at DaSciM (Vazirgiannis, Lutzeyer, Zhu)