Flow-Based Generative Models · UFRJ · 2026.2
Every previous lab ended by freezing an interface. This one starts by freezing two, because the freeze is the deliverable.
unet-skeleton — U3.L2 trains it, U3.L3 conditions it, PS0 imports iteval-harness — every number this course reports, from here onThe primitive blocks are given to you complete. You write the wiring, the conditioning path, and the metrics — the parts that transfer.
unet-skeleton contract — frozent is always (B,) floats in \([0,1]\) — the FM arrow in codey optional; the label embedding is summed into \(\mathrm{emb}(t)\)Every reference implementation you will read takes an integer timestep counting down from a thousand.
Every one of them therefore invites \(\beta_t\), \(\bar\alpha_t\) and a reversed arrow into your code — one function at a time, until the codebase speaks two languages.
The assert in forward refuses the first step of that.
Any later code needing another time variable converts outside the network.
eval-harness contract — frozenThe comparability rule: an FID is a property of a model and a measurement protocol.
U0.T4 left it open: the extractor decides what “similar” means.
MNIST → MNISTFeatures: a small CNN trained on MNIST, weights pinned and committed, penultimate layer (128 units).
Inception is reserved for CIFAR and beyond.
The weights are pinned, not trained by you — every FID in this course must be comparable across students and machines, which needs one shared feature space.
The extractor uses BatchNorm. The recipe card bans BatchNorm from the U-Net.
The law is about the failure, not about the layer.
eval() runs on frozen running statistics → depends on no batch at allThe diagram is U0.T4’s, unchanged (Ronneberger, Fischer, and Brox 2015). Every box in it is something you wire today.
The factor \(1000\) is the only rescale, and it is deliberate: it makes this ladder produce the same numbers as DiT and ADM.
So a later cross-check against a library reports a real bug, not a convention difference.
The honest difficulty of the session, and it is bookkeeping.
| Level | Resolution | Channels | Attention |
|---|---|---|---|
| 0 | \(28 \times 28\) | 64 | — |
| 1 | \(14 \times 14\) | 128 | — |
| 2 | \(7 \times 7\) | 128 | yes |
| bottleneck | \(7 \times 7\) | 128 | yes |
Attention only at the bottom: cost is quadratic in positions, so \(7\times7\) costs about \(1/256\) of \(28\times28\).
The decoder gets one more residual block per level than the encoder.
Not style. The level’s last skip is the one the downsampling layer pushed, and it needs a block of its own to be consumed.
A decoder with matching block counts leaves one skip on the stack — and the failure is a shape error deep inside the last level, not anything legible.
\[ c = \mathrm{emb}(t) + \mathrm{emb}(y) \;\longrightarrow\; \text{MLP} \;\longrightarrow\; (\gamma, \beta) \]
The modulation itself is FiLM (Perez et al. 2018). What is new here is the sum: the label table has the ladder’s width, not d_emb, so the sum happens before the projection.
One projection, whatever the number of signals. Concatenating would grow the modulation path every time a signal was added, and need a wider projection in every block.
num_groups(channels), not 32. What does it give for 64, 128 and 1?The class pathway costs 640 parameters: ten vectors of width 64. That is the whole price of building it a unit early.
Two checks the session plan asked for, in this order:
Run both at initialization and they cannot both hold.
Zero-init does not make the output approximately zero. It makes it exactly zero, for every \(t\) — so ritual 1 must fail, and not because anything is broken.
The flat grey line is not dead conditioning. It is a network that is the zero function — exactly what ritual 2 demands.
A check that asserts a network does something cannot run at initialization, if the initialization is designed to make the network do nothing.
Zero-init check first, at init. Then move the parameters. Then everything else.
Second time this course has met this shape — U0.L3’s gradient check had the same defect, for the same reason.
Thirty optimizer steps against random targets. No dataset, no generative objective, result discarded.
The U-Net is not trained today. Its first real training is U3.L2, by design.
| Ritual | Asserts | Catches |
|---|---|---|
| \(t\) sensitivity | outputs differ over \(t\) | a conditioning path wired to nothing |
| zero-init | output exactly \(0\) at init | a lost zero-init |
| \(y\) sensitivity | outputs differ over \(y\) | a dead class pathway |
| gradient check | finite diff = autograd | a wrong hand-written derivative |
A conditioning path wired to nothing still trains, the loss still falls, and the model is a \(t\)-independent average.
float64 accumulation is not fastidiousness. A covariance sums products across orders of magnitude; in float32 the small terms are lost, and the matrix can fail to be positive semi-definite.
The failure then surfaces two functions away, as a complex number nobody expected.
refstats__mnist-test__mnist-cnn-v1__01-float-28x28.npz
A key without the preprocessing serves the wrong pipeline on a hit — silently, permanently.
\[ \mathrm{FID} = \lVert \mu_r - \mu_g \rVert^2 + \operatorname{Tr}\!\big( \Sigma_r + \Sigma_g - 2 (\Sigma_r \Sigma_g)^{1/2} \big) \]
sqrtm returns a complex array. The true root is real; floating point decorates it with a tiny imaginary part.
The standard fix — call .real — is wrong, and only sometimes. A large imaginary part means the input was not PSD: a real bug upstream. So: measure before truncating.
fid returns a FIDValue — it is a float, and it carries its protocol.
A docstring warning does not survive contact with a results table, where two numbers sit in one column with nothing to say how each was measured.
\[ \mathrm{BPD} = -\frac{\mathbb{E}_{x \sim q}[\log p_\theta(x)]}{d \log 2} \]
Mechanical today. The dequantization term is deliberately absent — it is added when U1 first uses this number, and the docstring says so in as many words.
panel formalizes U0.L3’s trick: a fixed noise bank, so two panels differ because the model changed and not because the noise did.
A metric is not trusted because it is implemented. It is trusted because it was checked. FID (Heusel et al. 2017) is no exception.
These three numbers go in every student’s session log.
Report at a fixed sample count — ten thousand for MNIST — or report the whole curve. Never compare across counts.
In U0.L3 the wider VAE “looked better”. Now it has a size.
The VAE decoder outputs Bernoulli probabilities. Its samples could be those, or binary draws from them.
The reference side is grayscale MNIST, and the extractor’s preprocessing is named 01-float-28x28.
Thresholding would compare a binary set against a grayscale one — a protocol mismatch, on the reference side, invisible in the resulting number.
So the harness scores the probabilities.
Tagged u0l4-freeze. From here a change to either interface is a breaking change and needs a dated CHANGELOG note.
This is not an exercise. U3.L2 imports UNet and trains it. U3.L3 switches on the class pathway. U1.L2 imports bpd. PS0 imports everything.
Extend without breaking: add keyword-only arguments with defaults, never change the positional contract — the hygiene-stack lesson, applied to a second artifact.
| Component | Built in | Role |
|---|---|---|
vae-mnist-scratch |
U0.L3 | the base codebase |
unet-skeleton |
here | replaces the encoder |
hygiene-stack |
U0.L2 | the training discipline |
eval-harness |
here | the numbers and the panels |
Graded on correctness and reproducibility — config, seed, commit, tracking database — not on sample quality.
Draft statement visible today; formally assigned at the end of U0.L5.
Numerical solvers for ordinary differential equations — the drawer the second half of this course lives in.
Every continuous-time model from U2 on is defined by a velocity field and realized by a solver. The number of function evaluations becomes a reported quantity beside the quality of what it produced.
The instrument you calibrated today is what makes that trade measurable — U3.L2 plots function evaluations against FID, on the network you assembled today.