NaĆÆve Bayes classifiers, like LDA and QDA, are generative models. They aim to model how the data was generated for each class and use this knowledge to make predictions. The foundation of NaĆÆve Bayes is Bayesā Theorem.
Bayesā Theorem¶
Imagine youāre trying to assess whether someone is likely to be experiencing anxiety based on a behavioural cue like nail biting. What youāre really after is:
Whatās the probability that a person has anxiety, given that they bite their nails?
In probability notation, we can write this as:
This is called a conditional probability: it expresses how likely one event is (anxiety) given that another has occurred (nail biting).
Now, hereās the tricky part: directly estimating how often people who bite their nails are anxious might be hard. But we might already know a few other things:
What percentage of people in general are anxious:
Among those with anxiety, how common is nail biting:
How common is nail biting in the population overall:
Bayesā Theorem allows us to flip the conditional and compute the probability we care about:
This is useful because:
Directly measuring might be hard.
But we can estimate how common anxiety is in general, and how likely anxious people are to bite their nails.
Bayesā Theorem brings all this together!
What we just did with anxiety and nail biting is exactly what a NaĆÆve Bayes classifier does, just with more features and more classes. In general machine learning notation, we replace:
āAnxietyā with a class label
āNail bitingā (or more features) with a full observation
This gives us the general form of Bayesā Theorem used in classification:
Where:
: Posterior ā the probability of class given the features
: Likelihood ā the probability of seeing those features if the class is
: Prior ā how frequent the class is in general
: Evidence ā the overall probability of seeing (same across all classes)
This is already everything we need to build classifiers that estimate the most likely class based on input features .
The Naïve Assumption¶
To compute the likelihood term ā that is, how likely we are to observe a particular combination of features for a given classāwe usually need a complex model that captures how all the features interact.
NaĆÆve Bayes makes a simplifying assumption:
š” All features are conditionally independent given the class.
Letās return to our example. Suppose youāre trying to predict whether a person is anxious based on multiple behavioural features:
Nail biting (NB)
Fidgeting (FI)
Avoiding eye contact (EC)
In reality, these features are probably not independent. For example, people who are fidgety might also tend to avoid eye contact. However, modelling all these interactions can become very complex, so NaĆÆve Bayes says:
āLetās assume that once we know whether someone is anxious or not, these behaviours donāt influence each other anymore.ā
Mathematically, this means:
This assumption is clearly naĆÆve (we know behaviours are interrelated) but it simplifies things a lot, especially when we have many features. And surprisingly, this assumption often works well enough in practice to make useful predictions!
So in general terms, for a feature vector , the likelihood simplifies to:
The Algorithm¶
To understand how NaĆÆve Bayes works in practice, we will walk through a simple version of the algorithm using categorical (discrete) features: Discrete NaĆÆve Bayes. Here, likelihoods are calculated from frequency tables rather than continuous distributions.
1. Estimate priors¶
Priors are the class probabilities in the training data:
If we assume our training data has 60% anxious (A) and 40% not anxious (NA) people, we have:
2. Estimate class-conditional likelihoods¶
For each feature , estimate the likelihood of observing given class
For our example, let us assume the following likelihoods in the training data:
| Feature | ||
|---|---|---|
| Nail Biting (NB=yes) | 0.8 | 0.3 |
| Fidgeting (FI=yes) | 0.7 | 0.2 |
| Eye Contact (EC=no) | 0.6 | 0.4 |
Note: We use EC = no because weāre modelling avoiding eye contact.
3. Compute the posterior¶
Using Bayesā Theorem:
Suppose we observe a person who bites their nails, is fidgeting, and avoids eye contact. We can then compute the unnormalised posteriors:
Anxious (A):
Not Anxious (NA):
4. Make a prediction¶
For the prediction, we can simply choose the class with the highest posterior probability:
If we compare the posterior scores for our example, we have:
Anxious: 0.2016
Not Anxious: 0.0096
Since , the model predicts:
In case of a tie, the model would likely default to the class with the higher prior. However, this might be subject to the specific implementation of the model.
For the classification, this is already enough. However, we can also normalise the posterior probabilities to sum to 1. For this, we simply divide each score by the total:
Quiz¶
Solution
To solve this, we use the same priors and likelihoods as before, but change the feature values:
NB = no ā use
FI = yes
EC = yes ā so
For Anxious (A):
For Not Anxious (NA):
Result: Both scores are equal -> itās a tie.
Result: Both scores are equal ā itās a tie.
ā Correct answer: Tie
Gaussian Naïve Bayes¶
In Gaussian NaĆÆve Bayes, we assume that the features are continuous and that their values follow a normal distribution for each class.
This means that for each class and each feature :
So the steps of NaĆÆve Bayes for continuous data become:
Estimate class priors: from class proportions
Estimate means and variances and for each feature and class
Plug into the Gaussian formula to compute likelihoods
Multiply likelihoods and prior, and choose the class with the highest posterior
This is essentially what GaussianNB() in scikit-learn does under the hood.
Gaussian Naïve Bayes in Python¶
For a quick illustration, we can use the same simulated data as in the LDA/QDA session:
import numpy as np
from sklearn.datasets import make_classification
from sklearn.naive_bayes import GaussianNB
from sklearn.metrics import classification_report
# Generate synthetic data
X, y = make_classification(n_samples=200, n_features=2, n_informative=2,
n_redundant=0, n_classes=2, n_clusters_per_class=1,
random_state=42);We can then fit the model:
nb = GaussianNB()
nb.fit(X, y)
y_pred = nb.predict(X)
print(classification_report(y, y_pred)) precision recall f1-score support
0 0.87 0.85 0.86 100
1 0.85 0.87 0.86 100
accuracy 0.86 200
macro avg 0.86 0.86 0.86 200
weighted avg 0.86 0.86 0.86 200
And finally plot the predicted distributions and classification report
import seaborn as sns
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
sns.set_theme(style="darkgrid")
fig, ax = plt.subplots()
# Grid for likelihood computation
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xg = np.linspace(x_min, x_max, 300)
yg = np.linspace(y_min, y_max, 200)
xx, yy = np.meshgrid(xg, yg)
Xgrid = np.vstack([xx.ravel(), yy.ravel()]).T
# Plot class densities
for label, color in enumerate(['blue', 'red']):
mask = (y == label)
mu, std = X[mask].mean(0), X[mask].std(0)
P = np.exp(-0.5 * (Xgrid - mu) ** 2 / std ** 2).prod(1)
Pm = np.ma.masked_array(P, P < 0.03)
ax.pcolormesh(xx, yy, Pm.reshape(xx.shape), shading='auto', alpha=0.4, cmap=color.title() + 's')
ax.contour(xx, yy, P.reshape(xx.shape), levels=[0.01, 0.1, 0.5, 0.9], colors=color, alpha=0.2);
# Plot decision boundary
Z = nb.predict(np.c_[xx.ravel(), yy.ravel()]).reshape(xx.shape)
ax.contour(xx, yy, Z, levels=[0.5], linewidths=2, colors='black')
# Scatter plot
ax.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='bwr')
# Legend
ax.set(xlabel="Feature 1", ylabel="Feature 2", title="NaĆÆve Bayes Class Distributions")
legend_elements = [
Line2D([], [], marker='o', linestyle='None', markerfacecolor='blue',
markeredgewidth=0, label='Class 0', markersize=8),
Line2D([], [], marker='o', linestyle='None', markerfacecolor='red',
markeredgewidth=0, label='Class 1', markersize=8),
Line2D([], [], color='black', linestyle='-', linewidth=2, label='Decision boundary')
]
ax.legend(handles=legend_elements, loc="upper left"); # explicit loc: "best" is slow on a dense mesh
This plot visualises how the Gaussian NaĆÆve Bayes model estimates the class distributions:
Each class is modelled as a multivariate Gaussian distribution (with independent features)
The coloured contours represent density levels of the Gaussian likelihoods
The shaded background shows regions of higher probability under each class
The decision boundary between the two classes lies where the posterior probabilities are equal
Summary¶
| Aspect | LDA | QDA | NaĆÆve Bayes |
|---|---|---|---|
| Model type | Generative | Generative | Generative |
| Class distribution | Gaussian, shared covariance | Gaussian, separate covariance per class | Product of independent 1D distributions |
| Assumes feature independence? | ā No | ā No | ā Yes (the ānaĆÆveā assumption) |
| Decision boundary | Linear | Quadratic | Quadratic (for Gaussian NB) |
| When to use | Classes share a similar spread | Classes have clearly different spreads | Many features, text/categorical data, quick baseline |
Thatās it! You can now head to Exercise 6 to apply LDA, QDA, and NaĆÆve Bayes yourself š