ml-models / Mathematical foundations / Bayes' rule and likelihood core

Bayes' rule and likelihood

How to update a belief with evidence, and how fitting a model is the same move in reverse: Bayes' rule with a diagnostic test worked through, likelihood versus probability, maximum likelihood, MAP estimation with priors as regularizers, and naive Bayes as scikit-learn implements it.

On this page
  1. Problem
  2. Intuition
  3. Mechanics
  4. Bayes' rule
  5. Likelihood versus probability
  6. Maximum likelihood
  7. MAP estimation and priors as regularizers
  8. Naive Bayes
  9. In the libraries
  10. Worked example
  11. Implementation
  12. Tradeoffs
  13. Connections
  14. Read first
  15. Read next
  16. References

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 diagnostic test as natural frequencies 10,000 people split into 100 sick and 9,900 healthy. The sick split into 90 positive and 10 negative; the healthy into 891 positive and 9,009 negative. Of the 981 positives, 90 are sick, so P(sick | positive) = 0.092. 10,000 people 100 sick (1%) 9,900 healthy 90 positive 10 negative 891 positive 9,009 negative prior 0.01 0.99 sensitivity 0.9 0.1 false positive 0.09 0.91 P(sick | positive) = 90 / (90 + 891) = 0.092

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.

Prior, likelihood and posterior for a coin's bias Three curves over the bias θ from 0 to 1, each scaled to a peak of 1. The Beta(5, 5) prior peaks at 0.5. The likelihood of 7 heads in 10 flips peaks at the MLE, 0.7. The posterior, Beta(12, 8), lies between them and peaks at the MAP estimate, 0.611. 0.0 0.25 0.5 0.75 1.0 coin bias θ MLE 0.7 MAP 0.611 prior Beta(5, 5) likelihood, 7 of 10 posterior Beta(12, 8)

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

TermShape/ParamsMeaningSource
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.30.50.60.70.80.9
L(θ)0.00900.11720.21500.26680.20130.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:

ClassfreewinmeetingreportPriorSmoothed P(word | class), α = 1
spam32010.40.400, 0.300, 0.100, 0.200
ham10340.60.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

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.