朴素贝叶斯方法(naïve Bayes) 是一种以贝叶斯定理为基础,并假设各特征在条件上相互独立的分类方法。在处理训练数据集时,该方法首先从特征的独立性假设出发,来学习输入与输出之间的联合概率分布。得到这个模型后,它采用贝叶斯定理来对任意新输入的数据点 x,计算并找出具有最高后验概率的目标类别 y。由于其实现的简洁性和在学习及预测过程中展现出的高效率,朴素贝叶斯成为了一种广泛使用的算法。

Pod 7.1 朴素贝叶斯法简介

假设输入空间 \mathcal{X} \subseteq \boldsymbol{R}^{n} 是一个由n 维向量组成的集合,而输出空间是类标记集合 \mathcal{Y}=\{c_{1}, c_{2}, \cdots, c_{K}\}。这里,输入是特征向量 x \in \mathcal{X},输出是类标记 (class label) y \in \mathcal{Y}XY 分别是定义在输入空间 \mathcal{X} 和输出空间 \mathcal{Y} 上的随机变量。P(X, Y) 代表 XY 的联合概率分布。训练数据集

\begin{equation} T=\{\left(x_{1}, y_{1}\right),\left(x_{2}, y_{2}\right), \cdots,\left(x_{N}, y_{N}\right)\} \end{equation}

P(X, Y) 独立同分布产生。

朴素贝叶斯法通过训练数据集来学习联合概率分布 P(X, Y)。具体来说,它学习以下先验概率分布及条件概率分布。先验概率分布为

\begin{equation} P(Y=c_{k}), \quad k=1,2, \cdots, K \end{equation}

而条件概率分布为

\begin{equation} P(X=x \mid Y=c_{k})=P(X^{(1)}=x^{(1)}, \cdots, X^{(n)}=x^{(n)} \mid Y=c_{k}), \quad k=1,2, \cdots, K \end{equation}

这样便得到了联合概率分布 P(X, Y)

由于条件概率分布 P(X=x \mid Y=c_{k}) 涉及指数级数量的参数,其估计实际上是不可行的。例如,假设 x^{(j)} 的可能取值有 S_{j} 个,j=1,2, \cdots, n, 而 Y 的可能取值有 K 个,那么参数个数将达到 K \prod_{j=1}^{n} S_{j}

朴素贝叶斯法对条件概率分布作出了条件独立性假设,这是一个相对较强的假设,也是“朴素”这个名称的由来。具体来说,条件独立性假设表述为

\begin{equation} P(X=x \mid Y=c_{k}) = \prod_{j=1}^{n} P(X^{(j)}=x^{(j)} \mid Y=c_{k}) \end{equation}

朴素贝叶斯法实际上是学习生成数据的机制,因此属于生成模型。条件独立性假设意味着用于分类的特征在类别确定的条件下都是相互独立的。这一假设简化了朴素贝叶斯法的计算过程,但有时也会牺牲一定的分类准确率。

在分类时,朴素贝叶斯法会根据学习到的模型计算输入 x 的后验概率分布 P(Y=c_{k} \mid X=x),然后选择后验概率最大的类别作为 x 的类输出。后验概率的计算基于贝叶斯定理:

\begin{equation} P(Y=c_{k} \mid X=x)=\frac{P(X=x \mid Y=c_{k}) P(Y=c_{k})}{\sum_{k} P(X=x \mid Y=c_{k}) P(Y=c_{k})} \end{equation}

代入式 (4) 到式 (5),得到:

\begin{equation} P(Y=c_{k} \mid X=x)=\frac{P(Y=c_{k}) \prod_{j} P(X^{(j)}=x^{(j)} \mid Y=c_{k})}{\sum_{k} P(Y=c_{k}) \prod_{j} P(X^{(j)}=x^{(j)} \mid Y=c_{k})}, \quad k=1,2, \cdots, K \end{equation}

这是朴素贝叶斯法分类的基本公式。因此,朴素贝叶斯分类器可表示为:

