目录
  1. 1. 一、引言:从贝叶斯说起
  2. 2. 二、贝叶斯决策论基础
    1. 2.1. 2.1 贝叶斯定理
    2. 2.2. 2.2 朴素贝叶斯分类器
    3. 2.3. 2.3 条件独立假设——“朴素”的来源
    4. 2.4. 2.4 最终的分类决策
    5. 2.5. 2.5 后验概率最大化的含义
  3. 3. 三、参数估计
    1. 3.1. 3.1 极大似然估计(MLE)
    2. 3.2. 3.2 贝叶斯估计与拉普拉斯平滑
    3. 3.3. 3.3 对数域计算:防止下溢
  4. 4. 四、朴素贝叶斯的三种经典模型
    1. 4.1. 4.1 高斯朴素贝叶斯(Gaussian Naive Bayes)
    2. 4.2. 4.2 多项式朴素贝叶斯(Multinomial Naive Bayes)
    3. 4.3. 4.3 伯努利朴素贝叶斯(Bernoulli Naive Bayes)
    4. 4.4. 4.4 补充朴素贝叶斯(Complement Naive Bayes)
    5. 4.5. 4.5 模型对比
  5. 5. 五、垃圾邮件过滤实战
    1. 5.1. 5.1 完整实现
    2. 5.2. 5.2 数据预处理 Pipeline
  6. 6. 六、半朴素贝叶斯分类器
    1. 6.1. 6.1 ODE(One-Dependent Estimator)
    2. 6.2. 6.2 SPODE(Super-Parent ODE)
    3. 6.3. 6.3 AODE(Averaged One-Dependent Estimator)
    4. 6.4. 6.4 TAN(Tree-Augmented Naive Bayes)
  7. 7. 七、朴素贝叶斯的理论分析
    1. 7.1. 7.1 为什么”朴素”假设还能工作?
    2. 7.2. 7.2 渐进性质
    3. 7.3. 7.3 与逻辑回归的关系
  8. 8. 八、面试高频问题
  9. 9. 九、总结
【统计学习方法死磕系列】朴素贝叶斯法

一、引言:从贝叶斯说起

设想你收到一封邮件,标题是”Congratulations! You’ve won $1,000,000!”。你是怎么判断它是垃圾邮件的?你可能在内心快速地估计:这封邮件包含”won”和”$”这样的词,而包含这些词的邮件大概率是垃圾邮件——这就是贝叶斯推理在起作用。

朴素贝叶斯法(Naive Bayes)是机器学习中最经典的分类算法之一。它基于贝叶斯定理和特征条件独立假设,虽”朴素”但有效——尤其对于文本分类任务(如垃圾邮件过滤、情感分析、文档归类),朴素贝叶斯常常能达到出人意料的好效果。

本文将系统讲解朴素贝叶斯的数学原理、参数估计方法、不同的变体模型,并从零开始实现一个垃圾邮件过滤器。

二、贝叶斯决策论基础

2.1 贝叶斯定理

贝叶斯定理是概率论中最优雅的公式之一,它描述了如何用新的证据来更新我们对事件的信念:

$$ P(Y|X) = \frac{P(X|Y) \cdot P(Y)}{P(X)} $$

其中:

  • $P(Y|X)$:后验概率(posterior)——在观察到证据 $X$ 后,$Y$ 成立的概率
  • $P(Y)$:先验概率(prior)——在观察到任何证据之前,我们对 $Y$ 的信念
  • $P(X|Y)$:似然(likelihood)——如果 $Y$ 成立,观察到证据 $X$ 的概率
  • $P(X)$:证据(evidence)或边际似然(marginal likelihood)——用于归一化

2.2 朴素贝叶斯分类器

对于分类问题,我们想找到使后验概率最大的类别:

$$ \hat{y} = \arg\max_{y \in \mathcal{Y}} P(Y = y | X = x) $$

应用贝叶斯定理:

$$ \hat{y} = \arg\max_{y \in \mathcal{Y}} \frac{P(X = x | Y = y) P(Y = y)}{P(X = x)} $$

由于 $P(X = x)$ 对所有类别都是相同的常数,可以省略:

