Naive Bayes
A warm casing and an unstable reading may each suggest a faulty sensor. We need a way to combine those clues, while being explicit about what we assume about their relationship.
14 sections · 4 questions · 3 coding challenges
The arrow keys move between sections too.
Classifying Without a Table for Every Combination
BOTIE
That paper did not invent Bayes' rule or every naive Bayes model. It demonstrated why the way we represent observations matters even within this apparently simple family. We will use invented binary sensor signs, then distinguish that model from counting repeated words.
What If Several Clues Point to the Same Cause?
The warning light was only one clue. Botie can also inspect whether a sensor's casing is warm and whether its readings fluctuate. A faulty sensor might produce both signs, but a working sensor can sometimes produce them too.
We could count every possible combination of signs in each group. As the list of signs grows, many combinations would have very few examples. Let's start with a simpler proposal: learn how often each sign appears within each group, then combine those contributions. We need to understand the assumption that makes that possible.
The class priors and sign probabilities are illustrative; conditional independence is assumed.
Step 1 of 3
Keep the causes separate
These probabilities concern seeing both observed signs, one sign at a time within each cause.
| Possible cause | Warm | Unstable |
|---|---|---|
| Faulty: warm, unstable | 0.8 | 0.7 |
| Working: warm, unstable | 0.1 | 0.2 |
Faulty: warm, unstable
- Warm
- 0.8
- Unstable
- 0.7
Working: warm, unstable
- Warm
- 0.1
- Unstable
- 0.2
Step 2 of 3
Combine within each cause
Assume the signs are independent once the cause is known. Their products give the proposed likelihood of seeing both.
Faulty: 0.8 × 0.7 = 0.56 Working: 0.1 × 0.2 = 0.02
Step 3 of 3
Include how common the causes were
Faulty sensors start with a smaller prior share. Combining the prior and the likelihood, then normalising, gives the updated shares.
| Cause | Priors: faulty, working | Updated shares |
|---|---|---|
| Faulty | 0.2 | 0.875 |
| Working | 0.8 | 0.125 |
Faulty
- Priors: faulty, working
- 0.2
- Updated shares
- 0.875
Working
- Priors: faulty, working
- 0.8
- Updated shares
- 0.125
Combining signs requires a stated independence assumption
Each row keeps one possible cause intact. Multiplication combines the observed signs under the conditional-independence assumption; the prior and total then turn those likelihoods into posterior shares.
Which sensor condition could explain these signs?
Each row describes the same observed signs under a different possible cause. Change one likelihood and follow its effect through the class weight and the final normalisation.
BOTIE
Posterior shares for faulty and working sensors
- 00.875
- 10.125
Follow the operations
1. Faulty and working priors
- 00.2
- 10.8
2. Probabilities of observed signs by cause
0.8 0.7 0.1 0.2 3. Multiply conditional probabilities in each row
- 00.56
- 10.02
4. Weight evidence by prior
- 00.112
- 10.016
5. Add a list
0.128
6. Divide
- 00.875
- 10.125
7. Result
- 00.875
- 10.125
The table already describes observed outcomes. We are composing an inference calculation, not estimating rates or guaranteeing probability calibration.
1. Learn Separate Rates Within Each Labelled Group
A training record contains the observed signs and a known class. For our sensor, a sign is either present or absent. Within the faulty group, count how many sensors show a warm casing; repeat for unstable readings. Then do the same within the working group.
The class frequencies supply priors. Each feature's frequency within a class supplies a conditional probability. Keep the feature order and class order fixed when these values become arrays.
Class order: faulty, working
Feature order: warm, unstable
Priors: [0.2, 0.8]
P(sign present | class):
faulty [0.8, 0.7]
working [0.1, 0.2]These are supplied illustrative rates. Estimating them from labelled observations belongs entirely inside the training split.
2. Name the Independence Assumption
Let's make an assumption we can calculate with. Within each class, the model treats the features as conditionally independent. Once we specify faulty or working, learning that the casing is warm does not change the model's probability of an unstable reading. Across the whole population, the two signs can still be related because both may be more common in faulty sensors.
With that assumption, we can multiply the feature likelihoods for each class. Bayes' rule then brings in the prior class probabilities and lets us normalize the results. Later we will look at what happens when the signs are related even within a class.
Faulty contribution = 0.2 × 0.8 × 0.7 = 0.112
Working contribution = 0.8 × 0.1 × 0.2 = 0.016
Faulty posterior = 0.112 / (0.112 + 0.016) = 0.875Correlated clues can be counted too strongly. A classifier can still rank classes usefully while giving probabilities that are too confident.
3. An Absent Sign Also Supplies Evidence
In the Bernoulli version, every feature has a present-or-absent outcome. If the casing is warm but the reading is stable, use the warm probability and the complement of the unstable probability. Simply dropping absent features changes the model.
A multinomial text model instead describes counts of events such as word occurrences. It does not reuse the Bernoulli formula unchanged.
Observed signs: warm = 1, unstable = 0
Faulty likelihood = 0.8 × (1 - 0.7) = 0.24
Working likelihood = 0.1 × (1 - 0.2) = 0.08In the workshop, the supplied table already contains the probabilities of the outcomes actually observed. You combine its rows rather than guessing which features were present.
4. Keep One Unseen Event From Erasing Everything
A feature absent from a small class-specific training sample would otherwise receive a zero estimated rate. One such factor can erase that class's entire likelihood. Smoothing adds a small count to each possible outcome before dividing.
For a binary feature with one added count for presence and one for absence, both the numerator and denominator change. This is an explicit estimation choice, not evidence that the unseen event really occurred.
Present count = 0; class examples = 4
Smoothed present rate = (0 + 1) / (4 + 2) = 1/6With many features, add log likelihoods to avoid multiplying very small numbers. Fit smoothing choices on training and validation data, not the final test.
5. Check Whether the Simplification Helps
Compare this classifier with a simple class-frequency baseline. Inspect mistakes on new sensors, including cases where the two signs come from the same underlying measurement. Duplicating a feature can amplify its influence without adding information.
If we need reliable probabilities for inspection decisions, evaluate calibration as well as classification. A high posterior from an independence approximation is not a guarantee of correctness.
The calculation remains useful because every contribution is inspectable. Its simplicity also makes its assumptions visible.
Include a missing sign in the calculation
The observation is warm but stable. Each row describes one possible cause.
import numpy as np
prior = np.array([0.2, 0.8])
present = np.array([[0.8, 0.7], [0.1, 0.2]])
x = np.array([1, 0])
terms = np.where(x == 1, present, 1 - present)
scores = prior * terms.prod(axis=1)
print(np.round(scores / scores.sum(), 4).tolist())[0.4286, 0.5714]The stable reading changes the balance. The prior still matters after combining the signs.
6. Keep Fitting and Prediction as Separate Procedures
Fitting produces a class prior and a table of feature probabilities. Prediction receives a new sign vector and reads those saved tables. It must not count the new batch to redefine what a faulty sensor looks like.
For binary signs, every coordinate contributes either the probability of presence or the probability of absence. Add log probabilities to form one score per class. Subtract the largest score before exponentiating and normalising, just as we did for stable probability calculations in NumPy.
7. Fit Two Classes, Then Inspect a New Sensor
The training table below uses warm casing and unstable readings as its two columns. Labels zero and one mean working and faulty. We add one count for each binary outcome inside each class.
The example shows both learned tables and a new prediction. Inspect those tables before trusting the label: a feature order error can preserve every shape while changing the meaning of the evidence.
A complete Bernoulli prediction
The four sensors are an invented training set, too small to establish real diagnostic performance.
import numpy as np
X = np.array([[0,0], [1,0], [1,1], [1,1]])
y = np.array([0,0,1,1])
rate = np.array([(X[y == label].sum(axis=0) + 1) / ((y == label).sum() + 2) for label in [0,1]])
prior = np.array([(y == label).mean() for label in [0,1]])
new = np.array([1,1])
score = np.log(prior) + (new*np.log(rate) + (1-new)*np.log1p(-rate)).sum(axis=1)
probability = np.exp(score-score.max())
probability /= probability.sum()
print(rate.tolist())
print(np.round(probability, 4).tolist())[[0.5, 0.25], [0.75, 0.75]]
[0.1818, 0.8182]Both signs favour the faulty class under the fitted approximation. These probabilities depend on the class priors, smoothing and conditional-independence assumption.
8. Copying a Clue Can Make the Model Overconfident
If the unstable-reading feature is copied into a second column, the model multiplies its evidence twice. It treats the copy as another conditionally independent observation even though no new measurement occurred.
Try that change and compare probabilities before labels. The predicted class may stay the same while confidence becomes more extreme. This is why a useful classifier can still need calibration, and why a high probability is not a substitute for inspecting the feature construction.
Check yourself
Questions on Parts 1 to 8
Question 1 of 4
What likelihood does the model assign to both signs being present?
Use the information below.
Within the faulty class: P(warm)=0.8 and P(unstable)=0.7.
The model assumes conditional independence.Try it with Botie
Build with Botie
Write and run Python