朴素贝叶斯 Naïve Bayes = 贝叶斯公式 (Bayes) ➕ 条件独立假设(Naïve)

1. 朴素贝叶斯算法总结

1.1 模型

朴素贝叶斯法是基于贝叶斯定理与特征条件独立假设的分类方法。对于给定的训练数据集,首先基于特征条件独立假设学习输入输出的联合概率分布;然后基于此模型,对给定的输入 \(x\),利用贝叶斯定理求出后验概率最大的输出 \(y\)

设输入空间 \(\mathcal{X} \subseteq \mathbf{R}^n\)\(n\) 维向量的集合,输出空间为类标记集合 \(\mathcal{Y}=\{c_1, c_2, \cdots, c_K\}\)\(X\) 是定义在输入空间 \(\mathcal{X}\) 上的随机向量,\(Y\) 是定义在输出空间 \(\mathcal{Y}\) 上的随机变量。\(P(X, Y)\)\(X\)\(Y\) 的联合概率分布。训练数据集

\[T = \{(x_1, y_1), (x_2, y_2), \cdots, (x_N, y_N)\} \]

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

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

  • 先验概率分布

\[P(Y = c_k), \quad k = 1, 2, \cdots, K \]

  • 条件概率分布

\[P(X = x|Y = c_k) = P(X^{(1)} = x^{(1)}, \cdots, X^{(n)} = x^{(n)}|Y = c_k), \quad k = 1, 2, \cdots, K \]

于是学习到联合概率分布 \(P(X, Y)\)

条件独立性假设是

\[P(X = x|Y = c_k) = \prod_{j = 1}^{n} P(X^{(j)} = x^{(j)}|Y = c_k) \]

这意味着在分类的类别确定的条件下,用于分类的特征之间是条件独立的。

对于给定的输入 \(x\),通过学习到的模型计算后验概率分布 \(P(Y = c_k|X = x)\),将后验概率最大的类作为 \(x\) 的类输出。后验概率计算根据贝叶斯定理进行:

\[P(Y = c_k|X = x) = \frac{P(X = x|Y = c_k)P(Y = c_k)}{\sum_{k = 1}^{K} P(X = x|Y = c_k)P(Y = c_k)} \]

将条件独立性假设代入上式有

\[P(Y = c_k|X = x) = \frac{P(Y = c_k)\prod_{j = 1}^{n} P(X^{(j)} = x^{(j)}|Y = c_k)}{\sum_{k = 1}^{K} P(Y = c_k)\prod_{j = 1}^{n} P(X^{(j)} = x^{(j)}|Y = c_k)}, \quad k = 1, 2, \cdots, K \]

于是,朴素贝叶斯分类器可表示为

\[y = f(x) = \arg \max_{c_k} \frac{P(Y = c_k)\prod_{j = 1}^{n} P(X^{(j)} = x^{(j)}|Y = c_k)}{\sum_{k = 1}^{K} P(Y = c_k)\prod_{j = 1}^{n} P(X^{(j)} = x^{(j)}|Y = c_k)} \]

由于上式分母对所有 \(c_k\) 都是相同的,所以

\[y = \arg \max_{c_k} P(Y = c_k)\prod_{j = 1}^{n} P(X^{(j)} = x^{(j)}|Y = c_k) \]

1.2 策略

朴素贝叶斯法将实例分到后验概率最大的类中,这等价于期望风险最小化。假设选择 0 - 1 损失函数:

\[L(Y, f(X)) = \begin{cases} 1, & Y \neq f(X) \\ 0, & Y = f(X) \end{cases} \]

式中 \(f(X)\) 是分类决策函数。这时,期望风险函数为

\[R_{exp}(f) = E[L(Y, f(X))] \]

期望是对联合分布 \(P(X, Y)\) 取的。由此取条件期望

\[R_{exp}(f) = E_X \sum_{k = 1}^{K} [L(c_k, f(X))]P(c_k|X) \]

为了使期望风险最小化,只需对 \(X = x\) 逐个极小化,由此得到:

\[f(x) = \arg \min_{y \in \mathcal{Y}} \sum_{k = 1}^{K} L(c_k, y)P(c_k|X = x) = \arg \max_{c_k} P(c_k|X = x) \]

这样一来,根据期望风险最小化准则就得到了后验概率最大化准则:

\[f(x) = \arg \max_{c_k} P(Y = c_k|X = x) \]

即朴素贝叶斯法所采用的策略。

1.3 方法

  • 极大似然估计
    • 先验概率 \(P(Y = c_k)\) 的极大似然估计是

\[P(Y = c_k) = \frac{\sum_{i = 1}^{N} I(y_i = c_k)}{N}, \quad k = 1, 2, \cdots, K \]

其中 \(I\) 是指示函数,\(N\) 是样本容量。
- 设第 \(j\) 个特征 \(x^{(j)}\) 可能取值的集合为 \(\{a_{j1}, a_{j2}, \cdots, a_{jS_j}\}\),条件概率 \(P(X^{(j)} = a_{jl}|Y = c_k)\) 的极大似然估计是

\[P(X^{(j)} = a_{jl}|Y = c_k) = \frac{\sum_{i = 1}^{N} I(x_i^{(j)} = a_{jl}, y_i = c_k)}{\sum_{i = 1}^{N} I(y_i = c_k)} \]

