Naive Bayes › Visualizing Naive Bayes
Machine Learning from ScratchAll chapters Naive Bayes › Visualizing Naive BayesSection view · Sections in this chapter5 / 10 ↓
Reading tools Definitions, links & notes
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
The first figure shows decision regions of a two-dimensional Gaussian naive Bayes classifier. Each class gets its own mean and variance for each feature. The background color is the predicted probability of class 1. The decision boundary is where the two class scores tie. Because the class variances differ, the boundary can curve. The model is not using the correlation between the two features; it fits two separate one-dimensional Gaussians per class.
PythonIn [5]Expand
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from scipy.special import logsumexp
from sklearn.calibration import calibration_curve
from sklearn.datasets import make_classification
from sklearn.metrics import accuracy_score, brier_score_loss, log_loss
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import GaussianNB, MultinomialNB
# Generate a two-feature, two-class problem for the decision-region figure.
X_vis, y_vis = make_classification(
n_samples=300,
n_features=2,
n_informative=2,
n_redundant=0,
n_classes=2,
n_clusters_per_class=1,
class_sep=1.2,
flip_y=0.02,
random_state=42,
)
clf_vis = GaussianNB().fit(X_vis, y_vis)
# Build a grid over the feature space and compute the predicted probability
# of class 1 at each grid point for the decision-region figure.
x_min, x_max = X_vis[:, 0].min() - 0.5, X_vis[:, 0].max() + 0.5
y_min, y_max = X_vis[:, 1].min() - 0.5, X_vis[:, 1].max() + 0.5
xx, yy = np.meshgrid(
np.linspace(x_min, x_max, 300),
np.linspace(y_min, y_max, 300),
)
grid = np.column_stack([xx.ravel(), yy.ravel()])
Z = clf_vis.predict_proba(grid)[:, 1].reshape(xx.shape)VisualizationOut [6]Expand

Subtracting the two Gaussian log scores gives a quadratic expression without feature cross-products. Equal corresponding class variances cancel the quadratic terms, leaving a linear score difference. Unequal variances can produce curved boundaries, as in this fitted example. The shape is restricted, however: the model cannot represent a boundary that depends on a nonlinear interaction between the features beyond what the squared terms allow. If the true classes were arranged in a checkerboard pattern, this model would struggle.
The second figure examines calibration. A classifier is calibrated if, among all observations assigned a predicted probability of , about actually belong to the positive class. Naive Bayes is not guaranteed to satisfy this property. When features are correlated, the model treats them as independent evidence and can become overconfident. The simulation below creates two features that carry nearly the same information. The model treats these strongly overlapping measurements as separate evidence.
PythonIn [7]Expand
# Simulate a latent score, its binary label, and two nearly collinear features.
rng = np.random.default_rng(7)
n_cal = 5000
z = rng.normal(size=n_cal)
p_true = 1.0 / (1.0 + np.exp(-2.0 * z))
y_cal = rng.binomial(1, p_true)
# Feature 2 is almost a copy of feature 1, so the independence assumption fails.
x1 = z + 0.45 * rng.normal(size=n_cal)
x2 = x1 + 0.08 * rng.normal(size=n_cal)
X_cal = np.column_stack([x1, x2])
X_train_cal, X_test_cal, y_train_cal, y_test_cal = train_test_split(
X_cal, y_cal, test_size=0.5, random_state=7, stratify=y_cal
)
clf_cal = GaussianNB().fit(X_train_cal, y_train_cal)
# Predict on the held-out split and collect calibration diagnostics.
p_hat_cal = clf_cal.predict_proba(X_test_cal)[:, 1]
prob_true, prob_pred = calibration_curve(
y_test_cal, p_hat_cal, n_bins=10, strategy="quantile"
)
brier_cal = brier_score_loss(y_test_cal, p_hat_cal)ConsoleOut [8]Expand
Brier score on the simulated test set: 0.184
VisualizationOut [9]Expand

The reliability diagram is a diagnostic, not a proof. In this simulation, the two features are nearly collinear, so the independence assumption is badly violated. The class-conditional marginals are not guaranteed to be Gaussian either, so this single curve does not isolate the effect of dependence. The model can still separate the classes, but its probabilities are not trustworthy as estimates of risk. If we needed calibrated probabilities, we would apply a post-hoc calibration method such as Platt scaling or isotonic regression, which we will discuss in Part XII: Calibration and Prediction Uncertainty.
Comments
Add to the conversation
A question, a correction, or an example from your own work.
No comments yet. Be the first to share your thoughts!