Type at least 2 letters. Enter opens the first result, Esc closes.

    Generative Models · Lesson 3 · Models with an exact likelihood

    Autoregressive models: the chain rule, MADE, PixelCNN and transformer language models

    Intermediate 70 min 4 interactive figures 3 videos Colab notebook

    Before this lesson: lesson 01 (maximum likelihood is a forward KL; a VAE gives only a bound on log⁡p(x)\log p(x), here it is exact); cross-entropy from the information-theory track; convolutional networks and attention with masks from the deep-learning track. Time: about 70 minutes, plus the notebook.


    Why this matters #

    A VAE (lesson 02) gives only a lower bound on log⁡p(x)\log p(x). An autoregressive (AR) model gives the exact value, and it trains with nothing more than cross-entropy. This one idea powers GPT-style language models (next-token prediction), WaveNet for raw audio, PixelCNN for images, and the token models that generate the codes of a VQ-VAE. In science, most learned weather emulators are autoregressive in time: GenCast samples the next state of the atmosphere given the previous two and feeds the result back in. The cost structure of this lesson (cheap training, sequential sampling) and its failure mode (errors that build up over a long rollout) apply to all of them.

    1. Predict first #

    2. The picture #

    The chain rule with two bits #

    Take two binary variables x1,x2∈{0,1}x_1, x_2 \in \{0, 1\}. Every joint distribution of them can be written as

    p(x1,x2)=p(x1) p(x2∣x1).p(x_1, x_2) = p(x_1)\, p(x_2 \mid x_1).

    So three numbers describe it completely: p(x1=1)p(x_1 = 1), p(x2=1∣x1=0)p(x_2 = 1 \mid x_1 = 0) and p(x2=1∣x1=1)p(x_2 = 1 \mid x_1 = 1). This is the chain rule of probability. There is no assumption yet: it holds for every distribution. For DD variables it reads

    p(x)=p(x1) p(x2∣x1) p(x3∣x1,x2)⋯p(xD∣x1,…,xD−1)=∏i=1Dp(xi∣x<i),p(x) = p(x_1)\, p(x_2 \mid x_1)\, p(x_3 \mid x_1, x_2) \cdots p(x_D \mid x_1, \dots, x_{D-1}) = \prod_{i=1}^{D} p(x_i \mid x_{<i}),

    where x<i=(x1,…,xi−1)x_{<i} = (x_1, \dots, x_{i-1}) is the prefix of xix_i.

    A tree is the picture. Draw one level per variable. Each edge carries a conditional probability. The probability of a full outcome is the product of the edges on its path from the root to its leaf.

    Interactive figure: loading…

    Move the sliders to change the seven conditional probabilities of three binary variables. The bars are the eight joint probabilities (one per leaf); their sum stays exactly 1. In evaluate mode, pick an xx: its three factors light up together in yellow, because all of them can be read from the data at once. Switch to sample and press Next step: now each factor needs the result of the step before. Last, switch on the leak and watch the sum become 2.

    Two directions of use.

    • Evaluate p(x)p(x) for an observed xx. Every conditional is known, because its inputs x<ix_{<i} are part of the data. All DD factors can be computed in parallel.
    • Sample a new xx. Draw x1x_1, then x2x_2 given that x1x_1, and so on. Step ii needs the result of step i−1i - 1, so this is sequential. It is called ancestral sampling.

    Where the modeling happens. With DD binary variables, a full table of the joint needs 2D−12^D - 1 numbers: about 1030810^{308} for a tiny 32×3232 \times 32 binary image. An AR model replaces each conditional table by a neural network, for example p(xi=1∣x<i)=σ(logiti(x<i))p(x_i = 1 \mid x_{<i}) = \sigma(\text{logit}_i(x_{<i})) with σ\sigma the sigmoid. Three choices matter: the order of the variables, the family of each conditional (Bernoulli, a 256-way softmax over pixel values, a mixture of logistics, a Gaussian), and the network.

    One network for all conditionals. To get all DD conditionals in one pass, a single network produces DD outputs, and output ii must never see xi,…,xDx_i, \dots, x_D. Masks do this. MADE masks the weights of an MLP, PixelCNN masks the convolution kernels, and a transformer masks the attention matrix. Section 3 builds each one.

    Quick check

    Two bits with p(x1=1)=0.3p(x_1 = 1) = 0.3, p(x2=1∣x1=0)=0.8p(x_2 = 1 \mid x_1 = 0) = 0.8 and p(x2=1∣x1=1)=0.4p(x_2 = 1 \mid x_1 = 1) = 0.4. What is p(x1=1,x2=0)p(x_1 = 1, x_2 = 0)?

    A 2D example: two moons by counting #

    Discretize the plane into a 32×3232 \times 32 grid. Let x1x_1 be the column of a point and x2x_2 its row. The chain rule gives p(x1,x2)=p(x1) p(x2∣x1)p(x_1, x_2) = p(x_1)\,p(x_2 \mid x_1), and for discrete variables the maximum-likelihood estimate of each factor is a table of relative frequencies: count the points in each column, and in each cell of the column.

    Interactive figure: loading…

    Click a column of the two-moons picture. The top chart is p(x1)p(x_1), one bar per column; the bottom chart is the conditional p(x2∣x1)p(x_2 \mid x_1) of the selected column. Press Sample: each orange point takes two sequential steps, a column from p(x1)p(x_1), then a row from p(x2∣x1)p(x_2 \mid x_1). With smoothing α=0\alpha = 0 the model gives probability 0 to every empty cell; a larger α\alpha spreads some mass there.

    This model has 31+32⋅31=102331 + 32 \cdot 31 = 1023 free numbers: exactly as many as a full table of the 1024 cells, because with full tables the chain rule is not an approximation. That is fine for a small grid but impossible for an image. A neural AR model shares parameters across the conditionals; the counting model is the same idea without sharing.

    3. Definitions and main results #

    The autoregressive property #

    Fix an order x1,…,xDx_1, \dots, x_D. An autoregressive model is a set of conditionals qθ(xi∣x<i)q_\theta(x_i \mid x_{<i}), i=1,…,Di = 1, \dots, D, such that:

    1. for every value of x<ix_{<i}, qθ(⋅∣x<i)q_\theta(\cdot \mid x_{<i}) is a normalized distribution over xix_i;
    2. (autoregressive property) the parameters of factor ii are a function of x<ix_{<i} only.

    The model is qθ(x)=∏i=1Dqθ(xi∣x<i)q_\theta(x) = \prod_{i=1}^{D} q_\theta(x_i \mid x_{<i}).

    Theorem (an autoregressive model is normalized). For discrete x∈XDx \in \mathcal X^D, ∑xqθ(x)=1\sum_x q_\theta(x) = 1. (For continuous xx, replace the sums by integrals.)

    Why this is true. Sum out the last variable first. Only the last factor contains it, and that factor sums to 1. What is left is the same model with one variable less.

    Proof. Induction on DD. For D=1D = 1, ∑x1qθ(x1)=1\sum_{x_1} q_\theta(x_1) = 1 by condition 1.

    1. In ∑x1⋯∑xD∏i=1Dqθ(xi∣x<i)\sum_{x_1} \cdots \sum_{x_D} \prod_{i=1}^{D} q_\theta(x_i \mid x_{<i}), factor ii depends only on x1,…,xix_1, \dots, x_i (condition 2). So only the last factor contains xDx_D.
    2. Move ∑xD\sum_{x_D} next to the last factor: ∑xDqθ(xD∣x<D)=1\sum_{x_D} q_\theta(x_D \mid x_{<D}) = 1 by condition 1.
    3. What remains is the same sum for D−1D - 1 variables, which is 1 by induction. ■\blacksquare

    Why condition 2 matters. Take D=1D = 1 and let the "conditional" see x1x_1 itself: q(x1=1∣x1)=x1q(x_1 = 1 \mid x_1) = x_1. Then q(1)=1q(1) = 1 and q(0)=1−0=1q(0) = 1 - 0 = 1. The training log-likelihood is perfect (0 nats), but q(0)+q(1)=2q(0) + q(1) = 2: this is not a distribution. In the proof, step 2 fails: the last factor now depends on xDx_D also through its parameters, so its sum over xDx_D is no longer 1. A leak of xix_i into factor ii shows up as a training loss that is too good to be true.

    Quick check

    You train a MADE on binary images. After a few hundred steps the training loss is 0.000.00 nats per pixel, and samples look like noise. What is the most likely cause?

    Training: maximum likelihood with teacher forcing #

    The negative log-likelihood of one data point is a sum of DD terms:

    −log⁡qθ(x)=∑i=1D−log⁡qθ(xi∣x<i).-\log q_\theta(x) = \sum_{i=1}^{D} -\log q_\theta(x_i \mid x_{<i}).

    Each term is an ordinary cross-entropy loss: "predict xix_i from the true prefix x<ix_{<i}". Feeding the true prefix from the data, not the model's own samples, is called teacher forcing. With masks, one forward pass gives all DD terms. Averaged over the data, the loss is the cross-entropy H(pdata,qθ)=H(pdata)+KL(pdata ∥ qθ)H(p_{\text{data}}, q_\theta) = H(p_{\text{data}}) + \mathrm{KL}(p_{\text{data}} \,\|\, q_\theta), so training minimizes the forward KL, as in lesson 01. For language models, the mean loss per token ℓˉ\bar\ell in nats gives the perplexity eℓˉe^{\bar\ell}.

    Cost. Evaluating log⁡qθ(x)\log q_\theta(x) takes one parallel pass. Sampling takes DD sequential passes, because xix_i needs x<ix_{<i} first.

    Exposure bias #

    At training time, the prefix is always real data. At sampling time, it is the model's own output. A small error at step ii can move the prefix to a region the model never saw in training, where the later conditionals are worse. So errors can build up over a long sequence. This mismatch is called exposure bias.

    Two precise remarks. First, exposure bias does not bias the likelihood: log⁡qθ(x)\log q_\theta(x) is still exact. Second, a perfect model (qθ=pdataq_\theta = p_{\text{data}}) has no mismatch, because its own prefixes follow the data distribution. It is a problem of imperfect models at generation time. Fixes: scheduled sampling (sometimes feed the model's own predictions during training), sequence-level training (for example RL fine-tuning), and, for time-stepping emulators, training on multi-step rollouts. For large language models, how much it matters in practice is debated.

    MADE: masks from degrees #

    MADE (masked autoencoder for distribution estimation; Germain et al. 2015) takes an MLP with DD inputs and DD outputs (KDKD outputs for KK parameters per variable) and multiplies each weight matrix elementwise by a fixed binary mask. Every unit gets a degree:

    • input jj has degree jj;
    • hidden unit kk of layer ll gets a degree ml(k)∈{1,…,D−1}m_l(k) \in \{1, \dots, D - 1\}. It means "this unit may depend on x1,…,xml(k)x_1, \dots, x_{m_l(k)}". A simple choice is ml(k)=1+(k mod (D−1))m_l(k) = 1 + (k \bmod (D - 1)) for k=0,1,…k = 0, 1, \dots; the paper samples the degrees at random;
    • output ii (the parameters of factor ii) has degree ii.

    The masks have the shape of the weight matrix (rows = units of this layer, columns = units of the layer below). With m0(j)=jm_0(j) = j:

    hidden layers:Ml[k,j]=1[ml(k)≥ml−1(j)],\text{hidden layers:} \quad M^l[k, j] = \mathbb 1\big[m_l(k) \ge m_{l-1}(j)\big],
    output layer (strict):Mout[i,k]=1[i>mL(k)].\text{output layer (strict):} \quad M^{\text{out}}[i, k] = \mathbb 1\big[i > m_L(k)\big].

    In words: a hidden unit may read units of the same or lower degree; an output may read only units of strictly lower degree.

    Theorem (MADE has the autoregressive property). Output ii depends only on inputs j<ij < i.

    Proof.

    1. Entry (i,j)(i, j) of MoutML⋯M1M^{\text{out}} M^L \cdots M^1 counts the paths from input jj to output ii through allowed connections. If it is 0, output ii cannot depend on xjx_j, whatever the weights are.
    2. Along any path j→k1→⋯→kL→ij \to k_1 \to \dots \to k_L \to i, the hidden rule gives j≤m1(k1)≤⋯≤mL(kL)j \le m_1(k_1) \le \dots \le m_L(k_L), and the output rule gives mL(kL)<im_L(k_L) < i.
    3. So every path has j<ij < i. For j≥ij \ge i there is no path: the matrix of path counts is strictly lower triangular. ■\blacksquare

    Why the degrees are 1,…,D−11, \dots, D - 1. A unit of degree DD could only feed outputs with i>Di > D, which do not exist, so it is wasted. A unit of degree 0 has no inputs (it is a constant). Output 1 gets no connections at all: its parameters are just the biases, which is right for the unconditional factor p(x1)p(x_1).

    Interactive figure: loading…

    Click a hidden unit to change its degree, and choose an output to see all paths into it. The table of path counts (the product of the masks) must be zero on and above the diagonal; the shaded cells are the forbidden ones. Change the order: the same rule works for any order, with "degree" meaning "position in the order". Switch on the leak (output rule ≥\ge): the diagonal fills, and each output sees its own input.

    Order-agnostic training (short). The MADE paper also trains one network for many orders at once: it samples a new order and new degrees for each minibatch. At test time, an ensemble over several orders gives a better likelihood. A model trained for one order works only for that order.

    Quick check

    MADE with D=4D = 4 inputs and one hidden layer with degrees m=(1,2,3,1,2)m = (1, 2, 3, 1, 2). Is the weight from the hidden unit of degree 3 to output 3 kept by the mask?

    PixelCNN: masked convolutions #

    For images, PixelCNN (van den Oord et al. 2016) uses the raster order: row by row, left to right. Pixel (r,c)(r, c) may depend on all rows above and on the pixels to its left in row rr. A convolution is made causal by setting some kernel weights to zero. For 3×33 \times 3 kernels (1 = weight kept; the center is the middle entry):

    mask A=(111100000),mask B=(111110000).\text{mask A} = \begin{pmatrix} 1 & 1 & 1 \\ 1 & 0 & 0 \\ 0 & 0 & 0 \end{pmatrix}, \qquad \text{mask B} = \begin{pmatrix} 1 & 1 & 1 \\ 1 & 1 & 0 \\ 0 & 0 & 0 \end{pmatrix}.
    • Layer 1 uses mask A (center excluded). The feature at (r,c)(r, c) will predict input pixel (r,c)(r, c), so it must not see it.
    • Later layers use mask B (center included). The layer-(l−1)(l-1) feature at (r,c)(r, c) already depends only on pixels before (r,c)(r, c), so reading it again is safe. Mask B in layer 1 would leak x(r,c)x(r, c).

    For RGB images the papers also order the channels R, G, B inside a pixel, and masks A and B also say whether a channel may see itself. Here we use one channel.

    The blind spot. In a masked 3×33 \times 3 layer, the feature at (r,c)(r, c) reads positions (r+Δr,c+Δc)(r + \Delta r, c + \Delta c) with either Δr=−1\Delta r = -1 and Δc∈{−1,0,1}\Delta c \in \{-1, 0, 1\} (the row above), or Δr=0\Delta r = 0 and Δc≤0\Delta c \le 0 (same row, not to the right). After LL layers, input (r′,c′)(r', c') reaches output (r,c)(r, c) only as a sum of LL such offsets. Exactly r−r′r - r' of them go up, and only those can move right, by at most 1 each. So

    c′−c≤r−r′.c' - c \le r - r' .

    Pixel (r−1,c+2)(r - 1, c + 2) is never seen, although it comes before (r,c)(r, c) in raster order. The unseen region is a wedge to the upper right, and more layers do not remove it.

    Interactive figure: loading…

    Increase the number of layers LL. The blue region is everything the target pixel can see. The orange pixels come before the target in raster order and lie within LL steps, but no path reaches them: the blind spot. It grows with LL. Then switch on the two stacks: the blind spot disappears.

    The gated PixelCNN: two stacks and gated units

    The gated PixelCNN (van den Oord et al. 2016, "Conditional Image Generation with PixelCNN Decoders") removes the blind spot with two stacks of layers. A vertical stack sees all rows above the current pixel, with no masking inside those rows, so there is no wedge. A horizontal stack sees the current row to the left, and at each layer it also receives the features of the vertical stack (shifted so that they cover only rows above). The output combines both, so every earlier pixel within the reach of the layers is seen. The figure shows a simplified geometry of this design.

    The same paper replaces ReLU by a gated unit h′=tanh⁡(Wf∗h)⊙σ(Wg∗h)h' = \tanh(W_f * h) \odot \sigma(W_g * h), where ∗* is a masked convolution and σ\sigma the sigmoid. A class label or another condition yy can be added inside both terms: tanh⁡(Wf∗h+Vfy)⊙σ(Wg∗h+Vgy)\tanh(W_f * h + V_f y) \odot \sigma(W_g * h + V_g y). This is the conditional PixelCNN.

    Quick check

    A WaveNet generates raw audio at 16,000 samples per second. One forward pass takes 2 ms on your GPU, and one pass covers a whole second of audio. How long does it take to evaluate log⁡p(x)\log p(x) of one second of real audio, and to generate one second of new audio (no caching tricks)?

    Transformer language models: causal masks and the KV cache #

    A decoder-only transformer models tokens x1,…,xTx_1, \dots, x_T with the same chain rule. The input at positions 1,…,T1, \dots, T is the sequence shifted right by one, (BOS,x1,…,xT−1)(\text{BOS}, x_1, \dots, x_{T-1}), where BOS is a start token, and position ii outputs the distribution of xix_i. The causal mask sets the attention weight from position ii to every later position j>ij > i to zero (the score is set to −∞-\infty before the softmax). So position ii sees BOS,x1,…,xi−1\text{BOS}, x_1, \dots, x_{i-1}: exactly the prefix of xix_i. The three architectures put the strict inequality in different places:

    Model Non-strict part ("may see itself") Strict part
    MADE hidden masks ml(k)≥ml−1(j)m_l(k) \ge m_{l-1}(j) output mask i>mL(k)i > m_L(k)
    PixelCNN mask B in layers 2,…,L2, \dots, L mask A in layer 1
    Transformer LM causal attention j≤ij \le i in every layer input shifted right by one token

    The KV cache. Under a causal mask, the hidden state at position jj, in every layer, depends only on the inputs up to position jj. So when a new token is appended, the keys and values of all earlier positions do not change. Decoding step tt computes the query, key and value of the new position only, appends the key and value to a cache, and attends over the cache. This is exact, not an approximation. Queries are not cached, because each query is used only once, at its own step.

    Without a cache, generating T=1024T = 1024 tokens processes ∑t=1Tt=524,800\sum_{t=1}^{T} t = 524{,}800 positions through the projections and MLPs, instead of 1024, and the attention work grows like T3T^3 instead of T2T^2. The price is memory: 2LTd2LTd numbers per sequence for standard multi-head attention (LL layers, model width dd, one key and one value vector per layer and position); grouped-query attention stores fewer. A plain MADE has no such saving: it recomputes the full pass at each of its DD sampling steps.

    Quick check

    During decoding with a KV cache, what is stored for each earlier position?

    Answers to section 1. (1) The network can copy xix_i into its prediction, so the training loss goes to 0 nats. But the result is not a distribution: in the D=1D = 1 example above, the "probabilities" sum to 2. (2) log⁡p(x)\log p(x) takes one pass, 1 ms. Sampling takes 32⋅32=102432 \cdot 32 = 1024 sequential passes, about 1 s.

    4. Worked example #

    (a) Two bits. p(x1=1)=0.3p(x_1 = 1) = 0.3, p(x2=1∣x1=0)=0.8p(x_2 = 1 \mid x_1 = 0) = 0.8, p(x2=1∣x1=1)=0.4p(x_2 = 1 \mid x_1 = 1) = 0.4.

    (x1,x2)(x_1, x_2) (0, 0) (0, 1) (1, 0) (1, 1)
    p(x1) p(x2∣x1)p(x_1)\,p(x_2 \mid x_1) 0.7⋅0.2=0.140.7 \cdot 0.2 = 0.14 0.7⋅0.8=0.560.7 \cdot 0.8 = 0.56 0.3⋅0.6=0.180.3 \cdot 0.6 = 0.18 0.3⋅0.4=0.120.3 \cdot 0.4 = 0.12

    The sum is 0.14+0.56+0.18+0.12=10.14 + 0.56 + 0.18 + 0.12 = 1, as the theorem says. The log-likelihood of (0,1)(0, 1) is log⁡0.7+log⁡0.8=−0.3567−0.2231=−0.5798\log 0.7 + \log 0.8 = -0.3567 - 0.2231 = -0.5798 nats.

    The reverse order describes the same joint with three other numbers: p(x2=1)=0.56+0.12=0.68p(x_2 = 1) = 0.56 + 0.12 = 0.68, p(x1=1∣x2=1)=0.12/0.68=0.1765p(x_1 = 1 \mid x_2 = 1) = 0.12/0.68 = 0.1765 and p(x1=1∣x2=0)=0.18/0.32=0.5625p(x_1 = 1 \mid x_2 = 0) = 0.18/0.32 = 0.5625. With full tables the order does not matter. With a restricted network for each conditional, it does.

    (b) MADE masks, D=4D = 4, one hidden layer of 5 units. Degrees m(k)=1+(k mod 3)m(k) = 1 + (k \bmod 3) for k=0,…,4k = 0, \dots, 4, so m=(1,2,3,1,2)m = (1, 2, 3, 1, 2). Row kk of M1M^1 has ones in the columns j≤m(k)j \le m(k). Row ii of M2M^2 has ones where m(k)<im(k) < i:

    M1=(10001100111010001100),M2=(00000100101101111111),M2M1=(0000200042005310).M^1 = \begin{pmatrix} 1&0&0&0\\ 1&1&0&0\\ 1&1&1&0\\ 1&0&0&0\\ 1&1&0&0 \end{pmatrix}, \quad M^2 = \begin{pmatrix} 0&0&0&0&0\\ 1&0&0&1&0\\ 1&1&0&1&1\\ 1&1&1&1&1 \end{pmatrix}, \quad M^2 M^1 = \begin{pmatrix} 0&0&0&0\\ 2&0&0&0\\ 4&2&0&0\\ 5&3&1&0 \end{pmatrix}.

    Row ii of M2M1M^2 M^1 counts the paths from each input to output ii. Check one entry: output 3 reads the hidden units with degree below 3, which are units 0, 1, 3, 4 (degrees 1, 2, 1, 2). All four read x1x_1, so there are 4 paths from x1x_1; units 1 and 4 also read x2x_2, so there are 2 paths from x2x_2. Output 1 sees nothing (only its bias), output 2 sees x1x_1, output 3 sees x1,x2x_1, x_2, and output 4 sees x1,x2,x3x_1, x_2, x_3. The hidden unit of degree 3 feeds only output 4.

    (c) The blind spot, by counting offsets. Stack masks A, B, B (L=3L = 3) and look at output pixel (4,4)(4, 4), with rows and columns numbered from 0.

    • Row 3 (one step up): c′−4≤1c' - 4 \le 1, so columns up to 5.
    • Row 2 (two steps up): columns up to 6. Row 1 (three steps up): columns up to 7.
    • Row 4 itself: layer 1 (mask A) must move left, so only columns 1 to 3.

    Pixels such as (3,6)(3, 6), (2,7)(2, 7) and (1,8)(1, 8) come before (4,4)(4, 4) in raster order, but no path reaches (4,4)(4, 4) from them. (Column 0 and row 0 are unseen for a different reason: three layers reach at most three steps away.)

    5. In code #

    NumPy checks the claims of sections 3 and 4: the joint table of the two bits, the MADE mask product, the normalization theorem on a tiny causal transformer (summed over all 33=273^3 = 27 sequences), the causality that makes a KV cache exact, and the blind spot.

    import numpy as np
    from scipy.signal import convolve2d
    
    # (a) Two bits: the chain rule gives the joint table
    p1, p2 = 0.3, {0: 0.8, 1: 0.4}                 # p(x1=1) and p(x2=1 | x1)
    joint = np.array([[(1 - p1) * (1 - p2[0]), (1 - p1) * p2[0]],
                      [p1 * (1 - p2[1]),       p1 * p2[1]]])
    print(joint.round(2).tolist(), joint.sum().round(12), np.log(joint[0, 1]).round(4))
    # [[0.14, 0.56], [0.18, 0.12]] 1.0 -0.5798
    
    # (b) MADE masks from degrees (D = 4, one hidden layer of 5 units)
    D, H = 4, 5
    m_in = np.arange(1, D + 1)                     # input j has degree j
    m_h = 1 + np.arange(H) % (D - 1)               # hidden degrees [1 2 3 1 2]
    M1 = (m_h[:, None] >= m_in[None, :]).astype(int)   # hidden rule: >=
    M2 = (m_in[:, None] > m_h[None, :]).astype(int)    # output rule: strict >
    print(M2 @ M1)                                 # path counts: strictly lower triangular
    # [[0 0 0 0]
    #  [2 0 0 0]
    #  [4 2 0 0]
    #  [5 3 1 0]]
    
    # (c) A tiny causal self-attention language model sums to 1 over all V^T sequences
    rng = np.random.default_rng(0)
    V, T, BOS, d = 3, 3, 3, 8                      # vocabulary {0,1,2}, length 3, BOS id 3
    E, P = rng.normal(size=(V + 1, d)), rng.normal(size=(T, d))
    Wq, Wk, Wv, Wo = (rng.normal(size=(d, d)) for _ in range(4))
    Wout = rng.normal(size=(d, V))
    
    def log_prob(seq, causal=True):
        inp = [BOS] + list(seq[:-1])               # shift right: position i predicts token i
        h = E[inp] + P
        S = (h @ Wq) @ (h @ Wk).T / np.sqrt(d)
        if causal:
            S = np.where(np.tril(np.ones((T, T))) == 1, S, -np.inf)   # no attention to j > i
        A = np.exp(S - S.max(1, keepdims=True)); A /= A.sum(1, keepdims=True)
        out = h + (A @ (h @ Wv)) @ Wo
        logits = out @ Wout
        logp = logits - np.log(np.exp(logits).sum(1, keepdims=True))
        return logp[np.arange(T), seq].sum(), out
    
    seqs = [np.array(s) for s in np.ndindex(V, V, V)]                     # all 27 sequences
    print(round(sum(np.exp(log_prob(s)[0]) for s in seqs), 12))              # 1.0
    print(round(sum(np.exp(log_prob(s, causal=False)[0]) for s in seqs), 4)) # 0.9954: not 1
    # change the second token: only the last position, which reads it, changes
    _, a = log_prob(np.array([0, 1, 2]))
    _, b = log_prob(np.array([0, 2, 2]))
    print(np.abs(a - b).max(1).round(4))           # [0.     0.     4.3719]
    
    # (d) PixelCNN receptive field: masks A, B, B on a 9 x 9 image, output pixel (4, 4)
    A_ = np.array([[1, 1, 1], [1, 0, 0], [0, 0, 0]])
    B_ = np.array([[1, 1, 1], [1, 1, 0], [0, 0, 0]])
    g = np.zeros((9, 9)); g[4, 4] = 1               # backward pass from the output pixel
    for K in (B_, B_, A_):                          # the last layer first
        g = convolve2d(g, K, mode="same")           # adjoint of a cross-correlation
    for r in range(5):
        print("".join("o" if (r, c) == (4, 4) else "#" if g[r, c] > 0 else "." for c in range(9)))
    # .........
    # .#######.
    # .######..
    # .#####...
    # .###o....
    

    o is the output pixel and # marks the pixels it depends on. The empty wedge to the upper right of o is the blind spot. The leaky model in (c) sums to 0.9954 with this seed: a random leaky model can land on either side of 1, and a trained one goes far above it, because training rewards copying.

    6. Where it shows up #

    • Language models. GPT-style models are causal transformers trained with next-token cross-entropy, which is exactly the AR maximum-likelihood loss. Perplexity is exp⁡\exp of the mean negative log-likelihood per token. A later track on large language models will cover pretraining and inference.
    • Audio and images. WaveNet uses dilated causal 1D convolutions over raw audio samples. PixelCNN++ (a mixture of logistics per pixel) reaches 2.92 bits per dimension on CIFAR-10.
    • Two-stage generators. A VQ-VAE (lesson 02) turns an image into a grid of discrete codes. An AR prior over the codes (a PixelCNN in VQ-VAE, a transformer in VQGAN; DALL-E uses a transformer over the codes of a discrete VAE) generates new grids, and the decoder turns them into images.
    • Normalizing flows. An AR model with Gaussian conditionals, xi=μi(x<i)+σi(x<i) uix_i = \mu_i(x_{<i}) + \sigma_i(x_{<i})\,u_i with ui∼N(0,1)u_i \sim \mathcal N(0, 1), is a one-layer affine autoregressive flow. MAF stacks such layers with MADE inside (lesson 04).
    • ML for science. Weather emulators step forward in time. GenCast samples each 12-hour step from p(Xt+1∣Xt,Xt−1)p(X^{t+1} \mid X^t, X^{t-1}) with a diffusion model and is trained on single steps. GraphCast (deterministic) is fine-tuned on rollouts of 2 to 12 steps of 6 hours to limit error growth: the time-series form of exposure bias. For a long signal, such as a seismogram with 10410^4 samples, the DD sequential passes are the main reason to prefer flows or diffusion models, which update all samples in each network pass.

    7. Common mistakes #

    • Leaky masks. A non-strict MADE output mask, mask B in the first PixelCNN layer, or a transformer without the one-token shift. The training loss looks excellent, samples are garbage, and the probabilities over all states do not sum to 1. Check the mask product, or sum the model over all states of a tiny example.
    • Thinking sampling is parallel because training is. Teacher forcing parallelizes only evaluation. Sampling stays sequential; the KV cache makes each step cheaper, not the number of steps smaller.
    • Confusing exposure bias with a biased likelihood. The likelihood is exact. Exposure bias is about the distribution of the prefix at sampling time.
    • Caching queries, or caching in a bidirectional model. Only past keys and values are reused. Without a causal mask, past states change when a token is added, so a cache would be wrong.
    • Thinking more PixelCNN layers remove the blind spot. Every layer moves at most one column to the right per row up, so the wedge stays. The fix is a different architecture (two stacks).

    8. Check yourself #

    1. How many free parameters does a full probability table for D=10D = 10 binary variables have? How many does a logistic AR model, p(xi=1∣x<i)=σ(wi⊤x<i+bi)p(x_i = 1 \mid x_{<i}) = \sigma(w_i^\top x_{<i} + b_i), have?
      Answer210−1=10232^{10} - 1 = 1023. Factor ii has i−1i - 1 weights and one bias, so the AR model has ∑i=110i=55\sum_{i=1}^{10} i = 55. For D=784D = 784 (MNIST): 307,720307{,}720 against 2784−12^{784} - 1.
    2. In MADE with D=5D = 5, which hidden degrees are allowed? Why are 0 and 5 excluded?
      Answer1,2,3,41, 2, 3, 4. A degree-5 unit could only feed outputs with i>5i > 5, which do not exist. A degree-0 unit sees no input, so it is a constant.
    3. In worked example (b), which inputs does output 3 depend on, and through how many paths?
      Answerx1x_1 through 4 paths and x2x_2 through 2 paths: row 3 of M2M1M^2 M^1 is (4,2,0,0)(4, 2, 0, 0).
    4. A stack of masked 3×33 \times 3 layers (A, then B, B, ...) of any depth. Can output (r,c)(r, c) see pixel (r−2,c+3)(r - 2, c + 3)? Pixel (r−3,c+3)(r - 3, c + 3)?
      Answer(r−2,c+3)(r - 2, c + 3): never, because c′−c=3>2=r−r′c' - c = 3 > 2 = r - r' (blind spot). (r−3,c+3)(r - 3, c + 3): yes, with at least 3 layers (three up-right offsets; the row above is allowed in both masks).
    5. A transformer LM generates TT tokens. How many sequential steps does it need with and without a KV cache, and what changes?
      AnswerTT steps either way, because sampling is sequential. Without a cache, each step reprocesses the whole prefix (about T2/2T^2/2 positions in total). With a cache, each step processes one new position and attends over the tt cached keys and values.
    6. Why does a model trained with teacher forcing behave differently at sampling time? Why does a perfect model not have this problem?
      AnswerTraining conditions on true prefixes, sampling on generated ones (exposure bias). If qθ=pdataq_\theta = p_{\text{data}}, generated prefixes follow the data distribution, so there is no mismatch. The problem comes from errors of imperfect models that compound along the sequence.
    Recall cardsAnswer in your head first, then reveal. Add the cards to your review deck to see them again just before you would forget.
    1. Write the chain-rule factorization of p(x1,…,xD)p(x_1, \dots, x_D).

    2. What is the autoregressive property?

    3. Why does an autoregressive model sum to 1?

    4. What is teacher forcing?

    5. What is exposure bias?

    6. Cost of an autoregressive model with DD variables: evaluate log⁡p(x)\log p(x) vs sample?

    7. State the MADE mask rules with degrees mm.

    8. PixelCNN: which mask does layer 1 use, and why?

    9. What is the PixelCNN blind spot?

    10. Why is the KV cache exact, and what does it store?

    Open the review →

    9. Interview angle #

    • "Why can a GPT train on all positions in parallel but must generate one token at a time?" — The causal mask gives every conditional from the true prefix in one pass (teacher forcing). Generation needs x<ix_{<i} before xix_i.
    • "How does MADE enforce the autoregressive property?" — Degrees, ≥\ge in the hidden masks, a strict output mask; the mask product is strictly lower triangular.
    • "What is the PixelCNN blind spot, and how is it fixed?" — Each row up allows at most one column to the right, so a wedge to the upper right is never seen. The gated PixelCNN uses vertical and horizontal stacks.
    • "Why is the KV cache exact? Why are queries not cached?" — Causality: past keys and values never change. Each query is used once.
    • "What is exposure bias? Does it matter for LLMs?" — The train/sample prefix mismatch. Fixes: scheduled sampling, sequence-level training, multi-step rollout training. Its size for large LMs is debated.

    10. Practice #

    The notebook for this lesson has five exercises, each with a check cell and a folded solution:

    1. Fit a grid AR model of two moons by counting; check that the probabilities sum to 1; sample from it.
    2. Build MADE masks from degrees; check the dependency pattern of a random masked MLP with finite differences.
    3. Exposure bias on a Markov chain: compare the error of an imperfect model under true prefixes and under its own prefixes.
    4. Compute the receptive field of stacked PixelCNN masks and find the blind-spot pixels.
    5. Write causal self-attention in NumPy and check that output ii does not change when later inputs change.

    11. Watch and read #

    A full lecture on likelihood models: histograms, maximum likelihood, the chain rule, MADE, masked convolutions (PixelCNN and its variants) and attention-based models.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    A character-level bigram model by counting, then by a neural network: the chain rule, the negative log-likelihood and sampling, step by step.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    The first part covers PixelCNN (masked convolutions) and WaveNet (dilated causal convolutions); the last part covers VAEs, the topic of lesson 02.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play