Mathematics for Machine Learning

Distributions — Naming the Common Shapes of Uncertainty


You have 50,000 recorded checkout times from your web shop. Someone asks how many customers take longer than twelve seconds. Easy — count the rows, divide by 50,000, done. Then they ask a second question: what fraction take longer than two minutes?

You look. Your longest observed checkout is 103 seconds. So your data says the answer is zero, which is obviously wrong — you just have not seen one yet. And your histogram is no help, because the far right of it is built from four or five lonely observations that could each have gone a different way. The bit of the distribution you care about most is the bit your data describes worst.

There is a second problem. To answer any question at all you had to keep all 50,000 numbers. To answer it on a phone, or inside a training loop that runs a million times, that is not viable. And you cannot do algebra with a histogram: there is no way to say "if I add an independent delay with these properties, what does the total look like?"

The fix is to stop describing the data point by point and instead say which shape it has. Checkout times are roughly exponential with a rate of about 0.1 per second. That is one number. From it you get any probability you like, including for the two-minute case you never observed, plus closed-form answers about sums and averages. The cost is that if you pick the wrong shape, everything downstream is confidently wrong. So the skill is knowing the shapes, and knowing how to tell when one does not fit.

Pick the family by the question, not the histogramWhat isbeing generated?Sum of many effects: NormalSuccesses out ofn tries: BinomialCounts in a window: PoissonWaiting time untilan event: ExponentialA probability itself: Beta
Naming a family lets you answer about the two-minute tail you never observed, which counting rows cannot.

What a named family buys you, and what it costs

Raw empirical dataA named distribution
StorageEvery observation, foreverOne or two parameters
Beyond the observed rangeSays probability zero, wronglyExtrapolates smoothly into the tail
AlgebraNone availableClosed forms for sums, means, tails
Tail estimatesBuilt from a handful of points, very noisyBorrows strength from the whole sample
RiskHonest about what it sawConfidently wrong if the family is wrong

That last row is not a footnote. Assume request latency is normal with mean 200 ms and standard deviation 30 ms and the formula hands you a 99th percentile of 200+2.326×30≈270200 + 2.326 \times 30 \approx 270 ms. Real latency is right-skewed with a long tail, and the true p99 might be 900 ms. Your alerting thresholds, your capacity plan and your service-level objective are all built on a number that is off by a factor of three — not because the maths was wrong, but because the family was.

A named distribution replaces thousands of data points with two numbers. That is a bargain when the shape is right and a disaster when it is not, so always check the fit before you trust the tail.

The Normal distribution, and why it is everywhere

f(x)=1σ2π e−(x−μ)2/(2σ2)f(x) = \frac{1}{\sigma\sqrt{2\pi}} \, e^{-(x-\mu)^2 / (2\sigma^2)}

That looks forbidding until you read it in pieces, from the inside out:

  • (x−μ)(x - \mu) is the distance from the centre. Only distance appears, never xx on its own, which is why the curve is perfectly symmetric about μ\mu.
  • Squaring it makes left and right equivalent and punishes far-away points much harder than nearby ones.
  • Dividing by 2σ22\sigma^2 measures that squared distance in units of variance. A large σ\sigma makes the exponent grow slowly, so the curve is wide and flat.
  • The minus sign inside e ⋅ e^{\,\cdot\,} turns "far from the centre" into "very little density", and the exponential makes that fall-off ferociously fast.
  • 1σ2π\frac{1}{\sigma\sqrt{2\pi}} does no conceptual work at all. It is the number that makes the total area come out to exactly 1.

Two parameters, μ\mu and σ\sigma, and they mean exactly what you want them to mean: where the bump sits and how wide it is.

The 68-95-99.7 rule

For any normal distribution, roughly 68% of the mass lies within one standard deviation of the mean, 95% within two, and 99.7% within three. With latency μ=200\mu = 200 ms, σ=30\sigma = 30 ms: 68% of requests land between 170 and 230 ms, 95% between 140 and 260 ms, and 99.7% between 110 and 290 ms. Anything outside that last range happens about three times in a thousand — half of that on each side, so a request slower than 290 ms is roughly a one-in-750 event.