$$ \hat{y} = \arg\max_{y \in \mathcal{Y}} P(X = x | Y = y) P(Y = y) $$

2.3 条件独立假设——“朴素”的来源

问题来了:如何计算 $P(X = x | Y = y)$?$x$ 是一个多维特征向量 $(x^{(1)}, x^{(2)}, \dots, x^{(n)})$。要精确地计算联合条件概率,需要估计所有特征组合的分布,这是一个指数级复杂的任务。

朴素贝叶斯做了一个”强”假设:在给定类别的条件下,所有特征之间是相互独立的

$$ P(X = x | Y = y) = \prod_{j=1}^{n} P(X^{(j)} = x^{(j)} | Y = y) $$

这就是”朴素”(Naive)的含义——这个假设在现实中通常不成立(比如,如果一封邮件包含”lottery”,它也更可能包含”win”),但正是这个假设使得模型易于计算,且在实践中效果惊人地好。

2.4 最终的分类决策

将条件独立假设代入:

$$ \hat{y} = \arg\max_{y \in \mathcal{Y}} P(Y = y) \prod_{j=1}^{n} P(X^{(j)} = x^{(j)} | Y = y) $$

这就是朴素贝叶斯分类器的基本形式。

2.5 后验概率最大化的含义

可以证明,朴素贝叶斯分类器将实例分到后验概率最大的类等价于最小化期望风险(期望损失)。当损失函数是 0-1 损失时,贝叶斯最优分类器(使期望风险最小)就等价于后验概率最大化。

证明思路

在 0-1 损失下,条件风险为:

$$ R(c|x) = \sum_{y} L(y, c) P(y|x) = 1 - P(c|x) $$

最小化条件风险等价于最大化后验概率 $P(c|x)$。

三、参数估计

3.1 极大似然估计(MLE)

先验概率 $P(Y = y_k)$ 的极大似然估计为:

$$ P(Y = y_k) = \frac{\sum_{i=1}^{N} \mathbb{I}(y_i = y_k)}{N} = \frac{N_{y_k}}{N} $$

即类别 $y_k$ 在训练集中出现的频率。

条件概率 $P(X^{(j)} = a_{jl} | Y = y_k)$ 的极大似然估计(离散特征):

$$ P(X^{(j)} = a_{jl} | Y = y_k) = \frac{\sum_{i=1}^{N} \mathbb{I}(x_i^{(j)} = a_{jl}, y_i = y_k)}{\sum_{i=1}^{N} \mathbb{I}(y_i = y_k)} $$

即:在类别为 $y_k$ 的样本中,第 $j$ 个特征取值为 $a_{jl}$ 的样本比例。

3.2 贝叶斯估计与拉普拉斯平滑

MLE 的一个严重问题是:如果某个特征值在训练集的某个类别中从未出现,那么 $P(X^{(j)} = a_{jl} | Y = y_k) = 0$。由于乘法链,整个后验概率变为零——哪怕其他 99 个特征都强烈指向该类别!

拉普拉斯平滑(Laplace smoothing)解决了这个问题。它的核心思想是为每种情况添加一个小的”伪计数” $\lambda$:

$$ P(Y = y_k) = \frac{N_{y_k} + \lambda}{N + K\lambda} $$

$$ P(X^{(j)} = a_{jl} | Y = y_k) = \frac{N_{y_k, a_{jl}} + \lambda}{N_{y_k} + S_j \lambda} $$

其中:

  • $K$ 是类别总数
  • $S_j$ 是第 $j$ 个特征的可能取值数
  • $\lambda > 0$ 是平滑参数

当 $\lambda = 0$ 时,退化为极大似然估计。
当 $\lambda = 1$ 时,即为标准的拉普拉斯平滑(Laplace smoothing)。
当 $0 < \lambda < 1$ 时,称为 Lidstone 平滑。

3.3 对数域计算:防止下溢

在实际应用中,$\prod_j P(X^{(j)} | Y)$ 会导致数值下溢(许多小于 1 的小数相乘)。解决方法是转为对数域:

$$ \hat{y} = \arg\max_{y} \left[ \log P(Y = y) + \sum_{j=1}^{n} \log P(X^{(j)} = x^{(j)} | Y = y) \right] $$

