Problem
A test for a rare disease is 90% sensitive and gives a false positive 9% of the time. You test positive. How likely is it that you have the disease? The tempting answer is about 90%. The right answer for this test is under 10%. The data (a positive result) has to be combined with what was believed before it arrived (the disease is rare), and intuition is bad at that combination.
Machine learning faces the same question everywhere. A model has parameters θ and sees data x. The model says how probable the data is for each θ; what we want is the reverse, which θ is plausible given the data. Bayes' rule is the exact way to turn one into the other, and its two simplified uses, maximum likelihood and MAP estimation, are how almost every model in this reference is trained.
Intuition
Think in counts rather than percentages. Out of 10,000 people, 1% (100) are sick, and the test catches 90 of them. Of the 9,900 healthy people, 9% (891) also test positive. So 981 people test positive, and only 90 of them are sick: 9.2%. The false positives win because they come from a group 99 times larger. A positive result multiplies the odds of being sick by 10 (0.9 / 0.09), but from a starting point of 1 in 99.
The highlighted boxes are everyone who tests positive. Bayes' rule is this picture written as a formula.
Mechanics
Bayes' rule
The definition of conditional probability gives the joint probability two ways, P(A, B) = P(A | B) P(B) = P(B | A) P(A). Dividing by P(B):
P(θ | x) = P(x | θ) P(θ) / P(x) posterior = likelihood × prior / evidence
The prior P(θ) is the belief before the data, the likelihood P(x | θ) says how well each θ explains the data, and the evidence P(x) = Σθ P(x | θ) P(θ) just makes the posterior sum to one. For the test, P(positive) = 0.9 × 0.01 + 0.09 × 0.99 = 0.0981, and P(sick | positive) = 0.009 / 0.0981 = 0.0917. Because the evidence does not depend on θ, it is often dropped: posterior ∝ likelihood × prior. In odds form the evidence cancels exactly: posterior odds = likelihood ratio × prior odds.
Today's posterior is tomorrow's prior. A second, independent positive test starts from 0.0917 and ends at 0.5025; a third reaches 0.9099. Each result multiplies the odds by the same factor of 10.
Likelihood versus probability
P(x | θ) is one formula read two ways. With θ fixed and x varying, it is a probability: it sums (or integrates) to one over all possible data. With the data fixed and θ varying, it is the likelihood L(θ) = P(x | θ): a score for each parameter value, which need not sum to one over θ and is not a probability of θ. Flip a coin 10 times and see 7 heads. For a fixed bias θ = 0.7 the probabilities of 0 to 10 heads add to 1; the likelihood of the observed 7 heads, as a function of θ, has area 0.0909 (exactly 1/11). To make a statement about θ itself, you need a prior and Bayes' rule.
For independent examples the likelihood is a product, so in practice everything uses the log:
log L(θ) = Σi log P(xi | θ). Products of many small
numbers underflow; sums of logs do not. This is why every torch.distributions class exposes
log_prob rather than a probability.
Maximum likelihood
Maximum likelihood estimation (MLE) picks the θ that makes the observed data most probable, θ̂ = argmaxθ Σ log P(xi | θ); equivalently it minimizes the negative log-likelihood (NLL). For the coin, setting the derivative of 7 log θ + 3 log(1 − θ) to zero gives θ̂ = 7/10 = 0.7, the observed frequency.
The common training losses are NLLs of a chosen noise model. If y ∼ Normal(f(x), σ²)
with fixed σ, the NLL is Σ(y − f(x))² / 2σ² plus a constant, so least
squares is MLE under Gaussian noise; nn.GaussianNLLLoss keeps the variance term for models that
predict σ² too. If y ∼ Bernoulli(σ(z)), the NLL is binary cross-entropy, and PyTorch's
Bernoulli.log_prob is literally -binary_cross_entropy_with_logits. Categorical
labels give cross-entropy (see loss functions).
MAP estimation and priors as regularizers
MLE trusts the data completely. With 0 heads in 3 flips it says θ̂ = 0: the coin can never land heads. Maximum a posteriori (MAP) estimation instead maximizes the posterior, which by Bayes' rule is the likelihood plus a log-prior term:
θ̂MAP = argmaxθ [ Σi log P(xi | θ) + log P(θ) ]
For the coin, a Beta(a, b) prior acts like a − 1 extra heads and b − 1 extra tails, so θ̂MAP = (k + a − 1) / (n + a + b − 2). With a Beta(2, 2) prior, 0 heads in 3 gives 0.2 instead of 0: add-one (Laplace) smoothing is MAP estimation.
The log-prior is a regularizer. Take linear regression with Gaussian noise of variance σ² and a Gaussian prior w ∼ Normal(0, τ²) on each weight. The negative log-posterior is, up to constants and a factor 1/2σ²,
‖y − Xw‖² + (σ² / τ²) ‖w‖²
which is exactly ridge regression's objective with α = σ²/τ²: a tighter prior (small τ) means stronger regularization. A Laplace prior gives the L1 penalty of the lasso instead (see regularization). Weight decay in a neural network is the same Gaussian prior. With more data the likelihood term grows with n while the prior stays fixed, so MAP approaches MLE.
The posterior is the product of the other two, renormalized: narrower than either and between their peaks.
Naive Bayes
A classifier can use Bayes' rule directly: model how each class generates features, P(x | y), and the class frequencies, P(y), then predict argmaxy P(y) P(x | y). With many features P(x | y) is impossible to estimate jointly, so naive Bayes assumes the features are independent given the class:
log P(y | x) = log P(y) + Σj log P(xj | y) − log P(x)
The assumption is almost always false (words in an email are not independent), but the classifier still
ranks classes well, trains in one pass of counting, and needs little data. Its probabilities tend to be
overconfident because correlated features are counted as independent evidence. scikit-learn's estimators all
compute the "joint log likelihood" log P(y) + log P(x | y) and normalize it
with a log-sum-exp; they differ in the per-feature model: GaussianNB (a Normal per feature and
class), MultinomialNB (word counts), BernoulliNB (binary features) and
CategoricalNB. MultinomialNB's alpha adds pseudo-counts to every
feature: the MAP smoothing above.
In the libraries
| Term | Shape/Params | Meaning | Source |
|---|---|---|---|
| Log-likelihood | Distribution.log_prob(value) |
The base class's contract: log of the density or mass at value; every distribution implements it. |
distribution.py 204-212 |
| Gaussian log-likelihood | Normal(loc, scale).log_prob |
−(x − μ)²/2σ² − log σ − log √(2π); the squared error inside is why least squares is MLE. | normal.py 87-101 |
| Bernoulli log-likelihood | Bernoulli(logits=…).log_prob |
Returns -binary_cross_entropy_with_logits(logits, value): BCE is the Bernoulli NLL. |
bernoulli.py 121-125 |
| Gaussian NLL loss | full=False, eps=1e-6; (input, target, var) |
½(log max(var, eps) + (input − target)²/max(var, eps)), the constant omitted unless full. |
loss.py 377-461 |
| Gaussian prior as a penalty | Ridge(alpha=1.0) |
Minimizes ‖y − Xw‖² + α‖w‖², the MAP objective with α = σ²/τ². | _ridge.py 1022-1031 |
| Joint log likelihood | (n_samples, n_classes) | log P(y) + log P(x | y); predict takes its argmax. |
naive_bayes.py 72-117 |
| Posterior P(y | x) | predict_log_proba, predict_proba |
Subtracts log P(x), the log-sum-exp of the joint over classes. | naive_bayes.py 119-161 |
| Class prior P(y) | fit_prior=True, class_prior=None |
Given priors, else class frequencies, else uniform. | naive_bayes.py 615-637 |
| Smoothed word probabilities | MultinomialNB(alpha=1.0) |
log(count + α) − log Σ(count + α) per class; the joint is X · feature_log_probT + class_log_prior. | naive_bayes.py 925-937 |
| Gaussian class-conditionals | GaussianNB(priors=None, var_smoothing=1e-9) |
Sums per-feature Normal log-densities with each class's mean and variance; var_smoothing adds a fraction of the largest feature variance. |
naive_bayes.py 533-545, 451 |
Worked example
The diagnostic test. Prior 0.01, sensitivity 0.9, false-positive rate 0.09:
- Evidence: P(+) = 0.9 × 0.01 + 0.09 × 0.99 = 0.0981.
- Posterior: P(sick | +) = 0.009 / 0.0981 = 0.0917, the same as 90 / 981 in the tree.
- Odds: prior odds 1 : 99, likelihood ratio 0.9 / 0.09 = 10, posterior odds 10 : 99, which is 0.0917 again.
- Repeated positives: 0.0100 → 0.0917 → 0.5025 → 0.9099.
Likelihood, MLE and MAP for the coin (7 heads in 10):
| θ | 0.3 | 0.5 | 0.6 | 0.7 | 0.8 | 0.9 |
|---|---|---|---|---|---|---|
| L(θ) | 0.0090 | 0.1172 | 0.2150 | 0.2668 | 0.2013 | 0.0574 |
The likelihood peaks at θ̂MLE = 0.7. With the Beta(5, 5) prior (a mild belief that the coin is fair, worth 4 heads and 4 tails), the posterior is Beta(7 + 5, 3 + 5) = Beta(12, 8), with mode θ̂MAP = (7 + 4) / (10 + 8) = 0.6111 and mean 0.6000. With 70 heads in 100 the same prior moves the estimate much less: MAP 0.6852 against MLE 0.7. And with 0 heads in 3 under Beta(2, 2), the MAP is 0.2 rather than 0.
Ridge as MAP, one weight. Points (1, 1), (2, 3), (3, 2), model y = wx. Σxy = 13 and Σx² = 14, and the MAP weight is Σxy / (Σx² + α):
- α = 0 (no prior: least squares, the MLE): w = 13 / 14 = 0.9286.
- α = 1 (prior variance equal to the noise variance): w = 13 / 15 = 0.8667.
- α = 14 (prior variance one fourteenth of the noise variance): w = 13 / 28 = 0.4643.
Naive Bayes by hand. Training data: 2 spam and 3 ham emails, with these word counts:
| Class | free | win | meeting | report | Prior | Smoothed P(word | class), α = 1 |
|---|---|---|---|---|---|---|
| spam | 3 | 2 | 0 | 1 | 0.4 | 0.400, 0.300, 0.100, 0.200 |
| ham | 1 | 0 | 3 | 4 | 0.6 | 0.167, 0.083, 0.333, 0.417 |
Classify "free win meeting". Spam: 0.4 × 0.4 × 0.3 × 0.1 = 0.0048. Ham: 0.6 × 0.167 × 0.083 × 0.333 = 0.0028. Normalizing, P(spam | message) = 0.6334. In logs, as scikit-learn computes it, the joint log likelihoods are −5.3391 and −5.8861.
Without smoothing, "win" never appeared in ham, so P(win | ham) = 0 and any email containing "win" (and not "meeting") is spam with probability 1.0, however much else points to ham; "free win" alone gets 0.8521 with α = 1. And "free win meeting" without smoothing has zero probability under both classes ("meeting" never appeared in spam), so the posterior is undefined.
Implementation
import torch
from torch.distributions import Normal
data = torch.tensor([2.1, 1.7, 2.6, 2.0, 1.4])
mu = torch.zeros((), requires_grad=True)
log_sigma = torch.zeros((), requires_grad=True)
opt = torch.optim.Adam([mu, log_sigma], lr=0.05)
prior = Normal(0.0, 1.0) # a prior on mu; drop it for plain MLE
for _ in range(500):
nll = -Normal(mu, log_sigma.exp()).log_prob(data).sum()
loss = nll - prior.log_prob(mu) # negative log-posterior: MAP
opt.zero_grad(); loss.backward(); opt.step()
from sklearn.naive_bayes import MultinomialNB
X = [[3, 2, 0, 1], [1, 0, 3, 4]] # word counts, one row per class
y = ["spam", "ham"]
clf = MultinomialNB(alpha=1.0).fit(X, y)
clf.predict_proba([[1, 1, 1, 0]])
The MAP loss is the MLE loss plus −log prior, the same shape as a data loss plus a penalty. Parameterizing
σ through its log keeps it positive without constraints. The scikit-learn lines pool each class's counts
into one row, so the class prior they learn is uniform, not the 2 : 3 of the worked example; pass
class_prior=[0.6, 0.4] (classes are sorted, ham first) to match it.
Tradeoffs
- MLE versus MAP. MLE needs no prior but overfits small samples and gives zero probability to anything unseen; MAP fixes that at the price of choosing a prior, whose influence fades as data grows.
- Point estimates versus the full posterior. MLE and MAP return one θ and discard how uncertain it is. Keeping the posterior (Bayesian inference) gives calibrated uncertainty but is rarely tractable for neural networks; ensembles and dropout approximations are the usual compromises.
- MAP depends on the parameterization. The mode of a density moves when θ is transformed (say, to log θ), so MAP is not invariant the way MLE is.
- Naive Bayes. Fast, works with little data and many features, and a strong baseline for text; but the independence assumption makes its probabilities overconfident and it cannot model feature interactions.
- Base rates matter. A classifier's posterior depends on the class prior it was trained with; a model trained on balanced data overstates rare classes when deployed where they are rare.
A test has 99% sensitivity and a 1% false-positive rate. The disease affects 1 in 10,000 people. You test positive: roughly how likely are you to be sick?
About 1%. The likelihood ratio is 99, and the prior odds are 1 : 9,999, so the posterior odds are about 99 : 9,999, a probability near 0.0098.
Why does minimizing mean squared error amount to maximum likelihood?
Under Gaussian noise with a fixed variance, the negative log-likelihood of each target is (y − f(x))² / 2σ² plus a constant, so minimizing the summed NLL and minimizing the summed squared error pick the same parameters.
Which prior on the weights corresponds to L2 weight decay, and what does a stronger penalty mean about that prior?
A zero-mean Gaussian. The penalty coefficient is σ²/τ², so a stronger penalty means a smaller prior variance τ²: a firmer belief that the weights are near zero.
Connections
Read first
Read next
- Uncertainty advanced
- Variational autoencoders core
- Causal inference advanced
References
- Bishop. Pattern Recognition and Machine Learning. Springer, 2006 (Bayes' rule, MLE and MAP, Beta-binomial conjugacy, the Gaussian prior behind ridge).
- Murphy. Probabilistic Machine Learning: An Introduction. MIT Press, 2022 (naive Bayes and smoothing).
- Gigerenzer and Hoffrage. How to Improve Bayesian Reasoning Without Instruction: Frequency Formats. Psychological Review 102(4), 1995 (natural frequencies).
- PyTorch v2.15.0:
torch/distributions/distribution.py,normal.py,bernoulli.py. - scikit-learn 1.9.1:
sklearn/naive_bayes.py,_ridge.py.