1. 概述
朴素贝叶斯分类器(Naive Bayes Classifier)是一种基于贝叶斯定理的概率分类算法。其”朴素”之处在于假设所有特征在给定类别条件下相互独立,尽管这个假设在现实中很少成立,但朴素贝叶斯分类器在许多实际应用中表现出色。
1.1 核心思想
给定样本特征 ,朴素贝叶斯通过**最大后验概率(MAP)**准则进行分类:
应用贝叶斯定理:
由于分母对所有类别相同,优化目标简化为:
1.2 条件独立假设
在朴素假设下:
这使得联合概率可以分解为各个特征的边缘概率的乘积,大大简化了计算。
2. 算法推导
2.1 分类器形式
综合以上,朴素贝叶斯分类器可以写成:
在实际计算中,为了避免下溢,通常使用对数形式:
2.2 参数估计
使用**极大似然估计(MLE)**来估计参数:
- 类先验概率:,其中 是类别 的样本数
- 条件概率:根据特征类型不同,估计方法也不同
3. 三种常见变体
3.1 高斯朴素贝叶斯(Gaussian NB)
当特征是连续值时,假设每个类别下特征服从正态分布:
其中 和 是类别 下特征 的均值和方差。
适用场景:连续特征,如身高、体重、血压等。
3.2 多项式朴素贝叶斯(Multinomial NB)
适用于离散特征,特别是文本分类中的词频特征。
假设特征是多项分布:
其中 是类别 中特征 出现的次数。
适用场景:文本分类、文档分类、垃圾邮件检测。
3.3 伯努利朴素贝叶斯(Bernoulli NB)
适用于二元离散特征,每个特征只有0/1两种取值。
适用场景:文本分类中基于词袋模型的二元特征。
4. 拉普拉斯平滑
4.1 零概率问题
当某个特征值在训练集中没有出现在某个类别中时,,导致整个后验概率为0。
解决方案:使用拉普拉斯平滑(Laplace Smoothing):
其中:
- 是平滑参数(通常取1)
- 是特征 可能取值的数量
4.2 贝叶斯估计
从贝叶斯角度,拉普拉斯平滑等价于使用共轭先验进行参数估计。对于多项分布,使用狄利克雷先验:
5. 算法流程
5.1 训练阶段
输入:训练集 D = {(x_1, y_1), ..., (x_N, y_N)}
输出:朴素贝叶斯分类器
1. 对于每个类别 c ∈ C:
计算类先验概率 P(c) = N_c / N
2. 对于每个特征 x_i 和每个类别 c:
计算条件概率 P(x_i | c)
(使用极大似然估计或贝叶斯估计)
3. 返回分类器
5.2 预测阶段
输入:待预测样本 x,训练好的分类器
输出:预测类别
对于每个类别 c ∈ C:
计算 score(c) = log P(c) + Σ_i log P(x_i | c)
返回 score(c) 最大的类别
6. 代码实现
6.1 高斯朴素贝叶斯
import numpy as np
class GaussianNaiveBayes:
def __init__(self, alpha=1.0):
self.alpha = alpha # 平滑参数
self.classes = None
self.mean = {} # 存储每个类别的均值
self.var = {} # 存储每个类别的方差
self.priors = {} # 存储类先验概率
def fit(self, X, y):
self.classes = np.unique(y)
n_features = X.shape[1]
for c in self.classes:
X_c = X[y == c]
n_c = X_c.shape[0]
# 类先验概率(使用拉普拉斯平滑)
self.priors[c] = (n_c + self.alpha) / (len(y) + self.alpha * len(self.classes))
# 均值和方差
self.mean[c] = X_c.mean(axis=0)
self.var[c] = X_c.var(axis=0) + 1e-9 # 防止除零
def _gaussian_pdf(self, x, mean, var):
coef = 1 / np.sqrt(2 * np.pi * var)
exponent = - (x - mean) ** 2 / (2 * var)
return coef * np.exp(exponent)
def _log_likelihood(self, x, c):
return np.sum(np.log(self._gaussian_pdf(x, self.mean[c], self.var[c])))
def predict(self, X):
predictions = []
for x in X:
posteriors = []
for c in self.classes:
prior = np.log(self.priors[c])
likelihood = self._log_likelihood(x, c)
posteriors.append(prior + likelihood)
predictions.append(self.classes[np.argmax(posteriors)])
return np.array(predictions)6.2 多项式朴素贝叶斯(文本分类)
from collections import Counter
import numpy as np
class MultinomialNaiveBayes:
def __init__(self, alpha=1.0):
self.alpha = alpha
self.classes = None
self.class_word_counts = {} # 每个类的词计数
self.class_totals = {} # 每个类的总词数
self.vocab = set()
self.class_priors = {}
def fit(self, X, y):
"""X: 文档-词频矩阵 (N, V)"""
self.classes = np.unique(y)
n_features = X.shape[1]
for c in self.classes:
X_c = X[y == c]
self.class_priors[c] = np.log(X_c.shape[0] / X.shape[0])
self.class_word_counts[c] = X_c.sum(axis=0) + self.alpha
self.class_totals[c] = X_c.sum()
def _compute_log_likelihood(self, x, c):
# 词频/总词数(对数形式)
return np.sum(x * np.log(self.class_word_counts[c] / self.class_totals[c]))
def predict(self, X):
predictions = []
for x in X:
best_class = None
best_score = float('-inf')
for c in self.classes:
score = self.class_priors[c] + self._compute_log_likelihood(x, c)
if score > best_score:
best_score = score
best_class = c
predictions.append(best_class)
return np.array(predictions)6.3 使用sklearn
from sklearn.naive_bayes import GaussianNB, MultinomialNB, BernoulliNB
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, classification_report
# 加载数据
iris = load_iris()
X, y = iris.data, iris.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
# 高斯朴素贝叶斯
gnb = GaussianNB()
gnb.fit(X_train, y_train)
y_pred = gnb.predict(X_test)
print(f"Gaussian NB Accuracy: {accuracy_score(y_test, y_pred):.4f}")
# 文本分类示例
from sklearn.feature_extraction.text import CountVectorizer
# 示例文本数据
texts = [
"Chinese Beijing Chinese",
"Chinese Chinese Shanghai",
"Chinese Macao",
"Tokyo Japan Chinese"
]
labels = [1, 1, 1, 0] # 1: 中国相关, 0: 非中国相关
vectorizer = CountVectorizer()
X_text = vectorizer.fit_transform(texts)
mnb = MultinomialNB()
mnb.fit(X_text, labels)
# 预测新文本
new_text = vectorizer.transform(["Chinese Chinese Chinese Japan"])
print(f"Prediction: {mnb.predict(new_text)[0]}") # 预测为 1(中国相关)7. 朴素假设为什么不朴素?
虽然”条件独立”假设在现实中很少成立,但朴素贝叶斯仍然表现出色,原因如下:
7.1 分类边界的影响
即使联合分布不独立,分类决策边界可能对依赖关系不敏感。关键是正确分类,而不是准确估计概率。
7.2 概率估计的鲁棒性
虽然单个概率估计可能不准确,但相对排序(哪个类别的概率更高)往往是对的。
7.3 误差上界
理论上,朴素贝叶斯的分类错误率有一个上界:
其中 是特征数量。这意味着虽然概率估计可能有偏差,但不会无限放大。
8. 优缺点分析
优点
| 优点 | 说明 |
|---|---|
| 简单高效 | 训练和预测时间复杂度低 |
| 可解释性强 | 可以直接看到每个特征对分类的贡献 |
| 对小数据集友好 | 在数据较少时也能表现良好 |
| 对特征缩放不敏感 | 基于概率,不需要特征标准化 |
| 支持增量学习 | 可以在线更新参数 |
缺点
| 缺点 | 说明 |
|---|---|
| 强假设 | 条件独立假设往往不成立 |
| 概率估计不准确 | 由于假设,概率估计往往过于极端 |
| 处理连续特征 | 需要假设分布类型 |
| 特征相关时表现差 | 当特征高度相关时,性能下降明显 |
9. 应用场景
9.1 文本分类
朴素贝叶斯是文本分类的经典算法,特别是垃圾邮件检测和情感分析。
典型流程:
- 使用词袋模型或TF-IDF将文本转换为特征向量
- 训练多项式朴素贝叶斯分类器
- 预测新文本的类别
9.2 实时分类
由于计算简单,朴素贝叶斯适合实时分类场景。
9.3 作为基线模型
朴素贝叶斯常作为基线模型,用于比较更复杂模型的性能。
10. 改进方向
10.1 树增强朴素贝叶斯(TAN)
放松独立假设,允许特征之间形成树结构:
10.2 贝叶斯网络
使用更通用的图模型来表示特征之间的依赖关系。
10.3 特征选择
通过特征选择减少特征间的依赖,提高分类性能。