这样,乘法变为加法,数值稳定。

四、朴素贝叶斯的三种经典模型

4.1 高斯朴素贝叶斯(Gaussian Naive Bayes)

适用场景:特征是连续数值型,且近似服从正态分布。

模型假设

$$ P(X^{(j)} = x | Y = y_k) = \frac{1}{\sqrt{2\pi\sigma_{j,k}^2}} \exp\left(-\frac{(x - \mu_{j,k})^2}{2\sigma_{j,k}^2}\right) $$

参数估计

$$ \mu_{j,k} = \frac{1}{N_k} \sum_{i: y_i = k} x_i^{(j)} $$

$$ \sigma^2_{j,k} = \frac{1}{N_k} \sum_{i: y_i = k} (x_i^{(j)} - \mu_{j,k})^2 $$

决策面:高斯朴素贝叶斯的决策面是二次曲面(当各类方差不同时)。当各类共享同一方差时,退化为线性分类器。

4.2 多项式朴素贝叶斯(Multinomial Naive Bayes)

适用场景:特征是计数型数据(如词频),文本分类的标配。

模型假设

$$ P(X = x | Y = y_k) = \frac{(\sum_{j} x^{(j)})!}{\prod_j x^{(j)}!} \prod_j p_{j,k}^{x^{(j)}} $$

实际分类时(忽略与类别无关的阶乘项):

$$ \hat{y} = \arg\max_{k} \left[ \log P(Y = y_k) + \sum_{j} x^{(j)} \log p_{j,k} \right] $$

参数估计(拉普拉斯平滑):

$$ p_{j,k} = \frac{N_{j,k} + \alpha}{N_k + \alpha \cdot V} $$

其中 $N_{j,k}$ 是词 $j$ 在类别 $k$ 中出现的总次数,$N_k$ 是类别 $k$ 中的总词数,$V$ 是词汇表大小,$\alpha$ 是平滑参数(通常 $\alpha = 1$)。

4.3 伯努利朴素贝叶斯(Bernoulli Naive Bayes)

适用场景:特征是二值(0/1)的,表示某个词是否出现,而不是出现次数。

模型假设

$$ P(X^{(j)} = x^{(j)} | Y = y_k) = p_{j,k}^{x^{(j)}} (1 - p_{j,k})^{(1 - x^{(j)})} $$

其中 $x^{(j)} \in {0, 1}$。

参数估计

$$ p_{j,k} = \frac{N_{j,k} + \alpha}{N_k + 2\alpha} $$

其中 $N_{j,k}$ 是类别 $k$ 中包含词 $j$ 的文档数。

伯努利 vs 多项式

  • 伯努利关注”词是否出现”(0/1),对短文本(如 tweet)效果更好
  • 多项式关注”词出现几次”(计数),对长文本(如长邮件、新闻文章)效果更好
  • 伯努利需要显式建模”特征缺失”($x^{(j)} = 0$),所以对缺失的特征也要贡献概率

4.4 补充朴素贝叶斯(Complement Naive Bayes)

标准多项式 NB 在处理不平衡数据时倾向于多数类。Complement NB 通过使用补类(即”非该类别”)的数据来估计参数:

$$ \hat{\theta}_{j,k} = \frac{\alpha + \sum_{i: y_i \neq k} x_i^{(j)}}{\alpha \cdot V + \sum_{j'} \sum_{i: y_i \neq k} x_i^{(j')}} $$

分类时选择”补类参数下的权重和”最小的类别:

$$ \hat{y} = \arg\min_{k} \sum_{j} x^{(j)} \log \hat{\theta}_{j,k} $$

Complement NB 在文本分类中经常优于标准多项式 NB,尤其当类别分布不均时。

4.5 模型对比

模型 特征类型 经典应用 优点 缺点
Gaussian NB 连续值 鸢尾花分类 简单 假设正态分布
Multinomial NB 计数(词频) 文本分类、垃圾邮件过滤 适合长文本 对短文本一般
Bernoulli NB 二值(0/1) 短文本分类、情感分析 对短文本好 忽略词频信息
Complement NB 计数 不平衡文本分类 处理不平衡 非标准实现

