Gaussian Naive Bayes - Supervised Learning
Implement Gaussian Naive Bayes in Python with NumPy. medium exercise with examples, hints, and instant feedback. No setup or signup required.
Gaussian Naive Bayes
Implement gaussian_nb_fit and gaussian_nb_predict_proba: fit one Gaussian per class and feature, then turn new points into class probabilities with Bayes' rule, computed in log space.
Background
Naive Bayes assumes the features are independent given the class, so the class-conditional density factorises into one 1D density per feature. The assumption is rarely true, yet the classifier is fast, needs little data and is a classic text and tabular baseline. The interview content is in the numerics: multiplying many small densities underflows, so everything happens in log space with a logsumexp.
Fit
For each class c in ascending order (np.unique(y)), with n_c samples of class c out of n:
prior[c] = n_c / n
mean[c, j] = mean of feature j over the samples of class c
var[c, j] = variance (ddof=0) of feature j over class c + epsilon
epsilon = var_smoothing * max_j Var(X[:, j]) # computed once, over ALL samples
epsilon keeps variances away from zero (a feature that is constant within a class would otherwise divide by zero). These are exactly the conventions of scikit-learn's GaussianNB(var_smoothing=1e-9): priors = class_prior_, means = theta_, vars = var_.
Predict
The joint log-likelihood of sample x (shape (d,)) and class c:
jll[c] = log prior[c] - 0.5 * sum_j log(2*pi*var[c, j])
- 0.5 * sum_j (x_j - mean[c, j])^2 / var[c, j]
Normalise with logsumexp to get the posterior:
log P(c | x) = jll[c] - logsumexp_c'(jll[c'])
logsumexp(a) = max(a) + log(sum(exp(a - max(a))))
Inputs
X: float array(n, d)for fitting,(m, d)for predictiony: class labels(n,)var_smoothing: float, default1e-9
Expected Return Format
gaussian_nb_fit returns a dictionary with these keys:
'classes': sorted unique labels, shape(C,)'priors': class priors, shape(C,)'means': per-class feature means, shape(C, d)'vars': per-class feature variances including epsilon, shape(C, d)
gaussian_nb_predict_proba returns an array of shape (m, C) whose rows sum to 1, with columns in the order of model['classes'].
Edge Cases and Conventions
- Use the population variance (
ddof=0), as sklearn does. - Never multiply raw densities: with many features, or a point far from every class, they underflow to
0and you get0/0 = nan. In log space a point atx = -500still gets a clean[1.0, 0.0].
Constraints
- NumPy only; no sklearn or scipy. Vectorise prediction over samples and classes with broadcasting.
Follow-up
- Where does the "naive" assumption hurt most (duplicated or strongly correlated features)?
- How do multinomial and Bernoulli Naive Bayes differ, and why do they need Laplace smoothing instead of
var_smoothing?
Examples
Input: gaussian_nb_fit(np.array([[1.0], [2.0], [3.0], [6.0], [7.0], [8.0]]), np.array([0, 0, 0, 1, 1, 1])) Output: priors [0.5, 0.5], means [[2.0], [7.0]], vars ≈ [[0.6667], [0.6667]]
Each class has 3 points with variance 2/3; epsilon = 1e-9 * Var(X) is negligible here.
Input: gaussian_nb_predict_proba(model, np.array([[4.5]])) Output: array([[0.5, 0.5]])
4.5 is equidistant from both means, and the classes have equal priors and variances.
Practice in Python
import numpy as np
def gaussian_nb_fit(X, y, var_smoothing=1e-9):
"""
Fit Gaussian Naive Bayes.
Args:
X: Features, shape (n, d)
y: Class labels, shape (n,)
var_smoothing: Fraction of the largest feature variance added to all variances
Returns:
dict with 'classes' (C,), 'priors' (C,), 'means' (C, d),
'vars' (C, d) (ddof=0 variances plus epsilon)
"""
# Your code here
pass
def gaussian_nb_predict_proba(model, X):
"""
Posterior class probabilities, computed in log space.
Args:
model: dict returned by gaussian_nb_fit
X: Features, shape (m, d)
Returns:
Probabilities, shape (m, C), columns in the order of model['classes'];
rows sum to 1
"""
# Your code here
pass
The interactive editor loads with Python and NumPy. Run your implementation against 7 tests, or use the progressive hints.