其中 \(j = 1, 2, \cdots, n\)\(l = 1, 2, \cdots, S_j\)\(k = 1, 2, \cdots, K\)

  • 贝叶斯估计
    用极大似然估计可能会出现所要估计的概率值为 0 的情况。这时会影响到后验概率的计算结果,使分类产生偏差。解决这一问题的方法是采用贝叶斯估计。具体地,条件概率的贝叶斯估计是

\[P_{\lambda}(X^{(j)} = a_{jl}|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} \]

式中 \(\lambda \geq 0\)。当 \(\lambda = 0\) 时就是极大似然估计。常取 \(\lambda = 1\),这时称为拉普拉斯平滑(Laplace smoothing)。同样,先验概率的贝叶斯估计是

\[P_{\lambda}(Y = c_k) = \frac{\sum_{i = 1}^{N} I(y_i = c_k) + \lambda}{N + K\lambda} \]

2. Python代码实现

import numpy as np
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

class NaiveBayes:
    def __init__(self, lambda_=1):
        self.lambda_ = lambda_  # 贝叶斯估计的平滑参数
        self.prior_probs = {}
        self.cond_probs = {}

    def fit(self, X, y):
        n_samples, n_features = X.shape
        self.classes = np.unique(y)
        num_classes = len(self.classes)

        # 计算先验概率
        # \[P(Y = c_k) = \frac{\sum_{i = 1}^{N} I(y_i = c_k)}{N}, \quad k = 1, 2, \cdots, K\]
        for c in self.classes:
            self.prior_probs[c] = (np.sum(y == c) + self.lambda_) / (n_samples + num_classes * self.lambda_)

        # 计算条件概率
        # \[P(X^{(j)} = a_{jl}|Y = c_k) 
        #     = \frac{\sum_{i = 1}^{N} I(x_i^{(j)} = a_{jl}, y_i = c_k)}{\sum_{i = 1}^{N} I(y_i = c_k)}\]

        for c in self.classes: # 对c类别
            X_c = X[y == c] 
            self.cond_probs[c] = {}
            for j in range(n_features):
                feature_values = np.unique(X[:, j]) # 第j位置取值集合
                num_feature_values = len(feature_values)
                for value in feature_values:
                    # p(j位置取值value | c类别) ~ j位置取值是value的样本数 ➗ c类别的样本数
                    self.cond_probs[c][(j, value)] = (np.sum(X_c[:, j] == value) + self.lambda_) / (
                            len(X_c) + num_feature_values * self.lambda_)

    def predict(self, X):
        # \[y = \arg \max_{c_k} P(Y = c_k)\prod_{j = 1}^{n} P(X^{(j)} = x^{(j)}|Y = c_k)\]
        y_pred = []
        for x in X:
            posteriors = []
            for c in self.classes:
                prior = self.prior_probs[c]
                conditional = 1
                for j, value in enumerate(x):
                    if (j, value) in self.cond_probs[c]:
                        conditional *= self.cond_probs[c][(j, value)]
                    else:
                        # 如果特征值在训练集中未出现过,可根据贝叶斯估计处理
                        feature_values = np.unique(X[:, j])
                        num_feature_values = len(feature_values)
                        # 获取训练集中对应类别的样本数量
                        X_c = self.X_train[self.y_train == c]
                        conditional *= self.lambda_ / (len(X_c) + num_feature_values * self.lambda_)
                posterior = prior * conditional
                posteriors.append(posterior)
            y_pred.append(self.classes[np.argmax(posteriors)])
        return np.array(y_pred)

    def fit_and_store_train(self, X, y):
        self.X_train = X
        self.y_train = y
        self.fit(X, y)


# 生成随机数据
X, y = make_classification(n_samples=1000, n_features=10, n_informative=5, n_redundant=0, random_state=42)

# 离散化特征
num_bins = 15  # 分箱数量
X_discretized = np.zeros_like(X, dtype=int)
for j in range(X.shape[1]):
    bins = np.linspace(np.min(X[:, j]), np.max(X[:, j]), num_bins + 1)
    X_discretized[:, j] = np.digitize(X[:, j], bins)

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X_discretized, y, test_size=0.2, random_state=42)

#print first 10 X and y
print(X_train[:10], y_train[:10])
# 创建朴素贝叶斯分类器并训练
nb = NaiveBayes(lambda_=1)
nb.fit_and_store_train(X_train, y_train)

# print(nb.cond_probs, nb.prior_probs)
# 进行预测
y_pred = nb.predict(X_test)

# 计算准确率
accuracy = accuracy_score(y_test, y_pred)
print(f"朴素贝叶斯分类器的准确率: {accuracy}")

代码解释

  • __init__ 方法:初始化平滑参数 lambda_,以及存储先验概率和条件概率的字典。
  • fit 方法
    • 计算每个类别的先验概率,使用贝叶斯估计(拉普拉斯平滑)。
    • 计算每个特征在每个类别下取不同值的条件概率,同样使用贝叶斯估计。
  • predict 方法
    • 对于每个测试样本,计算它属于每个类别的后验概率。
    • 选择后验概率最大的类别作为预测结果。
  • 数据生成与评估:使用 make_classification 生成随机分类数据,划分训练集和测试集,训练朴素贝叶斯分类器并计算准确率。
posted @ 2025-03-14 21:12  wlu  阅读(340)  评论(0)    收藏  举报