五、垃圾邮件过滤实战

5.1 完整实现

import numpy as np
from collections import defaultdict

class MultinomialNaiveBayes:
def __init__(self, alpha=1.0):
self.alpha = alpha # Laplace smoothing parameter
self.class_log_prior = {}
self.feature_log_prob = {}
self.classes = []
self.vocab = set()

def fit(self, X, y):
"""
X: list of tokenized documents (list of str)
y: list of labels
"""
n_samples = len(X)
self.classes = list(set(y))
n_features = len(set([word for doc in X for word in doc]))
self.vocab = set([word for doc in X for word in doc])

# Count features per class
class_count = defaultdict(int)
feature_count = defaultdict(lambda: defaultdict(int))

for doc, label in zip(X, y):
class_count[label] += 1
for word in doc:
feature_count[label][word] += 1

# Compute log priors and log likelihoods
for c in self.classes:
n_c = class_count[c]
self.class_log_prior[c] = np.log(n_c / n_samples)

# Total word count in class c
total_count = sum(feature_count[c].values())

# Log probability for each word
self.feature_log_prob[c] = {}
for word in self.vocab:
word_count = feature_count[c][word]
self.feature_log_prob[c][word] = np.log(
(word_count + self.alpha) /
(total_count + self.alpha * len(self.vocab))
)

def predict_one(self, doc):
"""Predict class for a single document"""
best_class = None
best_score = -float('inf')

for c in self.classes:
# Start with log prior
score = self.class_log_prior[c]

# Add log likelihood for each word (skip OOV words)
for word in doc:
if word in self.vocab:
score += self.feature_log_prob[c][word]

if score > best_score:
best_score = score
best_class = c

return best_class

def predict(self, X):
return [self.predict_one(doc) for doc in X]

5.2 数据预处理 Pipeline

import re
import jieba

def preprocess_email(text):
"""Preprocess a raw email text"""
# 1. Lowercase
text = text.lower()

# 2. Remove HTML tags
text = re.sub(r'<[^>]+>', '', text)

# 3. Remove punctuation and digits
text = re.sub(r'[^\w\s]', '', text)
text = re.sub(r'\d+', '', text)

# 4. Tokenize (Chinese: use jieba; English: split by whitespace)
# For English:
tokens = text.split()
# For Chinese: tokens = jieba.lcut(text)

# 5. Remove stop words
stop_words = {'the', 'a', 'an', 'is', 'are', 'and', 'or', 'but', 'in', 'on', 'at'}
tokens = [t for t in tokens if t not in stop_words and len(t) > 1]

return tokens

# Example usage
emails = [
"Congratulations! You've won a free iPhone! Click here now!",
"Meeting reminder: team sync at 3pm tomorrow",
]

# Preprocess
X = [preprocess_email(e) for e in emails]
y = ['spam', 'ham']

# Train and predict
nb = MultinomialNaiveBayes(alpha=1.0)
nb.fit(X, y)

六、半朴素贝叶斯分类器

条件独立假设过于严格。为了在”完全依赖”(指数复杂)和”完全独立”(过于朴素)之间取得平衡,研究者提出了若干半朴素贝叶斯分类器

6.1 ODE(One-Dependent Estimator)

不再假设所有特征独立,而是假设每个特征最多依赖于一个其他特征(”父特征”)。

$$ P(y|x) \propto P(y) \prod_{j=1}^{n} P(x^{(j)} | y, x^{(pa(j))}) $$

其中 $pa(j)$ 是特征 $j$ 的父特征的索引。

6.2 SPODE(Super-Parent ODE)

SPODE 假设所有特征共享同一个父特征(super-parent node):

$$ P(y|x) \propto P(y) \prod_{j=1}^{n} P(x^{(j)} | y, x^{(s)}) $$

6.3 AODE(Averaged One-Dependent Estimator)

AODE 是 SPODE 的集成版本,它将多个以不同特征为超父的 SPODE 平均起来:

$$ P(y|x) \propto \sum_{s: N_s \geq m} P(y, x_s) \prod_{j \neq s} P(x^{(j)} | y, x^{(s)}) $$

