Skip to content
AI360Xpert
Math

Monte Carlo Methods

Replace an integral you cannot solve with an average over random samples: the error falls as one over the square root of the sample count, and — uniquely — that rate does not care how many dimensions you are in.

A Monte Carlo estimate averages a function over random samples instead of integrating it, and the error shrinks as one over the square root of the sample count — so a hundred times the samples buys ten times the accuracy
A Monte Carlo estimate averages a function over random samples instead of integrating it, and the error shrinks as one over the square root of the sample count — so a hundred times the samples buys ten times the accuracy

Why Does This Exist?

Because grid-based integration dies in high dimensions and machine learning lives there.

Slice each axis into 10 pieces and a dd-dimensional integral needs 10d10^d evaluations. In 3 dimensions that is 1,000 and fine. In 100 dimensions it is 1010010^{100}, which exceeds the number of atoms in the observable universe. Every deterministic quadrature rule has this exponent, and a latent space, a parameter vector or a token distribution has hundreds to billions of dimensions.

Monte Carlo estimation has an error that scales as 1/n1/\sqrt{n} with no dd in it at all. The dimension affects how large the constant is, but not the exponent. That single property is why sampling, rather than integration, is how modern machine learning computes expectations, and it is not an approximation people settle for — it is the only thing that works.

Five things you use are Monte Carlo estimates, and it is worth recognising them as the same operation:

  • Every minibatch gradient. The true gradient is an average over the whole dataset; a batch of 32 is a Monte Carlo estimate of it. This is why gradient noise scales as 1/batch size1/\sqrt{\text{batch size}} and why quadrupling the batch halves the noise rather than quartering it.
  • Every sampled generation. Temperature sampling, top-kk, nucleus sampling — drawing a token from a distribution rather than taking the argmax.
  • The reparameterisation trick in a VAE. The expectation in the ELBO is estimated with a single sample per datapoint. One sample is a terrible estimator with enormous variance, and it works because SGD averages over batches and steps.
  • Dropout at inference time (MC dropout). Run the network 30 times with dropout left on and average. That is a Monte Carlo estimate of a predictive distribution.
  • Bootstrap resampling. Resampling your data to get an interval on a statistic is Monte Carlo applied to the empirical distribution.

Think of It Like This

Measuring a lake by throwing stones into the field around it

You need the area of an irregular lake in a rectangular field. You cannot survey the shoreline — it is too jagged and too long.

So you stand at the edge and throw stones at random into the field, uniformly, without aiming. Then you count what fraction landed in water. Multiply that fraction by the field's known area, and you have the lake's area.

Throw 100 stones and your answer is roughly right — good to about 5% of the field. Throw 10,000 and it is good to about 0.5%. Notice the shape of that improvement: a hundredfold increase in effort bought a tenfold improvement in accuracy. That is 1/n1/\sqrt{n}, and it is the central economic fact about this method.

Now the part that makes it indispensable. Suppose instead of a field you have a 100-dimensional space, and you need the volume of some region in it. Surveying is now not merely hard, it is impossible — a grid over 100 axes has more cells than there are atoms. But throwing stones works exactly as well as before. You still need the same number of throws for the same relative accuracy, because each throw is one independent look at the answer, and how many coordinates the throw has does not change that.

Two limits of the analogy matter, and each is a pitfall below:

If the lake is tiny, uniform throwing is hopeless. A pond covering 0.01% of the field means 10,000 stones land one in the water. Your estimate is 0.01% or 0% and nothing in between. This is the rare-event problem, and importance sampling is the fix: throw where the water probably is, and correct for the aiming.

You have to be able to throw uniformly. For a rectangular field, easy. For sampling from a complicated posterior, there is no direct way to throw at all, and that is what MCMC is for — a walk that wanders the space and, given enough time, visits regions in proportion to their probability.

How It Actually Works

Any integral can be written as an expectation, and any expectation can be estimated by an average. That is the entire method in two steps.

Given a distribution p(x)p(x) and a function ff:

Ep[f(X)]=f(x)p(x)dx  μ^n=1ni=1nf(xi),xip\mathbb{E}_{p}[f(X)] = \int f(x)\, p(x)\, dx \ \approx\ \hat{\mu}_n = \frac{1}{n}\sum_{i=1}^{n} f(x_i), \qquad x_i \sim p

