Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Three ways to condition a flow

A conditional Gaussianizer maps each slice p(x|y) to the same N(0,I); the context y can enter at the base, the couplings, or both — and which you pick depends on whether y shifts the density or reshapes it

00 — Three ways to condition a flow

Parts 4-6 each learned a single density p(x)p(x). Part 7 makes the flow’s parameters functions of a context yy, so one model represents a whole family p(x∣y)p(x\mid y) — a conditional Gaussianizer Tθ(⋅∣y)T_\theta(\cdot\mid y) that maps every conditional slice of the data to the same N(0,I)\mathcal{N}(0,I) Winkler et al. (2019):

p(x∣y)=pZ(Tθ(x;y)) ∣det⁡JTθ(⋅;y)(x)∣.p(x\mid y) = p_Z\big(T_\theta(x; y)\big)\,\big|\det J_{T_\theta(\cdot;y)}(x)\big|.

The NLL training, sampling, and log-det machinery are unchanged — only where yy enters. A flow has three slots for it:

Where the context can enter a flow (top) and how it enters one coupling (bottom):

The context yy can feed the couplings (reshaping each conditional slice) and/or the base (shifting/scaling it) — the four combinations we compare below.

A coupling already feeds the passive half xAx_A into its conditioner; making it conditional just concatenates yy onto that input (the highlighted arrow). The triangular Jacobian — and hence the free log-det — is unchanged, so conditioning is nearly free.

We fit all four base×coupling combinations on class-conditional two moons — the label y∈{0,1}y\in\{0,1\} says which crescent a point belongs to — and let the per-class samples and the conditional NLL show which slot does what.

What you will see

  • Four flows (none / base-only / coupling-only / both) under identical hyperparameters.
  • Per-class samples — does each variant put mass on the right crescent?
  • The conditional NLL ranking.
  • The decision rule: base for shifts, couplings for shape.

1. Class-conditional two moons

The same two-moons of Parts 4-5, but we keep the label make_moons returns: y=0y=0 is the upper crescent, y=1y=1 the lower. So p(x∣y)p(x\mid y) is a single crescent — two well-separated conditional densities that differ in both position and shape, which is exactly what lets the four variants separate. We one-hot the label as the context cc.

X (3000, 2), context C (3000, 2); per class: 1500 / 1500
<Figure size 540x500 with 1 Axes>

2. Four configurations

Each flow is the same chain of four RQSplineCoupling layers (rational-quadratic splines Durkan et al. (2019), 8 bins) with a fixed Permute between them so both coordinates get transformed. The only difference is which slots read cc:

flowbasecouplingscan model
noneN(0,I)\mathcal{N}(0,I)unconditionalthe marginal only — ignores yy
baseN(μ(c),σ(c)2)\mathcal{N}(\mu(c),\sigma(c)^2)unconditionalper-class shift
couplingN(0,I)\mathcal{N}(0,I)cond_dim=2per-class shape
bothN(μ(c),σ(c)2)\mathcal{N}(\mu(c),\sigma(c)^2)cond_dim=2both
     none  params:  24448
     base  params:  29056
 coupling  params:  24960
     both  params:  29568

3. Train all four — same objective, same hyperparameters

Every flow minimises the conditional NLL −E(x,c)[log⁡p(x∣c)]-\mathbb{E}_{(x,c)}[\log p(x\mid c)] via the same code path; only the layers’ cond_shapes differ. Two-moons is easy for spline couplings, so a short run suffices.

     none  stopped at 231 epochs, val NLL +1.3516
     base  stopped at  58 epochs, val NLL +0.9945
 coupling  stopped at 147 epochs, val NLL +0.6180
     both  stopped at 107 epochs, val NLL +0.6152
<Figure size 660x420 with 1 Axes>

4. Per-class samples — does mass land on the right crescent?

Conditional sampling exploits invertibility: z∼pZ(⋅∣c)z\sim p_Z(\cdot\mid c), then x=T−1(z;c)x=T^{-1}(z; c). For each class we overlay flow samples (one panel per config) on the true class data. The none flow has no label, so it samples the whole two-moons in both rows — half its mass lands in the wrong crescent.

<Figure size 1500x760 with 8 Axes>

5. Conditional log-likelihood ranking

The mean conditional log-likelihood 1N∑nlog⁡p(xn∣cn)\frac1N\sum_n \log p(x_n\mid c_n) ranks the four — higher is better.

mean conditional log-likelihood (higher is better):
       none: -1.3208
       base: -0.9986
   coupling: -0.6282
       both: -0.6290

Recap — where to put the context

if yy changes...condition the...why
only the location / scale of p(x∣y)p(x\mid y)baseone Gaussian per context, cheap
the shape of p(x∣y)p(x\mid y)couplings (cond_dim)per-context map, KK-fold capacity
a transform that can’t read yy (rotation, norm)wrap in ConditionerFiLM modulation, keeps log-det closed-form

The ranking tells the story: none ignores yy and pays for it; base-only fixes the per-class position but shares one crescent shape; coupling (and both) read the label inside the splines and produce genuinely class-specific crescents, so they win. Here the classes differ in shape, so the couplings carry the conditioning — but the principle is the headline of Part 7: decide, per slot, whether it should see yy, and the same yy threads through.

Next up. 01 — Conditional marginals & density estimation moves to a continuous context and a yy-dependent CDF, and checks that p(x∣y)p(x\mid y) is calibrated — including the Gaussianization-direction view this notebook left implicit.

References
  1. Winkler, C., Worrall, D. E., Hoogeboom, E., & Welling, M. (2019). Learning Likelihoods with Conditional Normalizing Flows. arXiv Preprint arXiv:1912.00042.
  2. Durkan, C., Bekasov, A., Murray, I., & Papamakarios, G. (2019). Neural Spline Flows. Advances in Neural Information Processing Systems (NeurIPS).