其中 $N_s$ 是特征 $s$ 的取值在训练集中出现的次数,$m$ 是阈值(通常为 30)。

AODE 在保持高效率的同时,显著提升了标准 NB 的精度。

6.4 TAN(Tree-Augmented Naive Bayes)

TAN 放宽了 NB 的特征独立性假设,允许每个特征(除类别节点外)最多依赖一个其他特征。依赖关系通过最大权重生成树学习:

  1. 计算任意两特征在各类别下的条件互信息:
    $$ I(X^{(i)}, X^{(j)} | Y) = \sum_{y} P(y) \sum_{x_i, x_j} P(x_i, x_j | y) \log \frac{P(x_i, x_j | y)}{P(x_i | y)P(x_j | y)} $$

  2. 以条件互信息为边权,构建完全图

  3. 求最大生成树

  4. 在树结构上估计条件概率

TAN 在计算复杂度(仅比 NB 多一个生成树步骤)和模型精度之间取得了良好平衡。

七、朴素贝叶斯的理论分析

7.1 为什么”朴素”假设还能工作?

即使条件独立假设在现实中不成立,朴素贝叶斯仍常常表现出色。原因有:

  1. 概率排序而非精确估计:分类只需要后验概率的排序正确,而不需要概率值精确。即使 $P(y|x)$ 的估计值有偏,只要大小顺序正确,分类就正确。

  2. 零一损失下的鲁棒性:研究表明,只要条件概率的变化不是极端剧烈的,NB 的分类结果就可能正确,即使概率估计的绝对值不准确。

  3. 偏差-方差视角:条件独立假设引入了高偏差,但同时也大幅降低了方差(参数数量极大减少)。在数据量不足时,高偏差低方差的模型往往比低偏差高方差的模型表现更好。

7.2 渐进性质

朴素贝叶斯是一致估计(consistent estimator)吗?不是——当数据趋于无穷时,NB 可能不会收敛到真实的贝叶斯最优分类器,因为其条件独立假设本身就是错误的。

但它是一个高效的分类器(efficient classifier):在有限样本下,NB 往往优于需要更多数据才能充分训练的复杂模型(如全贝叶斯网络)。

7.3 与逻辑回归的关系

有趣的是,高斯朴素贝叶斯与逻辑回归的决策面形式相同(都是线性的),但它们的参数估计方式不同:

  • NB 通过生成式方法(model $P(X|Y)$ and $P(Y)$)来间接得到 $P(Y|X)$
  • 逻辑回归通过判别式方法直接建模 $P(Y|X)$

当样本量小时,NB 通常优于逻辑回归(利用了生成式假设);当样本量大时,逻辑回归通常更好(不需要条件独立的假设束缚)。

八、面试高频问题

Q1:朴素贝叶斯的”朴素”体现在哪里?这个假设为什么是必要的?

“朴素”体现在特征条件独立假设:给定类别后,所有特征相互独立。这一假设的必要性在于:如果没有它,我们需要估计 $P(X_1, X_2, \dots, X_n | Y)$ 的联合分布,其参数数量为 $O(K \cdot \prod_j S_j)$($K$ 为类别数,$S_j$ 为特征 $j$ 的取值数)。在 $n$ 个特征、每个特征 $S$ 种取值的情况下,参数数量是 $K \cdot S^n$,这是一个指数级增长的量。

而条件独立假设将联合分布分解为 $n$ 个单变量分布的乘积,参数数量降为 $O(K \cdot \sum_j S_j)$,使得模型在有限数据下变得可行。

Q2:拉普拉斯平滑解决了什么问题?如果 $\lambda$ 选得过大或过小会怎样?

拉普拉斯平滑解决零概率问题:当训练集中某个特征值在某类别下从未出现时,MLE 会给出 $P(X^{(j)}|Y_k) = 0$,使得整个后验概率的乘积为零。

  • $\lambda$ 过大:估计趋向均匀分布,先验信息过度主导,模型区分度下降
  • $\lambda$ 过小:对零概率的修正不够,仍可能出现接近零的概率

最佳 $\lambda$ 通常通过交叉验证选择。对文本分类,$\lambda = 1$ 是安全且普遍有效的默认值。