In plain words: draw nn samples from pp, apply ff to each, average. If your integral has no pp in it, insert one — abg(x)dx=(ba)EU(a,b)[g(X)]\int_a^b g(x)\, dx = (b-a)\,\mathbb{E}_{U(a,b)}[g(X)] turns any definite integral into an expectation over the uniform distribution.

Two guarantees make this legitimate rather than merely plausible.

It is unbiased. E[μ^n]=Ep[f(X)]\mathbb{E}[\hat{\mu}_n] = \mathbb{E}_p[f(X)] for every nn, including n=1n = 1. The estimator is centred on the right answer immediately; more samples reduce its spread, not a systematic offset. This is why a one-sample ELBO estimate is not wrong, merely noisy.

Its error is quantified. By the central limit theorem, μ^n\hat{\mu}_n is approximately Gaussian around the true value with standard deviation

SE(μ^n)=σn,σ2=Varp[f(X)]\mathrm{SE}(\hat{\mu}_n) = \frac{\sigma}{\sqrt{n}}, \qquad \sigma^2 = \mathrm{Var}_p[f(X)]

In plain words: the spread of your estimate is the spread of ff divided by the square root of the sample count. You can compute this from the same samples, using their observed standard deviation — so a Monte Carlo estimate always comes with an error bar, for free, and quoting one without it is throwing away half the output.

Reading the 1/n1/\sqrt{n} law honestly

Two consequences, and they point in opposite directions.

It is slow. One extra decimal digit costs 100×100\times the samples. Three digits of precision from a plain Monte Carlo estimate is a serious computation, which is why sampling is never the answer for a low-dimensional integral where quadrature converges far faster.

It is dimension-free. Grid quadrature costs O(kd)O(k^d). Monte Carlo costs O(n)O(n) for a fixed relative error regardless of dd. There is a crossover, and it arrives early — around 4 to 6 dimensions for smooth functions. Above that, sampling is not just better, it is the only option.

The variance σ2\sigma^2 does depend on the problem, and reducing it is where all the engineering is. Three levers, in the order they are usually reached for:

  • Importance sampling. Sample from a different distribution qq that puts mass where ff is large, and reweight: Ep[f]=Eq[fp/q]\mathbb{E}_p[f] = \mathbb{E}_q[f \cdot p/q]. With a well-chosen qq the variance drops by orders of magnitude. With a badly chosen one it becomes infinite, which the second pitfall covers.
  • Antithetic and common random numbers. Pair each sample with its mirror image so their errors cancel. Cheap, and it is why fixing the seed when comparing two configurations makes small differences measurable.
  • Control variates. Subtract something correlated with ff whose expectation you already know. This is exactly what a baseline does in REINFORCE, and it is the difference between policy gradients that train and policy gradients that do not.

When you cannot sample directly

Everything above assumes you can draw from pp. Often you cannot — a Bayesian posterior is known only up to its normalising constant, so you can evaluate p~(x)p(x)\tilde{p}(x) \propto p(x) but not sample from it.