This is the fastest sanity check in statistics. If a "normal" dataset has 5% of its points beyond three standard deviations, it is not normal — it has heavy tails, and every tail probability you compute from a normal fit will be far too small.

Z-scores

z=x−μσz = \frac{x - \mu}{\sigma}

The z-score restates a value as "how many standard deviations from the mean". A 275 ms request gives z=(275−200)/30=2.5z = (275 - 200)/30 = 2.5. That single number is comparable across any two quantities measured in any units, which is exactly why standardising features before training is standard practice — it puts every column on the same footing. And because every normal turns into the same standard normal after this transformation, one table of probabilities serves all of them: P(Z≤2.5)=0.9938P(Z \le 2.5) = 0.9938, so about 0.62% of requests are slower than 275 ms.

The Central Limit Theorem is why the normal keeps appearing

Take X1,X2,…,XnX_1, X_2, \dots, X_n drawn independently from any distribution with a finite mean μ\mu and a finite variance σ2\sigma^2. As nn grows, the distribution of their average Xˉn\bar{X}_n approaches a normal distribution with mean μ\mu and variance σ2/n\sigma^2/n.

Read the conditions carefully, because they carry the content. The source distribution can be wildly skewed, discrete, bimodal — it does not matter. What must hold is independence and finite variance. Distributions with infinite variance do not obey it, and heavy-tailed real-world quantities behave badly for exactly this reason.

This is why so many measured quantities look normal: they are sums of many small independent contributions. It is also why the average of a sample is normal even when the sample is not, which is the foundation every confidence interval and t-test in the world stands on. Note the σ2/n\sigma^2/n: quadruple your sample size and you halve the standard error, which is the arithmetic behind "you need four times the data to halve the error bar".

The Central Limit Theorem is a statement about averages, not about data. Your raw measurements need not be normal for the mean of them to be.

Counting things: Binomial and Poisson

Binomial: successes out of a fixed number of tries

P(X=k)=(nk)pk(1−p)n−k,E[X]=np,Var(X)=np(1−p)P(X = k) = \binom{n}{k} p^k (1-p)^{n-k}, \qquad E[X] = np, \qquad \mathrm{Var}(X) = np(1-p)

Each of the three factors does one job. pkp^k is the probability of the kk successes, (1−p)n−k(1-p)^{n-k} the probability of the failures, and (nk)\binom{n}{k} counts how many different orderings produce that same total. Use it when you have a fixed number of independent yes/no trials, each with the same probability.

Twenty visitors land on a checkout page and each converts with probability 0.15. Expected conversions: np=20×0.15=3np = 20 \times 0.15 = 3. Variance: np(1−p)=20×0.15×0.85=2.55np(1-p) = 20 \times 0.15 \times 0.85 = 2.55, so a standard deviation of about 1.60. The probability of seeing exactly five:

P(X=5)=(205)(0.15)5(0.85)15=15504×0.0000759×0.0874≈0.103P(X = 5) = \binom{20}{5}(0.15)^5(0.85)^{15} = 15504 \times 0.0000759 \times 0.0874 \approx 0.103

Roughly a 10% chance. Note what that means for your dashboard: five conversions instead of the expected three is completely unremarkable, and treating it as a win would be reading noise.

Poisson: counts in a window, with no ceiling

P(X=k)=λke−λk!,E[X]=Var(X)=λP(X = k) = \frac{\lambda^k e^{-\lambda}}{k!}, \qquad E[X] = \mathrm{Var}(X) = \lambda

Use it when events happen independently at a steady average rate and there is no natural maximum. Errors per hour, arrivals per minute, typos per page. The single parameter λ\lambda is the average count per window, and remarkably it is also the variance.

Your service averages three errors an hour, so λ=3\lambda = 3. The chance of a clean hour is P(X=0)=e−3=0.0498P(X = 0) = e^{-3} = 0.0498 — about one hour in twenty. The chance of exactly five errors:

P(X=5)=35e−35!=243×0.0498120≈0.101P(X = 5) = \frac{3^5 e^{-3}}{5!} = \frac{243 \times 0.0498}{120} \approx 0.101

Put that next to the binomial answer above: 0.101 against 0.103. That is not a coincidence. When nn is large and pp is small, the binomial converges to a Poisson with λ=np\lambda = np. Here np=3np = 3 in both cases. The Poisson is the right choice when you cannot even define nn — you do not know how many people could have hit your server this hour, only how many did.

The mean-equals-variance property is also a diagnostic. If your observed counts have a variance much larger than their mean, the data is overdispersed and a Poisson model will understate the risk of a bad hour. That usually means the rate is not constant, and you need a mixture or a negative binomial instead.

Waiting for things: the Exponential

f(x)=λe−λx(x≥0),P(X>t)=e−λt,E[X]=1λf(x) = \lambda e^{-\lambda x} \quad (x \ge 0), \qquad P(X > t) = e^{-\lambda t}, \qquad E[X] = \frac{1}{\lambda}

Poisson counts events per window; the exponential describes the gap between events. Same underlying process, opposite question. With errors arriving at λ=3\lambda = 3 per hour, the average gap is 1/31/3 hour, or 20 minutes. The probability of going a whole hour without one is e−3×1=0.0498e^{-3 \times 1} = 0.0498 — the same 5%, arrived at from the other direction.

Memorylessness, plainly

The exponential has a property that feels wrong the first time:

P(X>s+t∣X>s)=P(X>t)P(X > s + t \mid X > s) = P(X > t)

In words: having already waited tells you nothing about how much longer you will wait. At the start, the chance of waiting more than 20 minutes for the next error is e−3×(1/3)=e−1=0.368e^{-3 \times (1/3)} = e^{-1} = 0.368. After 40 quiet minutes, the chance of waiting another 20 is still 0.368. The process does not accumulate pressure and it is not "due".

This is the right model for genuinely random arrivals — cosmic rays, independent user requests, radioactive decay. It is the wrong model for anything that ages. A hard drive that has run for five years is more likely to fail this month than a new one; a bus that is ten minutes late is more likely to arrive in the next minute. Using an exponential for those understates the risk exactly when it matters.

Uniform and Beta: distributions over a bounded range

Uniform

Continuous uniform on [a,b][a, b] has f(x)=1/(b−a)f(x) = 1/(b-a), mean (a+b)/2(a+b)/2 and variance (b−a)2/12(b-a)^2/12. Discrete uniform on {1,…,n}\{1, \dots, n\} gives every value probability 1/n1/n. Its flatness is its meaning: every value in range is equally plausible, so it is the natural "I know only the bounds" assumption.

You meet it constantly in ML plumbing. Glorot and He weight initialisation draw from a uniform whose width is set by the layer size. Random hyperparameter search samples uniformly over a range (or log-uniformly, when the sensible range spans orders of magnitude). And every other sampler is built on it: draw u∼U(0,1)u \sim U(0,1), apply the inverse CDF F−1(u)F^{-1}(u), and out comes a sample from any distribution you like.

Beta: a distribution over a probability

The Beta lives entirely on [0,1][0, 1], which makes it the natural way to express uncertainty about a probability itself. Beta(α,β)(\alpha, \beta) has mean α/(α+β)\alpha/(\alpha+\beta), and Beta(1,1)(1,1) is flat — total ignorance.

Its magic property is conjugacy with the binomial: if your belief before seeing data is Beta(α,β)(\alpha, \beta) and you then observe ss successes and ff failures, your belief afterwards is

Beta(α+s,  β+f)\text{Beta}(\alpha + s, \; \beta + f)

You just add the counts. No integration, no sampling. Start from Beta(1,1)(1,1) and run 20 visitors through checkout, of whom 7 convert: the posterior is Beta(1+7, 1+13)=(1+7,\, 1+13) = Beta(8,14)(8, 14), with mean 8/22=0.3648/22 = 0.364. Compare that to the raw estimate 7/20=0.357/20 = 0.35.