Q3:多项式 NB 和伯努利 NB 的核心区别是什么?如何选择?

两者的核心区别在于如何处理词频:

  • 多项式 NB:将每个词的出现视为独立事件,考虑出现次数,分类似然基于多项分布
  • 伯努利 NB:只关心词是否出现(0 或 1),分类似然基于伯努利分布

选择指南

  • 文档越长,多项式 NB 越有优势(词频信息有价值)
  • 文档越短(如短信、tweet),伯努利 NB 越有优势(出现本身就是信号)
  • 伯努利 NB 需要显式惩罚缺失的特征($P(X_j=0|Y)$ 是有意义的概率贡献)

Q4:如何理解朴素贝叶斯是生成式模型(generative model)?

生成式模型”试图理解数据是如何生成的”。朴素贝叶斯学习的是:

  1. 各类别的先验概率 $P(Y)$
  2. 各类别下特征的生成概率 $P(X|Y)$

给定一个标签 $Y = y$,朴素贝叶斯可以”生成”一个特征向量——先按 $P(Y)$ 选类别,再按 $P(X^{(j)}|Y)$ 为每个特征生成值。

与之对比,判别式模型(如逻辑回归)直接学习决策边界 $P(Y|X)$,不关心 $P(X|Y)$ 的结构。生成式模型通常需要更强的假设(如条件独立),但在数据较少时可以利用这些假设获得更好的泛化能力。

Q5:如何使用朴素贝叶斯进行半监督学习?

可以利用大量无标签数据提升 NB 的性能:

  1. 自训练(Self-training)

    • 用少量有标签数据训练初始 NB
    • 用训练好的 NB 对无标签数据进行预测
    • 选择高置信度的预测结果,将无标签样本连同预测标签加入训练集
    • 重新训练,重复直到收敛
  2. EM 算法(将无标签数据的标签视为隐变量):

    • E 步:用当前参数估计无标签样本的类别后验概率
    • M 步:用带权重的样本(有标签+软标签的无标签数据)重新估计参数

自训练方法实现简单,在许多实际场景中有效。

九、总结

朴素贝叶斯法是一个精巧而强大的分类器。它的力量来源于一个看似粗糙的假设——条件独立——这个假设在现实中几乎从不成立,却意外地使模型不仅可行,而且在许多任务上表现出色。

朴素贝叶斯教会我们一个重要的机器学习哲学:有时候,一个强大的简化假设比一个复杂的、需要更多数据才能拟合的”正确”模型更有价值。在有标签数据稀缺的领域(如快速搭建的原型系统、对罕见类别的检测),朴素贝叶斯仍然是一个不可或缺的工具。

从更宏观的视角看,朴素贝叶斯也是理解贝叶斯推理的入门之选。通过它,我们不仅学到了一个具体的算法,更掌握了贝叶斯思维的核心——用先验信念和新证据共同做出决策。

参考文献

  1. Lewis, D. D. (1998). Naive (Bayes) at forty: The independence assumption in information retrieval. ECML.
  2. McCallum, A., & Nigam, K. (1998). A comparison of event models for naive Bayes text classification. AAAI Workshop on Learning for Text Categorization.
  3. 李航. (2019). 《统计学习方法》(第2版). 第4章.
  4. Rennie, J. D., Shih, L., Teevan, J., & Karger, D. R. (2003). Tackling the poor assumptions of naive Bayes text classifiers. ICML.
  5. Friedman, N., Geiger, D., & Goldszmidt, M. (1997). Bayesian network classifiers. Machine Learning, 29(2-3), 131-163.
文章作者: Leo·Cheung
文章链接: http://tufusi.com/2021/08/25/%E3%80%90%E7%BB%9F%E8%AE%A1%E5%AD%A6%E4%B9%A0%E6%96%B9%E6%B3%95%E6%AD%BB%E7%A3%95%E7%B3%BB%E5%88%97%E3%80%91%E6%9C%B4%E7%B4%A0%E8%B4%9D%E5%8F%B6%E6%96%AF%E6%B3%95/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 ONE·PIECE
打赏
  • 微信
  • 支付宝

评论