Bare NICE flow — invertible map plus the factorised latent prior.
The prior is part of the model rather than of the training loop
(matching the official NICE(encoder, prior) construction): the
flow only becomes a density once the latent space is measured against
. Three entry points cover the two directions and the
density:
encode(x) -> (h, log_det)— data to latent, with the per-sample log-determinant of the transformation.decode(h) -> x— the exact inverse, back to(B, D).log_prob(x) -> (B,)— exact log-likelihood in nats.
Data and latent are both flat (B, D) — the flow has no notion of
spatial structure, so image batches are flattened by the caller.
Parameters
configNICEConfigconfig.input_dim must be even; see
NICEConfig for the full field list.Attributes
couplingsnn.ModuleListconfig.num_coupling_layers additive coupling layers, each
followed by an index reversal.scalingnn.ModuleNotes
Reference: Dinh, Krueger, and Bengio, "NICE: Non-linear Independent Components Estimation", ICLR Workshop, 2015 (arXiv:1410.8516).
The exact objective, with additive couplings contributing zero:
The model expects dequantised data — the paper adds uniform noise of
1/256 and rescales to ( and
for CIFAR-10), and applies ZCA (SVHN, CIFAR-10) or
approximate whitening (TFD) beforehand. Feeding raw 8-bit integers
lets the flow put unbounded density on the quantisation lattice, so
the likelihood diverges.
Examples
>>> import lucid
>>> from lucid.models.generative.nice import NICEConfig, NICEModel
>>> cfg = NICEConfig(input_dim=64, num_coupling_layers=2,
... num_hidden_layers=1, hidden_dim=16)
>>> model = NICEModel(cfg).eval()
>>> x = lucid.rand((2, 64))
>>> h, log_det = model.encode(x)
>>> h.shape, log_det.shape
((2, 64), (2,))
>>> model.log_prob(x).shape
(2,)Used by 2
Constructors
1Properties
2Instance methods
4Invert the flow — latent (B, D) back to data (B, D).
Exact rather than approximate: every stage is run backwards, so
decode(encode(x)[0]) recovers x up to float round-off.
Map data to the latent space.
Parameters
(B, D).Returns
Examples
>>> import lucid
>>> import lucid.nn as nn
>>> from lucid.models.generative.nice import NICEConfig, NICEModel
>>> cfg = NICEConfig(input_dim=64, num_coupling_layers=2,
... num_hidden_layers=1, hidden_dim=16)
>>> model = NICEModel(cfg).eval()
>>> _ = nn.init.normal_(model.scaling.log_scale, std=0.1) # s = 0 at init
>>> x = lucid.rand((2, 64))
>>> h, log_det = model.encode(x)
>>> h.shape, log_det.shape
((2, 64), (2,))
>>> bool((log_det == model.scaling.log_scale.sum()).all().item())
True
>>> bool(lucid.allclose(model.decode(h), x, atol=1e-5)) # exact inverse
Trueforward(x: Tensor)Exact per-sample log-likelihood in nats.