Artificial Intelligence · AI Foundations
Naive Bayes
In AI because the first learned classifier you can derive end to end is Bayes rule plus one independence assumption — and it still ships in production text pipelines.
One independence assumption turns Bayes rule from a formula into a working classifier — the first learned model in this course you can derive, implement and debug end to end. Naive Bayes is old, fast, and still a serious baseline for text; just as importantly, its failure mode teaches you how learned probabilities go wrong, which the calibration lesson then makes precise.
- Artificial Intelligence
- Medium level
- 6 concepts
1Classification as inference
A classifier answers 'which class produced this input?' — and Bayes rule makes that literal. Score each class by prior × likelihood, P(c) × P(x|c), and pick the class with the largest product; the normaliser P(x) is shared by all classes and drops out of the comparison.
A model built this way is called generative: it describes how each class generates inputs, then inverts that story with Bayes rule. The other family — discriminative models such as logistic regression, coming in the ML course — skips the story and fits the boundary P(c|x) directly.
The generative route pays off when the class-conditional structure is easy to estimate. Count how often each word appears in spam and in ham and you have the whole model: no gradients, no iterations — counting is the training loop.
Figure. The generative story naive Bayes commits to: first the class is drawn, then each word is generated from the class independently of the other words. Every arrow points from class to feature — the classifier runs the arrows backwards with Bayes rule.
| Generative (this lesson) | Discriminative (ML course) | |
|---|---|---|
| Models | P(x|c) and P(c) | P(c|x) directly |
| Training | counting / density estimation | optimisation of a loss |
| Example | naive Bayes | logistic regression |
2The independence assumption
P(x|c) for a whole document is unlearnable head-on — no corpus contains every sentence. Naive Bayes replaces it with the one assumption that makes counting sufficient: given the class, features are independent, so the document's likelihood is just a product of one small factor per word.
The assumption is false in almost every real dataset — 'free' and 'offer' plainly travel together in spam. What failure looks like is double counting: correlated words re-submit the same evidence, and the multiplied score comes out far too confident.
Yet the decision often survives, because classification uses only the argmax. Distorted probabilities that still rank the true class first cost nothing at decision time — naive Bayes is routinely a well-ranking, badly-calibrated model, a distinction the calibration lesson makes precise.
Two words, four counts
A tiny corpus: 4 spam mails ('free' appears in 3, 'offer' in 3) and 4 ham mails ('free' in 0, 'offer' in 1). With add-one smoothing on presence features, classify a new mail containing both words. Priors are 4/8 each.
- P(free|spam) = (3+1)/(4+2), same for offer2/3 each
- P(free|ham) = (0+1)/(4+2); P(offer|ham) = (1+1)/(4+2)1/6 and 1/3
- spam: 1/2 × 2/3 × 2/3; ham: 1/2 × 1/6 × 1/32/9 versus 1/36
- P(spam|both words) = (2/9) / (2/9 + 1/36)8/9 ≈ 0.889
Pro tip. Every number here is a count plus one divided by a count plus two. That is the entire training algorithm — which is why naive Bayes fits in milliseconds where an iterative model takes minutes.
In a spam filter the words 'free' and 'offer' plainly travel together. Why does naive Bayes often work anyway?
- The two words really are independent once stop words have been removed
- Bag-of-words features are uncorrelated by construction
- The classifier detects and drops correlated features on its own
- The broken assumption distorts the probabilities but usually leaves the same class in front, and the ranking is what the decision uses
Correlated evidence gets double-counted, so the numbers come out overconfident. Argmax survives that, which is why a badly calibrated classifier can still be a good one.
3Bag-of-words text
To use any of this on text you need features, and the crudest featurisation is the one naive Bayes thrives on: forget word order entirely and keep only which words occur, or how many times. A document becomes a long, sparse count vector over the vocabulary — the bag of words.
The fit between representation and model is exact. One likelihood factor per vocabulary word is precisely what the independence assumption knows how to combine, and sparse counts are exactly what add-one smoothing repairs.
Two standard variants: Bernoulli naive Bayes looks only at presence or absence of each word (and multiplies in absence factors for words that do not appear); multinomial naive Bayes uses the counts, which usually wins on longer documents. Word order, negation and phrasing are invisible to both — 'not good' and 'good' carry the same bag.
| Variant | Feature per word | Typical use |
|---|---|---|
| Bernoulli | appears or not | short texts, small vocabularies |
| Multinomial | how many times | longer documents, the standard text baseline |
| Gaussian | real-valued features | non-text numeric columns |
4Learning is counting (plus one)
Where do the priors and likelihoods come from? From labelled examples: the prior is each class's share of the training set, and each word's likelihood is its frequency within that class. This is why naive Bayes is supervised learning rather than applied probability — a different labelled sample yields a different classifier.
Raw frequencies have a fatal edge case. A word never seen in a class has probability zero there, and one zero vetoes the entire product no matter how strong the other evidence is — a single unseen word would make a mail certainly-not-spam.
Add-one (Laplace) smoothing pretends every word was seen once more than it was, so no factor is ever exactly zero. In practice implementations also work in log space — sums of logs instead of products — because a few hundred small factors multiplied together underflow floating point.
Figure. In the four spam mails, lottery never appears. Unsmoothed P(lottery|spam) = 0/4 zeroes the whole spam product. Add-one writes (0+1)/(4+2) = 1/6, so strong words elsewhere can still win.
From corpus to classifier
- Count classesPrior = each class's share of the training set.
- Count words per classLikelihood = the word's frequency within the class, plus one.
- Sum logs at test timeScore each class by log prior plus log likelihoods; the argmax wins.
The zero that vetoes
In the four spam mails of the tiny corpus, the word 'lottery' never appears. Score a new mail containing it.
- Unsmoothed: P(lottery|spam) = 0/40 — the whole spam product collapses
- Add-one: (0+1)/(4+2)1/6
- Spam score multiplies by 1/6 instead of 0strong words elsewhere can still win
Pro tip. Smoothing is a prior in disguise: add-one says 'before any data, believe every word equally possible in every class'. More data drowns it out; tiny data leans on it.
What makes naive Bayes supervised learning rather than an application of probability theory?
- It passes a linear score through a sigmoid to obtain probabilities
- It cannot be run at all without a validation split
- It is iterative, so it has a training loop like any other model
- Its priors and likelihoods are estimated from labelled examples instead of being handed to it
Bayes rule is arithmetic. What makes this a learned model is that the counts feeding it come from a labelled sample, and a different sample gives a different classifier.
5Naive Bayes in code
The scikit-learn interface is three lines: construct, fit, score. GaussianNB is the variant for real-valued feature columns; for text you would reach for MultinomialNB or BernoulliNB on a count matrix.
The lab below stages the failure mode from the independence concept where you can watch it. Fit a Bernoulli model on eight tiny mails, then feed it the same evidence twice by duplicating a column: the reported probability climbs from 0.889 to about 0.97 — more confident on no new information — while the predicted class never moves.
That pair of observations — scores distorted, ranking intact — is the signature of correlated evidence in any probabilistic model, not just this one. It is worth internalising before the calibration lesson.
Figure. Construct, fit, score: GaussianNB for real-valued columns; MultinomialNB or BernoulliNB for counts. Duplicating a column climbs the reported probability from 0.889 to about 0.97 — more confident on no new information — while the predicted class never moves.
The interface
- ConstructPick the variant for the feature type: Gaussian for real values, multinomial or Bernoulli for counts.
- Fitclf.fit(X_train, y_train) does the counting.
- Scoreclf.score(X_test, y_test) reports accuracy on rows the fit never saw.
Gaussian naive Bayes
from sklearn.naive_bayes import GaussianNB
clf = GaussianNB()
clf.fit(X_train, y_train)
print(clf.score(X_test, y_test))Coding lab. Double-count the evidence runs in the app, with checks on your output.
6Where it wins, where it loses
Reach for naive Bayes when data is small, features are many, and an answer is needed now: it trains in one pass, has almost no hyperparameters, and is hard to beat on short-text classification. As a baseline it earns a place in any pipeline — a fancier model that cannot beat counting is telling you something.
It loses when correlated features double-count evidence badly enough to flip the ranking; when the numbers must be read as probabilities — its scores saturate toward 0 and 1; and when word order or feature interactions carry the signal.
The honest summary: trust its argmax sooner than its probabilities. What it would take to trust the probabilities is exactly the next lesson.
| Situation | Verdict |
|---|---|
| Short text, many features, little data | strong first choice |
| Need a fast, honest baseline | yes — beat it before shipping anything bigger |
| Scores feed a cost calculation as probabilities | recalibrate first, or use another model |
| Heavily correlated features, interactions matter | expect distortion; compare a discriminative model |
Notes
- Classification as inference: score each class by prior × likelihood and take the argmax; the normaliser drops out.
- The naive assumption — features independent given the class — makes counting sufficient and is false in almost every real dataset; the argmax often survives the distortion.
- Training is counting: priors are class shares, likelihoods are smoothed word frequencies, and implementations sum logs to avoid underflow.
Formulas
- score(c) ∝ P(c) × P(x₁|c) × … × P(xₙ|c)
- add-one smoothing (presence features): (count + 1) / (docs in class + 2)
Exam traps & shortcuts
- A single unseen word zeroes an unsmoothed class score — if a naive Bayes model suddenly outputs certainty, look for a zero count before anything else.
- Trust the argmax sooner than the probabilities: correlated features push naive Bayes scores toward 0 and 1.
- Multiply hundreds of small likelihoods and you underflow floating point — implementations sum logs, and so should any hand-rolled version.
Recap
This lesson in brief:
- Classification as inference
- Score each class by prior × likelihood and take the argmax; the normaliser drops out of the comparison.
- The independence assumption
- Features independent given the class makes counting sufficient — false almost everywhere, yet the argmax often survives.
- Learning is counting
- Priors are class shares, likelihoods are smoothed word frequencies, and one unseen word without smoothing vetoes a class.
- Trust the ranking first
- Correlated evidence saturates the scores toward 0 and 1 — a well-ranking, badly-calibrated model is the default outcome.
Practise Naive Bayes
Reading is free and needs no account. Practice, mocks and progress live in the app.
- 2 quick checks with worked explanations
- Timed mocks scored with the real marking scheme
- Readiness tracked per topic, kept on your device