Maximum likelihood
Choose the parameters that make the data most probable.
Create a free account Try part of it here
Why it matters
Every model has settings, and someone has to choose them. The most common way to choose, in statistics and in machine learning, is this: pick the settings that make the data you actually saw as probable as possible. That's called maximum likelihood. You already use it when you work out a conversion rate as conversions ÷ visitors. A language model trained on the internet uses it too. Its training goal, making the cross-entropy loss small, is maximum likelihood in disguise.
What you'll be able to do
- Define the likelihood of data under a model's parameters
- Find the maximum-likelihood estimate by searching and by formula
- Work with log-likelihoods instead of likelihoods
- Explain why training a language model is maximum likelihood
What it covers
- Which setting explains what you saw?
- Work with the log instead
- Worked example: an email click rate
- The same trick for other models
- Training a language model is maximum likelihood
Which setting explains what you saw?
A friend flips a coin 10 times and gets 8 heads. Is it a fair coin? It could be. A fair coin does land 8 heads out of 10 now and then, about 4 times in 100. But a coin weighted to land heads 80% of the time gets that result about 30 times in 100. So the weighted coin is the better explanation of what you saw.
That's the whole idea of this lesson. You have some data. You have a model with a setting you can turn, like a dial. For each position of the dial, you ask: how probable would this exact data be? Then you pick the position that makes it most probable.
A tiny shop
Four people visit your online shop. The first three buy something. The fourth leaves without buying. Call the true chance that a visitor buys , the conversion rate. You don't know . You only know what happened.
Try a few guesses for . Assume each visitor decides independently, so you can multiply their chances. Each buyer contributes . The visitor who left contributes , the chance of not buying.
- If : 0.5 × 0.5 × 0.5 × 0.5 = 0.0625
- If : 0.75 × 0.75 × 0.75 × 0.25 ≈ 0.105
- If : 0.9 × 0.9 × 0.9 × 0.1 ≈ 0.073
The guess 0.75 explains the data best. It's also 3 out of 4, the share of visitors who bought. That's no accident, as you'll see below.
Each number in that list is a likelihood. It says how probable your data would be if that guess were true. The guess with the highest likelihood is the maximum-likelihood estimate, or MLE for short.
for p in [0.5, 0.75, 0.9]: likelihood = p * p * p * (1 - p) print(p, round(likelihood, 4))
Notice which way around this goes. The data stays fixed. Only the guess changes. A likelihood is not the chance that the guess is right. It's the chance of the data, if the guess were right.
The same idea with real numbers
Now a bigger shop. You saw 64 conversions from 2,000 visitors. If the conversion rate were , the chance of seeing exactly this data is:
Here is the likelihood of the rate . The small raised 64 means "multiply by itself 64 times", once for each buyer. The 1,936 counts the visitors who didn't buy (2,000 − 64).
In plain words: multiply in the chance of "bought" once for every buyer. Multiply in the chance of "didn't buy" once for every visitor who left. The result says how well the rate explains your data.
A rate of 0.01 gives a tiny likelihood: at that rate, 64 sales would be very unlikely. A rate of 0.5 gives a far tinier one, since you'd expect around 1,000 sales, not 64. Somewhere in between, the likelihood reaches a peak. The rate at that peak is the MLE.
Slide the guess to find the top of the curve. It always peaks exactly at opens ÷ emails.
p = 0.5 print(p * p * (1 - p))
Show the answer
0.125
Two buyers give 0.5 × 0.5, and the visitor who left gives 1 − 0.5 = 0.5. So 0.5 × 0.5 × 0.5 = 0.125.
Work with the log instead
With 2,000 visitors, the likelihood is a product of 2,000 numbers below 1. You saw the problem in the lesson on exponentials and logarithms. The answer gets so small that the computer rounds it to zero. That's called underflow.
The fix is to take the log. The log turns multiplying into adding, so nothing shrinks to zero. And the log always goes up when its input goes up. So the peak stays at the same .
The curly letter (read "ell") stands for the log-likelihood. Here "log" means the natural log, np.log or math.log in code.
In plain words: add up once for each buyer. Add once for each visitor who didn't buy. The bigger the total, the better explains the data. The total is always negative, so "bigger" means closer to zero.
Back to the tiny shop, with 3 buyers, 1 visitor who left, and :
- 3 × log 0.75 ≈ 3 × (−0.288) ≈ −0.863
- 1 × log 0.25 ≈ −1.386
- total ≈ −2.249
That's the log of the likelihood you found before, 0.105. Same information, written as a sum.
import math p = 0.75 log_lik = 3 * math.log(p) + 1 * math.log(1 - p) print(round(log_lik, 4)) print(round(math.log(p * p * p * (1 - p)), 4))
Both lines print −2.2493. Now the full shop with 2,000 visitors. The code below tries 600 rates between 0.01 and 0.07. It works out the log-likelihood of each one and keeps the best. (np.argmax gives the position of the largest value in a list.)
import numpy as np import matplotlib.pyplot as plt conversions, visitors = 64, 2000 p = np.linspace(0.01, 0.07, 600) log_lik = conversions * np.log(p) + (visitors - conversions) * np.log(1 - p) best = p[np.argmax(log_lik)] plt.plot(p, log_lik) plt.axvline(best, linestyle="--", color="gray") plt.xlabel("conversion rate p") plt.ylabel("log-likelihood") print(f"MLE by search: {best:.4f} conversions / visitors: {conversions / visitors:.4f}")
The curve is a hill. Its top sits at , which is conversions ÷ visitors. The same holds for any rate: the MLE is successes divided by trials. Your everyday guess was maximum likelihood all along.
Go deeper Why the peak is always successes ÷ trials
At the top of a hill, the slope is zero. So find the derivative of and set it to zero. The derivative of is . The derivative of is . That gives:
Read it as: "the pull up from the buyers equals the pull down from the visitors who left." Multiply both sides by to get . That tidies up to , so . With successes in trials, the same steps give .
Show the answer
At the same place, p = 0.032
The log always rises when its input rises, so it never moves the peak. Taking logs only avoids underflow and turns products into sums.
Worked example: an email click rate
You send an email to 20 people and 3 of them click the link. Try five possible click rates and find the one with the highest log-likelihood.
import math clicks, sent = 3, 20 # 3 people clicked out of 20 emails sent for p in [0.05, 0.10, 0.15, 0.20, 0.30]: log_lik = clicks * math.log(p) + (sent - clicks) * math.log(1 - p) print(p, round(log_lik, 3))
Each line adds log p once per click and log(1 − p) once per person who didn't click. The best total is −8.454, at p = 0.15. That's 3 ÷ 20, the share who clicked. Rates either side score worse: −8.699 at 0.1 and −8.622 at 0.2. The numbers rise to a peak and fall away again, like the hill in the chart above.
The same trick for other models
Every model below has its own setting to estimate. In each case, maximum likelihood gives an answer you may already have guessed. A small "hat" on a letter, as in (read "p-hat"), means "the estimate worked out from the data".
| Model | Data | Maximum-likelihood estimate |
|---|---|---|
| Bernoulli (convert or not) | k successes in n | |
| Poisson (orders per hour) | counts | = their average |
| Normal | values | = mean, = average squared deviation (divide by ) |
| Linear regression with normal noise | (x, y) pairs | the least-squares fit |
Here's each row in plain words:
- Bernoulli is one yes-or-no try per visitor, like the shop. The best rate is the share of yeses.
- Poisson models counts in a fixed window, like orders per hour. Its setting is (lambda), the average rate. The best estimate is the plain average of your counts.
- Normal is the bell curve. Its middle, (mu), is estimated by the mean. Its spread, the variance (sigma squared), is estimated by the average squared distance from the mean. Maximum likelihood divides by , the number of values, not by .
- Linear regression draws the best straight line through a cloud of points. You'll meet it in Phase 3. "Least squares" means choosing the line that makes the squared misses as small as possible.
print(30 / 200)
Show the answer
0.15
30 / 200 is 0.15. For yes-or-no data, the rate that best explains what you saw is the share of yeses.
That last row explains why least squares is the default for drawing a line. If the noise around the line follows a bell curve, the least-squares line is the maximum-likelihood line.
Go deeper Why least squares is maximum likelihood
Recall the height of the bell curve from the normal distribution lesson. Its main part is . Take the log and that part becomes , plus a number that doesn't depend on . For a line, is a point's real value and is the line's prediction for it. So is that point's miss.
Read it as: "the log-likelihood of one point is minus its squared miss, scaled down." Add this up over all points. Making the total as large as possible is the same as making the sum of squared misses as small as possible. That's least squares.
Show the answer
4
For a Poisson model the MLE of λ is the average count: 20 / 5 = 4.
Training a language model is maximum likelihood
A language model reads text one token at a time. A token is a word or a piece of a word. At each position, the model gives a probability to every possible next token. Its parameters are all the numbers inside it that training adjusts. Together, they're written (theta).
Think back to the shop. There, you multiplied one probability per visitor. Here, you multiply one probability per token: the probability the model gave to the token that actually came next. That product is the likelihood of the training text. Training looks for the that makes it as large as possible. In logs, the goal is:
Take it one piece at a time:
- is the probability the model, with parameters , gives to the token at position . The bar reads "given", as in the conditional probability lesson.
- (a capital sigma) means "add up over every position ".
- means "find the parameters that make this total as large as possible".
In plain words: at every position in the training text, take the log of the probability the model gave to the token that really came next. Add them all up. Training adjusts the model to make that total as big as it can.
Now flip the sign and take the average instead of the total. You get the average negative log-likelihood, which is the training loss. The cross-entropy lesson, coming up soon, shows this is the same as cross-entropy loss. Gradient descent pushes the loss down. That's the same as pushing the likelihood up.
A small example. Say a model gives the real next tokens probabilities 0.5 and 0.25:
- log-likelihood: log 0.5 + log 0.25 ≈ −0.693 + (−1.386) ≈ −2.079
- average loss: flip the sign and divide by 2 tokens, ≈ 1.040
The code does the same for a five-token sentence and two models.
import numpy as np # Probabilities a model gave to the tokens that actually came next in one sentence probs_good = np.array([0.6, 0.8, 0.4, 0.7, 0.9]) probs_bad = np.array([0.1, 0.3, 0.05, 0.2, 0.4]) for name, p in [("good model", probs_good), ("weak model", probs_bad)]: print(f"{name}: log-likelihood {np.log(p).sum():.2f}, average loss {-np.log(p).mean():.3f}")
The good model scores a log-likelihood of −2.11 and an average loss of 0.422. The weak model scores −9.03 and 1.806. Higher likelihood, lower loss: they're the same statement made two ways.
Show the answer
Model A
The average loss is the average negative log-likelihood. Lower loss means the model gave the real next tokens higher probabilities, so higher likelihood.
Practice what you just read
- 5 code examples you can edit and run
- a practice exercise that checks your code
- a 5-question quiz
- 5 review cards for spaced repetition
Try a quiz question
What is the likelihood, in maximum likelihood estimation?