一、朴素贝叶斯法的目标和功能
朴素贝叶斯法是一个分类模型,它的目标是: 根据已知的特征(如单词、数值等),预测一个样本属于哪个类别(如垃圾邮件/正常邮件)。 它通过计算概率来实现分类,核心思想是:
如果一个样本的某些特征在某个类别下出现的概率很高,那么这个样本很可能属于这个类别。
假设我们要判断一封邮件是否是垃圾邮件,邮件中有两个关键词:“免费”和“优惠”。
已知:
- 在历史数据中,80%的垃圾邮件包含“免费”,而正常邮件中只有5%包含“免费”。
- 60%的垃圾邮件包含“优惠”,正常邮件中只有10%包含“优惠”。
- 整体数据中,垃圾邮件的占比是20%(先验概率)。
若给出一份新邮件,其内容包含“免费”和“优惠”,这封邮件是垃圾邮件的概率有多大?
这里先给出一个结论,这封邮件是垃圾邮件的概率为 96%,下一节会再给出详细计算过程。这个概率的计算过程一个贝叶斯分类器的应用过程。
二、朴素贝叶斯法的原理和目标
2.1 贝叶斯定理的数学表达
朴素贝叶斯的核心公式基于贝叶斯定理: $$ P(y=C_k \mid X) = \frac{P(X \mid y=C_k) P(y=C_k)}{P(X)} $$ 其中各个符号的含义说明如下:
- 后验概率 $P(y = C_k \mid X)$ 表示在已知特征 $X$ 的情况下,样本属于类别 $C_k$ 的概率。简单来说,这就是“给定当前数据,属于某个类别的可能性有多大”。
- 先验概率 $P(y = C_k)$ 表示在没有任何特征信息之前,样本属于类别 $C_k$ 的基本概率。这个值通常通过历史数据或者先验知识获得,比如在整个数据集中各类别出现的频率。
- 似然概率 $P(X \mid y = C_k)$ 表示在假设样本属于类别 $C_k$ 的条件下,观测到特征 $X$ 的概率。它反映了某个类别“产生”特定观测数据的可能性。
- 总体概率 $P(X)$ 表示观测到特征 $X$ 的总体概率,表示在所有可能类别下出现特征 $X$ 的总体概率。
实例:利用贝叶斯定理判断垃圾邮件
假设我们有两类邮件:垃圾邮件(spam)和正常邮件(normal)。邮件的特征采用关键词“免费”和“优惠”的存在(取值 1 表示出现,0 表示未出现)。假设已有如下经验数据:
- 垃圾邮件的先验概率:$P(\text{spam}) = 0.2$
- 正常邮件的先验概率:$P(\text{normal}) = 0.8$
以及,在各类别下,关键词出现的条件概率为:
- 对垃圾邮件:$P(\text{free}=1 \mid \text{spam}) = 0.8$,$P(\text{discount}=1 \mid \text{spam}) = 0.6$
- 对正常邮件:$P(\text{free}=1 \mid \text{normal}) = 0.05$,$P(\text{discount}=1 \mid \text{normal}) = 0.1$
假设我们观测到一封邮件,其“免费”和“优惠”均出现,我们可以做如下计算
-
计算是垃圾邮件同时包含“免费”和“优惠”的概率$ P_{sfd} $ $$ \begin{aligned} P_{sfd} &= P(free=1, discount=1 \mid \text{spam} ) \\ & = P(\text{spam}) \times P(\text{free}=1 \mid \text{spam}) \times P(\text{discount}=1 \mid \text{spam}) \\ &= 0.2 \times 0.8 \times 0.6 \\ &= 0.096 \end{aligned} $$
-
计算是正常邮件同时包含“免费”和“优惠”的概率 $ P_{nfd} $ $$ \begin{aligned} P_{nfd} &= P(free=1, discount=1 \mid \text{normal} ) \\ &= P(\text{normal}) \times P(\text{free}=1 \mid \text{normal}) \times P(\text{discount}=1 \mid \text{normal}) \\ &= 0.8 \times 0.05 \times 0.1 \\ &= 0.004 \end{aligned} $$
-
代入贝叶斯定理公式,计算同时包含“免费”和“优惠”的概率时是垃圾邮件的概率 $ P_s $:
$$ \begin{aligned} P_s &= P( \text{spam} \mid free=1, discount=1 ) \\&= \frac{P_{sfd}}{P_{sfd}+P_{nfd}} \\ &= \frac{0.096}{0.096 + 0.004} \\ &= 0.96 \end{aligned} $$
综上可得,同时包含“免费”和“优惠”的概率时是垃圾邮件的概率为 96%。
2.2 条件独立假设
在上一节的计算中,实际上我们做了一个假设,即“免费”和“优惠”出现的概率是独立的。用数学语言描述,就是在给定类别 $y$ 的条件下,各特征 $x_1, x_2, \ldots, x_n$ 彼此相互独立。这一假设将联合概率分解为每个单独特征的条件概率乘积,极大简化了计算: $$ P(X \mid y) = \prod_{i=1}^{n} P(x_i \mid y) $$
虽然在实际垃圾邮件中,不同单词间可能存在关联,但该假设往往能取得不错的分类效果。
当数据特征之间存在强相关性,简单的条件独立假设不再适用时,可以考虑更为灵活的概率模型。贝叶斯网络提供了一种在图结构中描述变量依赖关系的方式,可以视为对朴素贝叶斯模型的扩展,但这部分内容超出了本文的讨论范围。
2.3 模型目标
在了解了朴素贝叶斯法的基本原理和假设后,我们就可以给出朴素贝叶斯法模型的目标了。由于我们计算的是概率,所以对于任何新样本 $X$,我们在计算其在所有类别下的后验概率 $P(y=C_k \mid X)$后,选择后验概率最大的类别作为预测结果。
即,朴素贝叶斯法的决策规则是: $$ \hat{y} = \arg\max_{C_k} P(y=C_k \mid X) $$
在邮件分类的例子中,同时包含“免费”和“优惠”的概率时是垃圾邮件的概率为 96%,远超正常邮件的概率 4%,所以就可以认为它是垃圾邮件。
三、参数估计方法
要实现上述决策规则,需要对两类主要参数进行估计:
-
先验概率 $P(y=C_k)$
-
条件概率 $P(x_i \mid y=C_k)$
3.1 先验概率的参数估计
首先我们估计先验概率。先验概率的计算方式非常简单,我们就可以简单根据训练样本中 $ N_{C_k} $ 占总样本数 $ N$ 的比例即可估计,即 $$ P(y=C_k) =\frac{ N_{C_k} }{N} $$
3.2 条件概率的参数估计
3.2.1 极大似然估计(MLE)
假设特征 $x_i$ 在类别 $C_k$ 下有 $V$ 种可能取值,其条件概率为: $$ P(x_i=v \mid y=C_k) = \frac{n_{v}^{(k)}}{N_{C_k}} $$ 其中 $n_v^{(k)}$ 是类别 $C_k$ 中特征 $x_i$ 取值为 $v$ 的样本数,$ N_{C_k} $ 是类别 $C_k$ 的总样本数。
这样的估计实际上存在一个问题,当 $n_v^{(k)} = 0$ 时,概率估计为零,导致整体后验概率为零。
3.2.2 贝叶斯估计(拉普拉斯平滑)
为了解决前文所说的后验概率为零的问题,我们可以引入平滑参数 $\alpha$(通常取1),修正后的条件概率为: $$ P(x_i=v \mid y=C_k) = \frac{n_v^{(k)} + \alpha}{N_{C_k} + \alpha V} $$ 拉普拉斯平滑等价于假设特征的先验分布为均匀分布(Dirichlet先验),通过引入伪计数避免零概率问题。
3.3 代码实现
3.3.1 工程处理
在实际代码实现中,由于浮点数的精度问题,计算得到的概率值往往很小,为避免数值下溢,我们需要对计算过程进行一些修改,即对后验概率取对数: $$ \log P(y=C_k \mid X) \propto \log P(y=C_k) + \sum_{i=1}^{n} \log P(x_i \mid y=C_k) $$
这样,分类决策规则也变成了:
$$ \hat{y} = \arg\max_{C_k} \left[\log P(y=C_k) + \sum_{i=1}^{n} \log P(x_i \mid y=C_k)\right] $$
3.3.2 Python 代码
import numpy as np
class NavieBayesClassifier:
def __init__(self, alpha=1.0):
"""
参数:
- alpha: 平滑参数。若 alpha=0 则为极大似然估计;若 alpha>0 则采用贝叶斯平滑。
"""
self.alpha = alpha
self.classes = None
self.class_log_prior = None # 对数先验概率
self.feature_log_prob = None # 对数条件概率
def fit(self, X, y):
"""
X: 形状为 (n_samples, n_features) 的离散值数组,
比如每一行表示一封邮件中各关键词出现的次数或是否出现(0/1)。
y: 形状为 (n_samples,) 的标签数组,
标签取值如 0 表示“正常邮件”,1 表示“垃圾邮件”。
"""
X = np.array(X)
y = np.array(y)
self.classes = np.unique(y)
n_classes = len(self.classes)
n_features = X.shape[1]
n_samples = X.shape[0]
# 统计各类别样本数和每个特征的出现次数
class_count = np.zeros(n_classes)
feature_count = np.zeros((n_classes, n_features))
for idx, c in enumerate(self.classes):
X_c = X[y == c]
class_count[idx] = X_c.shape[0]
feature_count[idx] = np.sum(X_c, axis=0)
# 计算对数先验概率
self.class_log_prior = np.log(class_count / n_samples)
# 平滑处理,计算平滑后的条件概率
smoothed_fc = feature_count + self.alpha
smoothed_cc = np.sum(smoothed_fc, axis=1) # 各类别下所有特征总和
self.feature_log_prob = np.log(smoothed_fc) - np.log(smoothed_cc.reshape(-1, 1))
def predict(self, X):
X = np.array(X)
log_likelihood = np.dot(X, self.feature_log_prob.T) + self.class_log_prior
return self.classes[np.argmax(log_likelihood, axis=1)]
# 示例:
# 假设我们有两项离散特征:是否包含“免费”,“优惠”
# 其中 X 的一行为 [1, 1] 表示该邮件同时包含两个关键词
# Y 是一个二项分类,1 表示垃圾邮件,0 表示正常邮件
X_train = [
[1, 1], # 垃圾邮件
[1, 0], # 垃圾邮件
[0, 1], # 垃圾邮件
[0, 0], # 正常邮件
[0, 0] # 正常邮件
]
y_train = [1, 1, 1, 0, 0]
model = NavieBayesClassifier(alpha=1.0)
model.fit(X_train, y_train)
print("预测结果:", model.predict([[1, 1]])) # 输出 1,即垃圾邮件
四、特征值的分布模型
此前我们讨论的特征值时,没有讨论其概率分布模型。实际上在邮件分类的例子中,对于是否包含“免费”和“优惠”这两个特征值,我们一直假设其服从二项分布。
实际上,对于很多特征值,他们不一定服从二项分布。
- 比如身高、体重、分数等无法用二项分布描述,而更适合用正态分布来表达。
- 比如对于单位时间内的访问量,则适合柏松分布来表达。
这里简单给出两个分布的参数估计方法。
3.1 正态分布参数估计
假设连续特征 $x_i$ 在类别 $C_k$ 下服从正态分布: $$ P(x_i \mid y=C_k) = \frac{1}{\sqrt{2\pi\sigma_{k,i}^2}} \exp\left(-\frac{(x_i - \mu_{k,i})^2}{2\sigma_{k,i}^2}\right) $$ 参数估计方法:
- 均值 $\mu_{k,i} $: $$ \mu_{k,i} = \frac{1}{N_{C_k}} \sum_{x \in C_k} x_i $$
- 方差 $\sigma_{k,i}^2$: $$ \sigma_{k,i}^2 = \frac{1}{N_{C_k}} \sum_{x \in C_k} (x_i - \mu_{k,i})^2 + \epsilon $$ 其中 $\epsilon$ 为防止方差为零的修正项
3.2 柏松分布参数估计
假设离散型计数特征 $x_i$ 在类别 $ C_k $下服从柏松分布: $$ P(x_i \mid y=C_k) = \frac{\lambda_{k,i}^{x_i} e^{-\lambda_{k,i}}}{x_i!} $$ 参数估计方法: - 参数 $\lambda_{k,i}$(事件发生率的均值): $$ \lambda_{k,i} = \frac{1}{N_{C_k}} \sum_{x \in C_k} x_i + \epsilon $$ 其中 $ \epsilon $ 是为防止 $ \lambda_{k,i}=0 $ 的修正项。