The difference is small here, and enormous with little data. Suppose a variant gets 2 conversions from 3 visitors. Raw estimate: 0.667, and a naive dashboard now declares it the best-performing variant on the site. The Beta posterior is Beta(3,2)(3, 2) with mean 3/5=0.63/5 = 0.6 and a distribution so wide it overlaps almost everything. The prior acts as a couple of imaginary prior observations that stop three data points from shouting.

This is exactly the machinery behind Thompson sampling. Keep a Beta posterior per variant, draw one random sample from each, and send the next user to whichever variant drew highest. Variants with little data have wide posteriors and get explored; variants that are genuinely better win more draws over time. You get the explore-exploit trade-off for free, out of arithmetic on two counters.

Two distributions that describe estimates rather than data

The chi-square with kk degrees of freedom is what you get by squaring kk independent standard normals and adding them:

Q=Z12+Z22+⋯+Zk2,E[Q]=k,Var(Q)=2kQ = Z_1^2 + Z_2^2 + \cdots + Z_k^2, \qquad E[Q] = k, \qquad \mathrm{Var}(Q) = 2k

Because it is a sum of squares it lives on [0,∞)[0, \infty) and is right-skewed. Nobody models raw data with it. It appears whenever a test statistic is built from squared deviations: goodness-of-fit tests, tests of independence in contingency tables, and the sampling distribution of a sample variance.

Student's t exists because of one honest admission. To standardise a sample mean you want (xˉ−μ)/(σ/n)(\bar{x} - \mu)/(\sigma/\sqrt{n}), but you do not know σ\sigma — you estimate it as ss from the same small sample. That estimate is itself uncertain, and sometimes too small, which makes the ratio blow up more often than a normal would. The t distribution accounts for that with heavier tails, controlled by degrees of freedom ν=n−1\nu = n-1.

The practical size of the effect: for a 95% two-sided interval, the normal uses a multiplier of 1.96. With ν=4\nu = 4 (a sample of five) the t needs 2.776 — a 42% wider interval for the same confidence. By ν=30\nu = 30 the multiplier is 2.04 and the difference has almost vanished. That is the whole story of t versus normal: it is the price of not knowing σ\sigma, and it is only expensive when nn is small.

Choosing a family without guessing

DistributionSupportParametersMeanVarianceTypical ML use
Normal(−∞,∞)(-\infty, \infty)μ,σ\mu, \sigmaμ\muσ2\sigma^2Residuals, weight init, VAE latents
Binomial{0,…,n}\{0,\dots,n\}n,pn, pnpnpnp(1−p)np(1-p)Conversions, correct predictions in a batch
Poisson{0,1,2,… }\{0,1,2,\dots\}λ\lambdaλ\lambdaλ\lambdaEvent counts, click and error rates
Exponential[0,∞)[0, \infty)λ\lambda1/λ1/\lambda1/λ21/\lambda^2Waiting times, survival, churn timing
Uniform[a,b][a, b]a,ba, b(a+b)/2(a+b)/2(b−a)2/12(b-a)^2/12Initialisation, random search, sampling
Beta[0,1][0, 1]α,β\alpha, \betaα/(α+β)\alpha/(\alpha+\beta)see noteBandits, A/B testing, calibration
Chi-square[0,∞)[0, \infty)kkkk2k2kGoodness-of-fit, independence tests
Student's t(−∞,∞)(-\infty, \infty)ν\nu0 (for ν>1\nu > 1)ν/(ν−2)\nu/(\nu-2) (for ν>2\nu > 2)Small-sample means, robust likelihoods

(Beta variance is αβ/[(α+β)2(α+β+1)]\alpha\beta / [(\alpha+\beta)^2(\alpha+\beta+1)] — rarely needed by hand.)

In practice you can get to the right family with four questions, asked in this order:

  1. Is the outcome a count? If there is a fixed number of trials, binomial. If events accumulate in a window with no ceiling, Poisson.
  2. Is it a waiting time or a duration? Exponential if the process has no memory; something with an ageing hazard otherwise.
  3. Is it a proportion, bounded between 0 and 1? Beta.
  4. Is it unbounded in both directions, roughly symmetric, plausibly a sum of many small effects? Normal. If it is unbounded but heavily skewed, consider modelling its logarithm instead.