Markov chain Monte Carlo solves this with a walk. Propose a move, accept it with a probability that depends only on the ratio p~(x)/p~(x)\tilde{p}(x') / \tilde{p}(x) — in which the unknown normaliser cancels — and the chain's long-run distribution is pp. That cancellation is the whole trick, and it is why MCMC can sample a distribution nobody can normalise.

The cost is that consecutive samples are correlated, so nn draws carry less information than nn independent ones, and the chain needs time to forget where it started. In practice: discard a burn-in period, run several chains, and check convergence with R^\hat{R} and effective sample size as described in Bayesian inference. Modern samplers — Hamiltonian Monte Carlo and NUTS — use the gradient of logp~\log \tilde{p} to propose better moves, which is the same gradient your autodiff system already computes.

Worked example

Estimate 01x2dx\int_0^1 x^2\, dx, whose exact value is 13=0.3333\tfrac{1}{3} = 0.3333, by sampling. This is a case where Monte Carlo is the wrong tool — one dimension, closed form available — and that is exactly why it is the right example: you can check every number against the truth.

Write the integral as an expectation over the uniform distribution on [0,1][0,1], where the density is 1:

01x2dx=EU(0,1)[X2]\int_0^1 x^2\, dx = \mathbb{E}_{U(0,1)}[X^2]

Now the error bar, before running anything. The variance of f(X)=X2f(X) = X^2 under U(0,1)U(0,1) uses E[X4]=15\mathbb{E}[X^4] = \tfrac{1}{5} and E[X2]=13\mathbb{E}[X^2] = \tfrac{1}{3}:

σ2=E[X4](E[X2])2=1519=9545=445=0.0889\sigma^2 = \mathbb{E}[X^4] - \left(\mathbb{E}[X^2]\right)^2 = \frac{1}{5} - \frac{1}{9} = \frac{9 - 5}{45} = \frac{4}{45} = 0.0889

So σ=0.2981\sigma = 0.2981, and the standard error at each sample size follows immediately:

n=100: 0.298110=0.0298n=10,000: 0.2981100=0.00298n = 100: \ \frac{0.2981}{10} = 0.0298 \qquad n = 10{,}000: \ \frac{0.2981}{100} = 0.00298

In plain words: with 100 samples expect to be within about 0.03 of 13\tfrac{1}{3}; with 10,000, within about 0.003. A hundredfold increase in work for a tenfold improvement, predicted in advance, without running the estimator once.

Running it confirms both the estimates and the error bars, and the code below prints the observed errors alongside the predicted ones so you can see them agree.

Now the dimension-free claim, made concrete. Estimate E[X2]\mathbb{E}[\lVert X \rVert^2] for XX uniform on the unit cube in dd dimensions. Each coordinate contributes E[Xj2]=13\mathbb{E}[X_j^2] = \tfrac{1}{3}, so the answer is d/3d/3 exactly — 0.333 in 1 dimension, 33.33 in 100 dimensions. The relative standard error is what to watch: with 10,000 samples it is about 0.9% in one dimension and about 0.09% in a hundred. It got better, because summing 100 coordinates averages away their individual noise. Grid quadrature would need 1010010^{100} evaluations to attempt the same integral at all.

Show Me the Code

import numpy as np
rng = np.random.default_rng(0)TRUE, SIGMA = 1 / 3, np.sqrt(4 / 45)                 # exact answer and exact sd of x^2
for n in (100, 10_000, 1_000_000):    f = rng.uniform(size=n) ** 2                     # f(x) = x^2, shape (n,)    est, se = float(f.mean()), float(f.std(ddof=1) / np.sqrt(n))    print(n, round(est, 4), f"se={se:.4f}", f"predicted={SIGMA / np.sqrt(n):.4f}",          f"error={abs(est - TRUE):.4f}")# -> 100     0.3925 se=0.0324 predicted=0.0298 error=0.0592# -> 10000   0.3327 se=0.0030 predicted=0.0030 error=0.0007# -> 1000000 0.3336 se=0.0003 predicted=0.0003 error=0.0003
# Dimension-free: the relative error IMPROVES with d, it does not degrade.for d in (1, 100):    f = (rng.uniform(size=(10_000, d)) ** 2).sum(axis=1)          # ||x||^2, shape (10000,)    print(d, round(float(f.mean()), 3), f"true={d / 3:.3f}",          f"rel_se={f.std(ddof=1) / np.sqrt(10_000) / f.mean():.5f}")# -> 1   0.342  true=0.333  rel_se=0.00880# -> 100 33.326 true=33.333 rel_se=0.00090

The observed standard errors match the predicted ones — 0.0324 against 0.0298, 0.0030 against 0.0030 — so the error bar is trustworthy even when computed from the same samples as the estimate.

The n=100n = 100 row is worth sitting with. The estimate is 0.3925, off by 0.0592, which is about 1.8 standard errors. Nothing has gone wrong: a ±1\pm 1 standard error band covers only 68% of the time, so a 1.8-sigma miss is entirely ordinary and will happen on roughly one seed in fourteen. That is precisely why the error bar has to be reported rather than the estimate alone. Anyone who ran only this seed and quoted 0.39 as the answer would be 18% high with no way to know.

The second loop is the property the method exists for: a hundredfold increase in dimension, the same 10,000 samples, and the relative error went down tenfold rather than up.

Watch Out For

Reporting a Monte Carlo estimate without its standard error

The samples that produced the estimate also produce its uncertainty, at zero extra cost. Omitting it converts a measurement into a number that cannot be checked, and the consequences are concrete rather than stylistic.

Two evaluations get compared as if they were exact. Model A scores 0.834 on a sampled benchmark, model B scores 0.841. With 500 examples the standard error on each is around 0.017, so the 0.007 gap is a quarter of one standard error — indistinguishable from noise. Someone ships model B. This is the single most common quiet error in ML evaluation, and one extra printed number prevents it.

Sampled generation metrics get over-read. A pass@1 measured with temperature sampling over 200 problems has a standard error near 3.5 percentage points. Leaderboard gaps smaller than that are not results.

Convergence gets declared by eye. An estimate that stops moving between n=1,000n = 1{,}000 and n=2,000n = 2{,}000 has not converged; the noise floor is simply larger than the drift. The standard error says how far you actually are.

The habits:

  • Always print f.std(ddof=1) / sqrt(n) next to the mean. For a proportion, p^(1p^)/n\sqrt{\hat{p}(1-\hat{p})/n}.
  • Choose nn from the precision you need, by inverting σ/n\sigma/\sqrt{n}. If you need ±0.01\pm 0.01 on something with σ=0.3\sigma = 0.3, you need n=900n = 900, and no amount of hoping changes that.
  • Fix and record the seed. Not to make a noisy result look stable, but so that a comparison between two configurations uses the same random draws — the common-random-numbers trick, which removes shared noise from the difference.
  • Report the sample count with the metric. "0.834 ± 0.017 (n = 500)" is a result. "0.834" is a rumour.

Importance sampling with a proposal whose tails are too light

Importance sampling reweights samples from qq to estimate an expectation under pp, using weights w=p/qw = p/q. It is the standard variance-reduction tool and it has a failure mode that produces confident nonsense.

If qq has thinner tails than pp, then in the region where pp still has mass and qq has almost none, the weight p/qp/q becomes enormous. The estimator's variance can be infinite even though every individual sample looks fine, and the symptom is not an error — it is an estimate that seems stable across several runs and then jumps by an order of magnitude when one extreme sample finally appears.

The diagnostic is cheap and should be automatic: effective sample size, ESS=(wi)2/wi2\mathrm{ESS} = (\sum w_i)^2 / \sum w_i^2. Ten thousand samples with one dominant weight gives an ESS near 1. If ESS is below a few hundred, your estimate rests on a handful of points regardless of how many you drew.

Where this bites in ML:

  • Off-policy reinforcement learning. The ratio πnew/πold\pi_{\text{new}}/\pi_{\text{old}} is an importance weight, and after a few updates it can be huge. PPO's clipped objective exists specifically to bound it — that clip is not a heuristic, it is variance control on an importance sampling estimator.
  • Off-policy recommender evaluation. Estimating how a new ranker would have performed from logged data collected under an old one. Weights explode wherever the new policy likes something the old one never showed, and the standard fixes are weight clipping and doubly-robust estimators.
  • Rare-event estimation. The case importance sampling is for, and where a hand-picked proposal is most likely to be too narrow.

The rules: make qq heavier-tailed than pp, never lighter — a Student's tt proposal for a Gaussian target, not the reverse. Report ESS beside every importance-weighted number. And clip or truncate the weights when you must, accepting the bias knowingly, because a slightly biased estimator with finite variance beats an unbiased one with infinite variance every time.

The Quick Version

  • Monte Carlo replaces an integral with an average over random samples: Ep[f]1nf(xi)\mathbb{E}_p[f] \approx \frac{1}{n}\sum f(x_i).
  • The estimator is unbiased at every nn, so a one-sample estimate is noisy rather than wrong.
  • The standard error is σ/n\sigma/\sqrt{n} and is computable from the same samples. A Monte Carlo number without an error bar is incomplete.
  • 1/n1/\sqrt{n} is slow: one more decimal digit costs a hundredfold more samples.
  • 1/n1/\sqrt{n} contains no dd. That is the whole reason the method exists, and why grid quadrature is unusable above about 6 dimensions.
  • Variance reduction is where the engineering is: importance sampling, antithetic variates, control variates. A REINFORCE baseline is a control variate.
  • MCMC samples distributions known only up to a constant, because the accept ratio cancels the normaliser. The price is correlated samples and convergence diagnostics.
  • Minibatch gradients, temperature sampling, the VAE reparameterisation trick, MC dropout and the bootstrap are all this same operation.
  • Importance weights with a light-tailed proposal give infinite variance and a stable-looking wrong answer. Check effective sample size.

Related concepts