Change of Variables
Transform a random variable and its density does not simply move with it — it gets rescaled by how much the transform stretched or squeezed space, and that scaling factor is a Jacobian determinant.
Why Does This Exist?
Because three techniques that look like separate tricks are the same piece of calculus, and none of them makes sense without it.
Normalising flows. A flow builds a complicated distribution by pushing a simple Gaussian through a stack of invertible functions. The log-likelihood it reports is the Gaussian's log-density plus a log-determinant term per layer. Where that extra term comes from, why it must be there, and why every flow architecture is designed around making it cheap to compute — all of it is this page.
The reparameterisation trick. A VAE samples with instead of sampling directly. That is a change of variables, deliberately chosen so that the randomness sits in a variable with no parameters in it, which is what lets a gradient pass through a sampling step at all.
Inverse transform sampling. How every library turns uniform random numbers into exponential, Gaussian or Cauchy draws. rng.exponential() is a uniform sample pushed through , and this page is why the result has exactly the right density.
There is also a correctness issue that bites outside generative modelling. A density is not invariant under a change of units or parameterisation. Rescale your data and every log-likelihood you report shifts, by an amount this page computes exactly. That is why bits-per-dimension figures are only comparable under identical preprocessing, and why a MAP estimate of disagrees with a MAP estimate of .
Think of It Like This
Rolling out dough
You have a lump of dough on a table, thicker in the middle and thinner at the edges. Roll it out with a pin, stretching it to twice its length.
The dough is now twice as long, so it is half as thick everywhere. No dough was added or removed — the amount is conserved — so stretching in one direction has to thin it in the other.
Now roll unevenly. Press hard in the middle and barely at all near the edges. The middle stretches a lot and thins a lot; the edges barely change. The thinning factor is different at every point, and it is set by how much the rolling stretched space right there.
That is the whole content of this page. A probability density is the dough's thickness. The total amount of dough — the total probability — is fixed at 1. Transform the variable and you stretch the table; the density must thin or thicken by exactly the reciprocal of the local stretch factor to keep the total at 1.
Two extensions, and both matter:
In more than one dimension you care about area, not length. Stretch a sheet of dough to twice its width and twice its height and it becomes four times thinner, not twice. The relevant quantity is how much the transform multiplies volume, and that number is a determinant. This is why a Jacobian determinant appears rather than a plain derivative.
Folding is not allowed. If the transform maps two different inputs to the same output, the dough overlaps and thickness at that point is the sum of two layers, which the simple formula cannot express. This is why every transform in a normalising flow must be invertible, and why that constraint is architectural rather than a nicety.
How It Actually Works
Let have density , let for an invertible, differentiable , and write . Then
In plain words: to find the density at , look up the density at the that maps to it, then correct for how much the map stretched the neighbourhood. The absolute value is there because a decreasing transform flips orientation, and a density cannot be negative.
The derivation is one line, and it is worth seeing because it explains why the correction is a reciprocal. Probability in a small interval is conserved:
In plain words: the same probability mass now occupies an interval of a different width, so the height has to compensate. Stretch by a factor of 2 and .
In several dimensions the derivative becomes a determinant
For with both in , the local stretch is described by the Jacobian matrix , whose entry is . The factor by which it scales volume is :
In plain words: divide by how much the forward transform expanded volume. The two forms are equivalent because the Jacobian of an inverse is the inverse of the Jacobian, so their determinants are reciprocals.
In practice you always work with logs, because a product of determinants across flow layers underflows and because losses are log-densities anyway:
This single line is the entire likelihood of a normalising flow. Push the data back through the inverse transforms to a Gaussian, evaluate the Gaussian's log-density, subtract a log-determinant per layer. Nothing else.
Why flow architectures look the way they do
A general determinant costs , which is unusable when is the dimension of an image. Every flow design is a way of making the determinant cheap, and once you know that, the architectures stop looking arbitrary:
- Triangular Jacobians. If each output depends only on inputs at or before its own index, the Jacobian is triangular and its determinant is the product of the diagonal — . This is exactly what autoregressive flows (MAF, IAF) enforce.
- Coupling layers. Split the input in half, pass one half through unchanged, and use it to compute a scale and shift for the other half. The Jacobian is block-triangular, the determinant is the product of the scales, and the transform is trivially invertible. This is RealNVP and Glow, and it is the design most flows still use.
- Permutations between layers. Necessary because a coupling layer leaves half its input untouched, and a permutation is free: its determinant is , so the log-determinant is 0.
- Residual and continuous flows. Neural ODEs replace the determinant with a trace, which can be estimated cheaply. Different route to the same problem.
Two special cases you use constantly
Inverse transform sampling. Take and set for any CDF . Then has density . The change-of-variables factor is by the CDF–PDF relationship, and multiplying by the uniform density of 1 gives back exactly . This is how every non-Gaussian sampler in every library is built.
Affine transforms. For , the Jacobian is the constant , so . Every standardisation step you have ever applied does this, and the is why the log-likelihood of your model changes when you rescale your features.
Worked example
Start with the simplest case, where the answer is checkable without any calculus. Let , so on . Let .
lives on . Its inverse is , so , giving
In plain words: doubling the variable halved the density. Check the area: width 2 times height 0.5 is 1. The distribution did not become "less likely"; it spread over twice the range.
Now a non-linear transform, and the one that generates exponential samples. Let again and set . Inverting, , and
That is the exponential density with rate , exactly. A uniform generator plus a logarithm produces exponential samples, and the change-of-variables factor is the density — nothing was fitted or approximated.
Now two dimensions, where the determinant earns its place. Take uniform on the unit square, so on an area of 1. Apply the linear map
This is the same shear used in linear transformations: the unit square becomes a parallelogram of area 2. So
In plain words: the region doubled in area, so the density halved. Area 2 times density 0.5 is 1, as it must be. The log-determinant term is , and this is precisely the number a flow layer would subtract.
Finally, the case that catches people out in reporting. Suppose a model assigns a log-density of to a data point measured in metres, and you switch to centimetres, so per dimension. In dimensions the log-density changes by :
For that is instead of . The model is identical, the data is identical, and the reported number moved by 13.82 nats purely because of a unit change. Anyone comparing log-likelihoods or bits-per-dimension across differently preprocessed pipelines is comparing preprocessing.
Show Me the Code
import numpy as np
rng = np.random.default_rng(0)u = rng.uniform(size=200_000) # X ~ Uniform(0,1), shape (200000,)
y = 2 * u # doubling halves the densityprint(round(float(np.histogram(y, bins=20, range=(0, 2), density=True)[0].mean()), 3))
lam = 1.5 # inverse transform samplingexp_samples = -np.log(u) / lam # should be Exponential(1.5)print(round(float(exp_samples.mean()), 4), round(1 / lam, 4)) # -> 0.6676 0.6667
A = np.array([[2.0, 1.0], [0.0, 1.0]]) # (2, 2) shearprint(float(np.linalg.det(A)), round(float(np.log(2.0)), 4)) # -> 2.0 0.6931
box = rng.uniform(size=(400_000, 2)) * [3.0, 1.0] # bounding box of area 3pre = box @ np.linalg.inv(A).T # map each point back to x-spaceinside = float(np.mean((pre >= 0).all(1) & (pre <= 1).all(1)))print(round(inside * 3, 3)) # -> 1.996 measured area == |det A|print(round(-2.30 - 3 * np.log(100), 2)) # -> -16.12 metres to centimetresThe exponential mean lands on to three decimals — 0.6676 against 0.6667, within its own Monte Carlo error — so the transform is producing the exact density rather than an approximation. The doubled uniform averages 0.5 exactly. The area measurement recovers without ever calling det, which is what "the determinant is the volume scale factor" means operationally. And the last line is the reporting hazard as a number: same model, same data, 13.82 nats apart.
Watch Out For
Comparing log-likelihoods or bits-per-dimension across different preprocessing
A density has units of "probability per unit volume", so its value depends on what a unit is. Change the units and every log-density shifts by the log-determinant of the rescaling — a constant that has nothing to do with model quality.
The concrete cases in generative modelling:
- Pixel scaling. A model trained on pixels and one trained on differ in log-likelihood by nats. For a image that is 17,035 nats, which utterly dominates any real difference between the models.
- Dequantisation. Image data is discrete, and a continuous density model assigns infinite likelihood to a discrete point mass unless noise is added. How much noise, and whether it is uniform or variational, changes the reported number. Published bits-per-dimension figures are only comparable when the dequantisation matches.
- Any invertible preprocessing at all. A logit transform, a per-channel standardisation, a whitening matrix. Each contributes a log-determinant. Some papers report it, some fold it in, some omit it.
Bits-per-dimension exists to make numbers comparable across image sizes — it divides by and converts to base 2 — and it does not fix the units problem. The convention it assumes is pixel values with uniform dequantisation, and a number computed under any other convention is not on the same scale despite carrying the same name.
What to do:
- State the preprocessing whenever you report a likelihood. Data range, dequantisation scheme, and any transform's log-determinant.
- Only compare models you evaluated yourself, under one pipeline. Reproduce a baseline rather than quoting its number.
- Remember a continuous log-likelihood can be positive, since a density above 1 has a positive log. That is not a bug and not a probability above 1.
- For ranking or optimisation, none of this matters — a constant offset has no gradient. It matters the moment you compare across pipelines.
Assuming a transform is invertible when it is not, or when its Jacobian is numerically singular
The formula requires a bijection with a non-vanishing determinant. Break either condition and you get a silently wrong likelihood rather than an error.
Non-invertible transforms. maps both and to 4, so the density at 4 is the sum of two contributions and the single-branch formula undercounts by half. A ReLU is not invertible at all — it discards the sign of everything negative — which is why no flow uses one. tanh is invertible in principle and saturates in practice: at its derivative is under , so the log-determinant term becomes a large negative number driven by floating-point noise rather than by the data.
Determinants that vanish or explode. In a coupling layer the log-determinant is , the sum of the log-scales. Nothing constrains those scales, and if the network outputs a scale near zero the transform is locally collapsing dimensions — the inverse is numerically undefined and the log-determinant runs to . The likelihood improves as this happens, so the optimiser drives straight toward it. The training curve looks excellent right up to the nan.
The standard defences, all of which appear in production flow implementations:
- Parameterise the log-scale, not the scale, and pass it through a bounded function.
s = tanh(raw) * limitors = log_sigmoid(raw) + ckeeps the scale in a safe band by construction. - Constrain the transform to be monotone by construction. Spline flows enforce monotonicity per bin, which guarantees invertibility rather than hoping for it.
- Assert the round trip in a test.
assert np.allclose(inverse(forward(x)), x)catches a broken invertibility claim immediately, and it is a one-line test that most flow bugs fail. - Log the mean log-determinant per layer during training. A layer drifting toward large negative values is the early warning, visible long before the loss goes non-finite.
- Check the determinant's sign, not just its magnitude. A sign flip mid-training means the transform passed through a singular point, and everything after it is suspect.
The Quick Version
- Transform a random variable and its density is rescaled by the reciprocal of the local stretch factor, because total probability is conserved.
- One dimension: . Several dimensions: divide by , because volume rather than length is what scales.
- In log form, . That single line is the entire likelihood of a normalising flow.
- Flow architectures exist to make the determinant cheap: triangular Jacobians cost , coupling layers give a product of scales, permutations contribute zero.
- Inverse transform sampling is this formula. So is every affine standardisation you apply.
- Densities are not invariant under reparameterisation. Rescaling pixels from to shifts a log-likelihood by nats with no change to the model.
- Bits-per-dimension does not fix that. Only compare likelihoods under one preprocessing pipeline, ideally one you ran yourself.
- The transform must be invertible with a non-vanishing determinant.
ReLUis out; saturatingtanhand unbounded coupling scales are numerical traps. - Test the round trip and log the per-layer log-determinant. Both failures show up there before they show up as
nan.
What to Read Next
- Integrals in Probability is where densities, normalising constants and the non-invariance warning come from.
- Jacobian and Hessian is the matrix whose determinant does the work here.
- Integration and Area Under a Curve supplies the conservation-of-area argument the derivation rests on.
- Linear Transformations is where the shear matrix with determinant 2 comes from, and what a determinant means geometrically.
- Probability Distributions is what gets transformed.
- Definitions worth a look: Jacobian Matrix, Normal Distribution, Random Variable, and Logarithm.
- Related interview question: What does a tensor's shape tell you?