Quant Memo
Advanced

Conditional Variational Autoencoders

A conditional VAE hands the label or context to both the encoder and decoder, so the latent code only has to capture what varies within a class instead of the class itself — letting you generate on demand rather than by luck.

Prerequisites: Variational Autoencoders, Latent Variable Models and the ELBO

A plain VAE trained on, say, digits 0–9 learns one shared latent space for everything, and if you want a sample of a specific digit you have no lever to pull — you sample a random zz and hope. The fix sounds almost too simple: tell the model the label every time, during both encoding and decoding, so the latent code is freed up to describe only what's left over — style, thickness, slant — rather than wasting capacity re-encoding "which digit is this."

The analogy: a form letter with a name field

A plain VAE is like a form letter that has to reconstruct the recipient's name from a compressed summary of the whole letter — wasteful, since the name is already known. A conditional VAE is the same form letter, but with an explicit name field: you fill in the name yourself, and the compressed summary only needs to capture the parts that actually vary between letters — tone, length, specific requests. Telling the model "this one is for Company X" up front means the latent code never has to rediscover Company X from scratch.

The mechanics: condition everything on yy

A conditional VAE (CVAE) takes the same ELBO as a standard VAE but conditions every distribution on an extra input yy (a class label, a market regime, a tenor bucket — anything you know at generation time):

ELBO(x,y)=Eq(zx,y)[logp(xz,y)]KL(q(zx,y)p(zy))\text{ELBO}(x, y) = \mathbb{E}_{q(z|x,y)}\big[\log p(x|z,y)\big] - \text{KL}\big(q(z|x,y)\,\|\,p(z|y)\big)

In words: the encoder now looks at both the data and its label to produce zz; the decoder reconstructs from both zz and the label; and the prior can itself depend on the label instead of being one fixed N(0,I)\mathcal{N}(0, I) for everything. At generation time you pick the yy you want, sample zz from its prior, and decode — no guessing required.

Worked example 1: sampling on demand

Train a CVAE on handwritten digits with y{0,,9}y \in \{0, \dots, 9\} one-hot encoded and appended to both encoder and decoder inputs. To generate a "7": set y=y = one-hot(7), sample zN(0,I)z \sim \mathcal{N}(0, I) (say z=[0.3,0.8]z = [0.3, -0.8] in a 2-D latent space), and decode p(xz=[0.3,0.8],y=7)p(x \mid z=[0.3,-0.8],\, y=7). Change only the label to y=3y=3, keep the same zz: the decoder now produces a "3" with the same stylistic wobble that [0.3,0.8][0.3,-0.8] encoded for a 7 — because zz never had to store "which digit," only "which style." A plain VAE cannot do this cleanly: its single zz tangles digit identity and style together, so swapping identity while holding style fixed isn't a defined operation.

Worked example 2: conditioning on a market regime

Suppose xx is a day's 1-minute return path and y{calm,stressed}y \in \{\text{calm}, \text{stressed}\} is a realized-vol regime flag known historically. A CVAE trained this way learns p(xz,y=stressed)p(x \mid z, y=\text{stressed}) separately from p(xz,y=calm)p(x \mid z, y=\text{calm}) within one shared model. To stress-test a strategy, fix y=stressedy = \text{stressed} and sample many zz's: each decode is a different stressed-regime path, all sharing the property "this came from the stressed prior," letting a risk team generate a batch of plausible stress scenarios on demand rather than being stuck with the handful of stressed days actually observed in history.

x y encoder z y also feeds the decoder directly
Label $y$ enters both the encoder and the decoder, so $z$ is freed from having to encode $y$ itself — it only needs to capture what varies once $y$ is already known.

Distribution · normal
-2.000.002.00μvalue →
Within ±1σ 68.3%mean μ 0.00std σ 1.00

Drag the mean and spread above and picture it as one class-conditional prior p(zy)p(z \mid y): a CVAE can give each label its own such prior rather than forcing every class through one identical N(0,I)\mathcal{N}(0, I).

A conditional VAE conditions encoder, decoder, and optionally the prior on a known label yy, so the latent code only has to represent what varies within a class — freeing you to generate a specific, chosen category on demand instead of sampling blindly and hoping.

What this means in practice

CVAEs are the natural tool whenever generation needs to be steerable — synthetic scenario generation conditioned on a regime label, synthetic order-book snippets conditioned on a venue or time-of-day bucket, or portfolio return paths conditioned on a macro state. The label doesn't have to be a clean one-hot class either — it can be a continuous or structured context vector, as long as it's known at generation time.

The common mistake is conditioning only the decoder and not the encoder (or vice versa) — if the encoder never sees yy, it still has to infer the label from xx implicitly, dragging label information back into zz and defeating the point. Both directions of the model need the same yy, and the prior p(zy)p(z\mid y) should match whatever the encoder can actually produce for that yy, or the KL term will punish the encoder for a mismatch it didn't cause.

Related concepts

Practice in interviews

Further reading

  • Sohn, Lee & Yan, Learning Structured Output Representation using Deep Conditional Generative Models (2015)
ShareTwitterLinkedIn