Fitting and checking in SciPy

Every distribution in scipy.stats exposes the same four methods, and learning them once covers all of them.

Python
from scipy import statsimport numpy as nplat = stats.norm(loc=200, scale=30)   # a "frozen" distributionlat.pdf(275)        # 0.00058 -- a DENSITY, not a probabilitylat.cdf(275)        # 0.99379 -- P(X <= 275)lat.sf(275)         # 0.00621 -- the survival function, 1 - cdflat.ppf(0.95)       # 249.35  -- the p95: inverse of the cdflat.rvs(size=5, random_state=0)       # five random drawsstats.binom(n=20, p=0.15).pmf(5)      # 0.1028  discrete: .pmf not .pdfstats.poisson(mu=3).pmf(5)            # 0.1008  the same answer, near enoughstats.expon(scale=1/3).sf(1.0)        # 0.0498  P(wait > 1 hour)stats.beta(a=8, b=14).mean()          # 0.3636  the posterior above

Fitting is one call, and so is the sanity check that stops you trusting a bad fit. A Kolmogorov-Smirnov test compares your data's empirical CDF against the fitted one; a small p-value means the family is being rejected.

Python
rng = np.random.default_rng(0)data = rng.exponential(scale=4.0, size=2000)   # right-skewed, definitely not normalmu, sigma = stats.norm.fit(data)print(stats.kstest(data, stats.norm(mu, sigma).cdf).pvalue)# effectively zero -- the normal family is decisively rejectedloc, scale = stats.expon.fit(data, floc=0)print(stats.kstest(data, stats.expon(loc, scale).cdf).pvalue)# comfortably large -- no evidence against the exponential fit# The normal fit still "works": it happily returns a p99...print(stats.norm(mu, sigma).ppf(0.99))    # ~13.6print(stats.expon(loc, scale).ppf(0.99))  # ~18.6  the honest answer

Both fits happily produce a p99. Only one of them is right, and nothing but the goodness-of-fit check tells you which.

So always pair a fit with a check — a KS test, or simply plotting the fitted density over the histogram and looking hard at the tail.

Where these show up in the models you build

Linear regression is a normal assumption in disguise. Least squares is the maximum-likelihood fit precisely when the residuals are normal with constant variance. That assumption is what makes the reported standard errors and p-values meaningful. So plot your residuals: if they are skewed, or fan out as the prediction grows, your coefficients may still be usable but your uncertainty estimates are not.

Naive Bayes is a pile of likelihoods. Gaussian naive Bayes fits one μ\mu and one σ\sigma per feature per class and multiplies the resulting densities. Multinomial naive Bayes swaps in count-based likelihoods for text. Same algorithm, different distributional assumption — and choosing the wrong one is the usual reason a naive Bayes model underperforms.

Initialisation is variance engineering. He and Glorot initialisation draw weights from a normal or uniform with variance scaled to the layer's width, chosen so activation variance neither explodes nor collapses as signals pass through many layers. Get it wrong and a deep network either saturates or produces nothing but noise.

Several standard layers are just sampling. Dropout multiplies activations by a Bernoulli mask. A variational autoencoder's encoder outputs a μ\mu and a log⁡σ2\log \sigma^2 per latent dimension and samples a Gaussian from them. Data augmentation applies uniformly random crops and flips.

Count targets need count models. Predicting clicks, purchases or defects with ordinary least squares lets the model output negative counts and assume constant variance when variance actually grows with the mean. Poisson regression fixes both. And when the observed variance runs well ahead of the mean, that is your signal that a single Poisson rate does not describe your data and something richer is required.

The habit worth building is small. Before you fit anything, plot the target and ask which of these shapes it looks like. After you fit, plot what the model implies and compare it to what you actually saw. Most modelling failures are not clever failures — they are a right-skewed quantity that someone assumed was symmetric.