1. 朴素贝叶斯算法总结
1.1 模型
朴素贝叶斯法是基于贝叶斯定理与特征条件独立假设的分类方法。对于给定的训练数据集,首先基于特征条件独立假设学习输入输出的联合概率分布;然后基于此模型,对给定的输入 ,利用贝叶斯定理求出后验概率最大的输出 。
设输入空间 为 维向量的集合,输出空间为类标记集合 。 是定义在输入空间 上的随机向量, 是定义在输出空间 上的随机变量。 是 和 的联合概率分布。训练数据集
由 独立同分布产生。
朴素贝叶斯法通过训练数据集学习联合概率分布 。具体地,学习以下先验概率分布及条件概率分布。
- 先验概率分布
- 条件概率分布
于是学习到联合概率分布 。
条件独立性假设是
这意味着在分类的类别确定的条件下,用于分类的特征之间是条件独立的。
对于给定的输入 ,通过学习到的模型计算后验概率分布 ,将后验概率最大的类作为 的类输出。后验概率计算根据贝叶斯定理进行:
将条件独立性假设代入上式有
于是,朴素贝叶斯分类器可表示为
由于上式分母对所有 都是相同的,所以
1.2 策略
朴素贝叶斯法将实例分到后验概率最大的类中,这等价于期望风险最小化。假设选择 0 - 1 损失函数:
式中 是分类决策函数。这时,期望风险函数为
期望是对联合分布 取的。由此取条件期望
为了使期望风险最小化,只需对 逐个极小化,由此得到:
这样一来,根据期望风险最小化准则就得到了后验概率最大化准则:
即朴素贝叶斯法所采用的策略。
1.3 方法
- 极大似然估计:
- 先验概率 的极大似然估计是
其中 是指示函数, 是样本容量。
- 设第 个特征 可能取值的集合为 ,条件概率 的极大似然估计是
其中 ;;。
- 贝叶斯估计:
用极大似然估计可能会出现所要估计的概率值为 0 的情况。这时会影响到后验概率的计算结果,使分类产生偏差。解决这一问题的方法是采用贝叶斯估计。具体地,条件概率的贝叶斯估计是
式中 。当 时就是极大似然估计。常取 ,这时称为拉普拉斯平滑(Laplace smoothing)。同样,先验概率的贝叶斯估计是
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
生成随机分类数据,划分训练集和测试集,训练朴素贝叶斯分类器并计算准确率。