The Naive Bayes Classifier
Chapter Fifty-Eight
Syllabus topic Module 2, "Naive Bayes"
Pages 321 to 327 of 591
In one line
Naive Bayes computes the probability of each class given the evidence by multiplying the evidence together as if the pieces were independent, which they are not, and it works anyway.
In the wording a student can write in an examination: the naive Bayes classifier assigns to an instance with features x1 ... xn the class c maximising
P(c | x1..xn) proportional to P(c) * product over i of P(xi | c)
This follows from Bayes theorem together with the naive assumption that the features are conditionally independent given the class. The denominator P(x1..xn) is the same for every class and so is dropped. Laplace smoothing adds a constant to every count to prevent an unobserved feature value from forcing the whole product to zero.
It is Module 1, twice
The derivation is three lines and uses nothing not already proved.
Start from Bayes theorem, Bayes Theorem:
P(c | x1..xn) = P(x1..xn | c) * P(c) / P(x1..xn)
Drop the denominator. It does not depend on c, so it cannot change which class is largest; it is the normalising constant of that chapter.
Apply conditional independence, Conditional Independence. If the features are conditionally independent given the class, the joint likelihood factorises:
P(x1..xn | c) = product over i of P(xi | c)
so P(c | x1..xn) proportional to P(c) * product over i of P(xi | c)
There is nothing else in the method. No optimisation, no iteration, no hyperparameter except the smoothing constant. Training is counting, which is why it is the fastest classifier in MU's list.
And the saving is the one Conditional Independence counted: with 30 binary features the full joint needs 2,147,483,647 numbers and this needs 61.
It worked, with every fraction printed
# The naive Bayes classifier: worked BY HAND on the 14 rows, with every
# probability printed, and the zero-frequency problem shown and then fixed.
ROWS = [
("rain", "far", "no", "yes", "no"),
("rain", "far", "no", "no", "no"),
("clear", "far", "no", "yes", "yes"),
("humid", "near", "no", "yes", "yes"),
("humid", "near", "yes", "yes", "yes"),
("humid", "near", "yes", "no", "no"),
("clear", "near", "yes", "no", "yes"),
("rain", "near", "no", "yes", "no"),
("rain", "near", "yes", "yes", "yes"),
("humid", "far", "yes", "yes", "yes"),
("rain", "far", "yes", "no", "yes"),
("clear", "far", "yes", "no", "yes"),
("clear", "near", "no", "no", "yes"),
("humid", "far", "no", "no", "no"),
]
COLS = ["weather", "distance", "friend", "early"]
TARGET = 4
VALUES = [sorted({r[i] for r in ROWS}) for i in range(len(COLS))]
def counts(cls, col, val):
n = sum(1 for r in ROWS if r[TARGET] == cls)
k = sum(1 for r in ROWS if r[TARGET] == cls and r[col] == val)
return k, n
def cond(cls, col, val, alpha):
"""P(feature = val | class), with Laplace smoothing by alpha."""
k, n = counts(cls, col, val)
return (k + alpha) / (n + alpha * len(VALUES[col]))
def predict(case, alpha, show=False):
scores = {}
for cls in ("yes", "no"):
prior = sum(1 for r in ROWS if r[TARGET] == cls) / len(ROWS)
p = prior
if show:
print(" class %s" % cls)
print(" P(%s) = %d/%d = %.4f"
% (cls, sum(1 for r in ROWS if r[TARGET] == cls), len(ROWS), prior))
for i, v in enumerate(case):
c = cond(cls, i, v, alpha)
k, n = counts(cls, i, v)
p *= c
if show:
print(" P(%s = %s | %s) = (%d + %g)/(%d + %g*%d) = %.4f"
% (COLS[i], v, cls, k, alpha, n, alpha, len(VALUES[i]), c))
scores[cls] = p
if show:
print(" product = %.8f" % p)
total = scores["yes"] + scores["no"]
return scores, (scores["yes"] / total if total else 0.0)
CASE = ("rain", "near", "yes", "no")
print("a new student: weather %s, distance %s, friend going %s, early lecture %s"
% CASE)
print()
print("WITHOUT smoothing (alpha = 0):")
scores, p_yes = predict(CASE, 0.0, show=True)
print(" unnormalised: yes %.8f no %.8f" % (scores["yes"], scores["no"]))
print(" P(attend = yes) = %.8f / (%.8f + %.8f) = %.4f"
% (scores["yes"], scores["yes"], scores["no"], p_yes))
print(" ANSWER: %s" % ("yes" if p_yes > 0.5 else "no"))
print()
print("THE ZERO-FREQUENCY PROBLEM. no student who ATTENDED had weather 'x', an")
print("unseen value, so P(weather = x | yes) would be 0/9 = 0, and the WHOLE")
print("product becomes 0 no matter how strong the other evidence is.")
print("the same happens for any value never seen with a class. check 'clear':")
for cls in ("yes", "no"):
k, n = counts(cls, 0, "clear")
print(" P(weather = clear | %s) = %d/%d = %.4f %s"
% (cls, k, n, k / n, "<- ZERO, and it wipes out the product" if k == 0 else ""))
print()
print("WITH Laplace smoothing (alpha = 1): add 1 to every count, and add")
print("alpha times the number of possible values to every denominator.")
for cls in ("yes", "no"):
k, n = counts(cls, 0, "clear")
print(" P(weather = clear | %s) = (%d+1)/(%d+1*3) = %.4f"
% (cls, k, n, cond(cls, 0, "clear", 1.0)))
print()
print("the smoothed prediction for a student in CLEAR weather, near, no friend,")
print("early lecture:")
C2 = ("clear", "near", "no", "yes")
for alpha in (0.0, 1.0):
s, p = predict(C2, alpha)
print(" alpha = %.0f: P(yes) = %.4f -> %s" % (alpha, p, "yes" if p > 0.5 else "no"))The Naive Bayes Classifier
a new student: weather rain, distance near, friend going yes, early lecture no
WITHOUT smoothing (alpha = 0):
class yes
P(yes) = 9/14 = 0.6429
P(weather = rain | yes) = (2 + 0)/(9 + 0*3) = 0.2222
P(distance = near | yes) = (5 + 0)/(9 + 0*2) = 0.5556
P(friend = yes | yes) = (6 + 0)/(9 + 0*2) = 0.6667
P(early = no | yes) = (4 + 0)/(9 + 0*2) = 0.4444
product = 0.02351558
class no
P(no) = 5/14 = 0.3571
P(weather = rain | no) = (3 + 0)/(5 + 0*3) = 0.6000
P(distance = near | no) = (2 + 0)/(5 + 0*2) = 0.4000
P(friend = yes | no) = (1 + 0)/(5 + 0*2) = 0.2000
P(early = no | no) = (3 + 0)/(5 + 0*2) = 0.6000
product = 0.01028571
unnormalised: yes 0.02351558 no 0.01028571
P(attend = yes) = 0.02351558 / (0.02351558 + 0.01028571) = 0.6957
ANSWER: yes
THE ZERO-FREQUENCY PROBLEM. no student who ATTENDED had weather 'x', an
unseen value, so P(weather = x | yes) would be 0/9 = 0, and the WHOLE
product becomes 0 no matter how strong the other evidence is.
the same happens for any value never seen with a class. check 'clear':
P(weather = clear | yes) = 4/9 = 0.4444
P(weather = clear | no) = 0/5 = 0.0000 <- ZERO, and it wipes out the product
WITH Laplace smoothing (alpha = 1): add 1 to every count, and add
alpha times the number of possible values to every denominator.
P(weather = clear | yes) = (4+1)/(9+1*3) = 0.4167
P(weather = clear | no) = (0+1)/(5+1*3) = 0.1250
the smoothed prediction for a student in CLEAR weather, near, no friend,
early lecture:
alpha = 0: P(yes) = 1.0000 -> yes
alpha = 1: P(yes) = 0.8319 -> yesThe Naive Bayes Classifier
The arithmetic a paper wants. For the class yes: the prior is 9/14, and the four conditional probabilities are read straight off the table by counting. Their product is 0.02351558. The same for no gives 0.01028571. Normalising, P(yes) is 0.6957, so the answer is yes.
Note that the two products do not sum to 1 before normalising. They are P(class) * P(evidence | class), which is the joint, and normalising by their sum divides by P(evidence). That is the normalising constant of Bayes Theorem computed without ever being named.
The zero-frequency problem
Read the middle block. Among the five students who did not attend, none had clear weather, so P(weather = clear | no) is 0/5 = 0.
One zero destroys everything. The product for no becomes exactly 0, whatever the other three features say, so the classifier reports P(yes) = 1.0000: absolute certainty from one missing count. That is not confidence, it is an artefact, and it would occur on any feature value that happens not to appear with a class in a finite sample.
The Naive Bayes Classifier
Laplace smoothing is the fix and it is one line:
P(xi = v | c) = (count + alpha) / (n_c + alpha * number of possible values of xi)
With alpha = 1, P(weather = clear | no) becomes (0 + 1)/(5 + 3) = 0.1250 instead of 0, and the same student's prediction falls from the absurd 1.0000 to a sensible 0.8319. Same answer, honest confidence.
The denominator must add alpha times the number of possible values, not alpha once, or the smoothed probabilities for a feature no longer sum to 1. And this is Regularization again: smoothing is a prior pulling every estimate towards uniform, and alpha = 1 is exactly a uniform prior on each feature's distribution. It is the same device as The Learning Agent used to stop an early run of failures producing an estimate of zero.
The three standard variants
MU writes Naive Bayes without qualification, and a paper may ask which kind.
| Variant | Features are | P(xi given c) is |
|---|---|---|
| Multinomial | counts, such as how often a word occurs | proportional to the count, smoothed |
| Bernoulli | present or absent | a probability of presence, and absence is also evidence |
| Gaussian | continuous numbers | a normal density with the mean and variance of that feature within that class |
Multinomial and Bernoulli differ in a way that matters for text: Bernoulli explicitly scores a word's absence, multinomial ignores it. For short documents Bernoulli usually wins; for long ones multinomial does.
Gaussian naive Bayes is how continuous features are handled without discretising them: estimate the mean and variance of each feature within each class from the training data, and use the normal density. That is two numbers per feature per class, so it remains linear in the number of features.
Why a false assumption gives a good classifier
The assumption is almost always false. In text, New and York are strongly dependent given any class. In the 14 rows, weather and distance may well be related. And the classifier works. A paper asking why expects this answer.
The probabilities are wrong; the ordering is often right. Classification needs only the largest class, not a correct probability. Dependent features count their shared evidence more than once, which pushes the winning class's product further ahead, and the winner usually does not change.
So the correct statement is precise: naive Bayes is a good classifier and a bad probability estimator. Its outputs are notoriously overconfident, clustering near 0 and 1. Anything that uses the probability as a number, such as ranking by risk or setting a threshold on expected cost, should not use naive Bayes without recalibrating it.
The Naive Bayes Classifier
Two more practical reasons it survives: it needs very little data, since each estimate is a one-dimensional count rather than a joint; and it is immune to the curse of dimensionality that defeats k-NN in k-NN for Regression, and What Limits the Method, because it never computes a distance.
Two implementation points that cost marks
Underflow. Multiplying a hundred probabilities gives a number too small for a computer to represent, and it becomes 0. The fix is to work with logarithms: maximise log P(c) + sum of log P(xi | c), which turns the product into a sum and cannot underflow. Every real implementation does this.
A feature value never seen at all, in any class, contributes the same factor to every class and so cannot affect the comparison. It is usually simply skipped.
Distinctions
| Naive Bayes | Bayes theorem | |
|---|---|---|
| Is | a classifier | a theorem |
| Extra assumption | conditional independence of the features | none |
| Exact | no | yes |
| A good classifier | A good probability estimator | |
|---|---|---|
| Needs | the right class to be largest | the number to be right |
| Naive Bayes is | yes | no, it is overconfident |
| Multinomial | Bernoulli | Gaussian | |
|---|---|---|---|
| Features | counts | present or absent | continuous |
| Scores absence | no | yes | not applicable |
| Parameters per feature per class | one | one | two, mean and variance |
| No smoothing | Laplace smoothing | |
|---|---|---|
| An unseen value gives | probability 0, wiping out the product | a small positive probability |
| Confidence here | 1.0000, from one missing count | 0.8319 |
| It is | a bug waiting for a finite sample | a uniform prior, that is regularization |
What it does not mean
Naive does not mean simple-minded. It names the independence assumption specifically.
The assumption is not usually true, and the classifier is not claiming it is. It is claiming that the ordering of the classes survives the approximation.
The output probability is not a probability you should act on numerically. It is overconfident. Use it to choose a class, not to price a risk.
Smoothing is not a fudge. It is a uniform prior, and it is the same device as the ridge penalty of Regularization.
A zero probability is not evidence of impossibility. It is evidence that the value did not occur in a finite sample.
Multiplying the probabilities directly is not how it is implemented. Logarithms are used, to avoid underflow.
Quick revision
P(c | x) proportional to P(c) * product of P(xi | c). Bayes theorem, drop the denominator, and assume the features are conditionally independent given the class.- Training is counting. No optimisation, no iteration. With 30 binary features it needs 61 numbers where the full joint needs 2,147,483,647.
- Worked: prior 9/14, four conditionals counted off the table, product 0.02351558 against 0.01028571, giving
P(yes) = 0.6957. - Zero frequency:
P(weather = clear | no) = 0/5 = 0makes the whole product 0, so the classifier reports 1.0000. One missing count produces absolute certainty. - Laplace smoothing:
(count + alpha) / (n + alpha * number of values). Withalpha = 1the estimate becomes 0.1250 and the confidence falls from 1.0000 to 0.8319. It is a uniform prior, that is regularization. - Variants: multinomial (counts), Bernoulli (presence, and absence is evidence), Gaussian (continuous, mean and variance per feature per class).
- A good classifier and a bad probability estimator. The assumption is false, the probabilities are overconfident, and the class ordering usually survives.
- Implementation: use logarithms, or the product underflows to zero.
The Naive Bayes Classifier
Test yourself
1. Derive the naive Bayes rule from Bayes theorem. Bayes theorem gives P(c | x) as P(x | c) times P(c) divided by P(x). The denominator is the same for every class and cannot change which is largest, so it is dropped. Assuming the features are conditionally independent given the class, P(x | c) factorises into the product of P(xi | c), giving P(c | x) proportional to P(c) times that product.
2. Compute the prediction for a student in rain, near, with a friend going, no early lecture. For yes: prior 9/14 times the four conditionals, giving 0.02351558. For no: prior 5/14 times its conditionals, giving 0.01028571. Normalising, P(yes) is 0.6957, so the prediction is that the student attends.
3. What is the zero-frequency problem, and what does it do to the output? If a feature value never occurs with a class in the training data, its conditional probability is exactly zero, and multiplying by it makes the entire product zero however strong the other evidence. In this chapter no non-attending student had clear weather, so the classifier reports a probability of 1.0000, absolute certainty produced by one missing count.
4. State Laplace smoothing and apply it to that case. Estimate each conditional as the count plus alpha, divided by the class count plus alpha times the number of possible values of that feature. With alpha of 1, P(weather = clear | no) becomes one over eight, that is 0.1250, instead of zero, and the classifier's confidence falls from 1.0000 to 0.8319.
5. Name the three variants of naive Bayes and say what each suits. Multinomial, for count features such as word frequencies. Bernoulli, for presence-or-absence features, which also scores a feature's absence as evidence and suits short documents. Gaussian, for continuous features, modelling each with a normal distribution whose mean and variance are estimated per class.
The Naive Bayes Classifier
6. The independence assumption is usually false. Why does the classifier still work? Because classification needs only the correct class to have the largest score, not a correct probability. Dependent features double-count their shared evidence, which exaggerates the leading class's score but usually does not change which class leads. The correct summary is that it is a good classifier and a poor probability estimator, since its outputs are systematically overconfident.
7. Why are logarithms used in an implementation? Because multiplying many probabilities produces a number too small to represent, which becomes zero and destroys the comparison. Taking logarithms turns the product into a sum of logarithms, which cannot underflow, and the class with the largest sum is the same as the class with the largest product.
The rest of this subject
These notes are cut from the University's printed syllabus. Open the syllabus itself, or the past papers, for the same subject.