\begin{equation} y=f(x)=\arg \max _{c_{k}} \frac{P(Y=c_{k}) \prod_{j} P(X^{(j)}=x^{(j)} \mid Y=c_{k})}{\sum_{k} P(Y=c_{k}) \prod_{j} P(X^{(j)}=x^{(j)} \mid Y=c_{k})} \end{equation}

由于在式 (7) 中的分母对所有的 c_{k} 都是相同的,因此可以简化为:

\begin{equation} y=\arg \max _{c_{k}} P(Y=c_{k}) \prod_{j} P(X^{(j)}=x^{(j)} \mid Y=c_{k}) \end{equation}

Pod 7.2 朴素贝叶斯法的实现

在朴素贝叶斯法中,学习的过程包括估计先验概率 P(Y=c_{k}) 和条件概率 P(X^{(j)}=x^{(j)} \mid Y=c_{k})。这些概率可通过极大似然估计法来估计。先验概率 P(Y=c_{k}) 的极大似然估计为:

\begin{equation} P(Y=c_{k})=\frac{\sum_{i=1}^{N} I(y_{i}=c_{k})}{N}, \quad k=1,2, \cdots, K \end{equation}

其中,第 j 个特征 x^{(j)} 的可能取值集合为 \{a_{j 1}, a_{j 2}, \cdots, a_{j S_{j}}\}。相应地,条件概率 P(X^{(j)}=a_{j l} \mid Y=c_{k}) 的极大似然估计为:

\begin{equation} \begin{aligned} P(X^{(j)}=a_{j l} \mid Y=c_{k})=\frac{\sum_{i=1}^{N} I(x_{i}^{(j)}=a_{j l}, y_{i}=c_{k})}{\sum_{i=1}^{N} I(y_{i}=c_{k})}, \\ j=1,2, \cdots, n, \quad l=1,2, \cdots, S_{j}, \quad k=1,2, \cdots, K \end{aligned} \end{equation}

这里,x_{i}^{(j)} 是第 i 个样本的第 j 个特征,a_{j l} 是第 j 个特征可能的第 l 个值,而 I 是指示函数。

整个算法流程如下:

输入: 训练数据集 T=\{(x_{1}, y_{1}), (x_{2}, y_{2}), \cdots, (x_{N}, y_{N})\},其中 x_{i}=(x_{i}^{(1)}, x_{i}^{(2)}, \cdots, x_{i}^{(n)})^{\mathrm{T}}x_{i}^{(j)} 是第 i 个样本的第 j 个特征,x_{i}^{(j)} \in \{a_{j 1}, a_{j 2}, \cdots, a_{j S_{j}}\}a_{j l} 是第 j 个特征可能的第 l 个值,y_{i} \in \{c_{1}, c_{2}, \cdots, c_{K}\};实例 x

输出: 实例 x 的分类。

1. 计算先验概率及条件概率:

\begin{equation} \begin{aligned} P(Y=c_{k})=\frac{\sum_{i=1}^{N} I(y_{i}=c_{k})}{N}, \quad k=1,2, \cdots, K \\ P(X^{(j)}=a_{j l} \mid Y=c_{k})=\frac{\sum_{i=1}^{N} I(x_{i}^{(j)}=a_{j l}, y_{i}=c_{k})}{\sum_{i=1}^{N} I(y_{i}=c_{k})}, \\ j=1,2, \cdots, n, \quad l=1,2, \cdots, S_{j}, \quad k=1,2, \cdots, K \end{aligned} \end{equation}

2. 对于给定的实例 x=(x^{(1)}, x^{(2)}, \cdots, x^{(n)})^{\mathrm{T}},计算:

\begin{equation} P(Y=c_{k}) \prod_{j=1}^{n} P(X^{(j)}=x^{(j)} \mid Y=c_{k}), \quad k=1,2, \cdots, K \end{equation}

3. 确定实例 x 的类:

\begin{equation} y=\arg \max _{c_{k}} P(Y=c_{k}) \prod_{j=1}^{n} P(X^{(j)}=x^{(j)} \mid Y=c_{k}) \end{equation}

当使用极大似然估计法时,可能会遇到要估计的概率值为0的情况,这会影响后验概率的计算并导致分类偏差。为解决这个问题,可以采用贝叶斯估计方法。具体来说,条件概率的贝叶斯估计公式为:

\begin{equation} P_{\lambda}(X^{(j)}=a_{jl} \mid Y=c_{k})=\frac{\sum_{i=1}^{N} I(x_{i}^{(j)}=a_{jl}, y_{i}=c_{k}) + \lambda}{\sum_{i=1}^{N} I(y_{i}=c_{k}) + S_{j} \lambda} \end{equation}

这里,\lambda \geqslant 0 是一个正数,它被加到随机变量各个取值的频数上。当 \lambda=0 时,该估计等同于极大似然估计。通常选择 \lambda=1,这种情况被称为拉普拉斯平滑(Laplacian smoothing)。显然,对于任何 l=1,2, \cdots, S_{j}, k=1,2, \cdots, K,我们有:

\begin{equation} \begin{aligned} & P_{\lambda}(X^{(j)}=a_{jl} \mid Y=c_{k}) > 0 \\ & \sum_{l=1}^{S_{j}} P_{\lambda}(X^{(j)}=a_{jl} \mid Y=c_{k}) = 1 \end{aligned} \end{equation}

这表明式 (14) 确实构成了一种概率分布。同样,先验概率的贝叶斯估计公式为:

\begin{equation} P_{\lambda}(Y=c_{k})=\frac{\sum_{i=1}^{N} I(y_{i}=c_{k}) + \lambda}{N + K \lambda} \end{equation}

通过引入这种平滑机制,我们可以确保即使在某些特征值在训练集中未出现的情况下,也不会产生零概率的问题,从而提高了朴素贝叶斯分类器的稳健性。

Pod 7.3 朴素贝叶斯法的Python实现

要用Python实现上述朴素贝叶斯算法,我们需要遵循算法的主要步骤。这包括计算先验概率 P(Y=c_k),计算条件概率 P(X^{(j)}=a_{jl} \mid Y=c_k),以及使用这些概率来对新实例进行分类。下面是这个算法的一个简单实现:

import numpy as np

class NaiveBayesClassifier:
    def __init__(self):
        self.classes = None
        self.feature_probs = None
        self.class_probs = None

    def fit(self, X, y):
        """
        训练朴素贝叶斯分类器
        X: 特征数据
        y: 目标标签
        """
        self.classes = np.unique(y)
        n_features = X.shape[1]
        
        # 初始化存储条件概率和先验概率的字典
        self.feature_probs = {c: {j: {} for j in range(n_features)} for c in self.classes}
        self.class_probs = dict.fromkeys(self.classes, 0)

        # 计算先验概率和条件概率
        for c in self.classes:
            X_c = X[y == c]
            self.class_probs[c] = len(X_c) / len(X)
            for j in range(n_features):
                feature_values, counts = np.unique(X_c[:, j], return_counts=True)
                total = counts.sum()
                for value, count in zip(feature_values, counts):
                    self.feature_probs[c][j][value] = count / total

    def predict(self, X):
        """
        对新数据进行分类
        X: 新数据
        """
        predictions = []
        for x in X:
            class_scores = {c: np.log(self.class_probs[c]) for c in self.classes}
            for c in self.classes:
                for j, value in enumerate(x):
                    class_scores[c] += np.log(self.feature_probs[c][j].get(value, 1e-6))
            predictions.append(max(class_scores, key=class_scores.get))
        return predictions

这个实现仅适用于离散特征。对于连续特征,需要修改条件概率的计算方法,通常使用高斯分布来估计。为了防止概率乘积下溢,我们使用对数概率。在条件概率中,使用了一个小的平滑值 1e-6 以避免零概率问题。