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 文本分类

朴素贝叶斯是文本分类的经典算法,特别是垃圾邮件检测和情感分析。

典型流程

  1. 使用词袋模型或TF-IDF将文本转换为特征向量
  2. 训练多项式朴素贝叶斯分类器
  3. 预测新文本的类别

9.2 实时分类

由于计算简单,朴素贝叶斯适合实时分类场景。

9.3 作为基线模型

朴素贝叶斯常作为基线模型,用于比较更复杂模型的性能。

10. 改进方向

10.1 树增强朴素贝叶斯(TAN)

放松独立假设,允许特征之间形成树结构:

10.2 贝叶斯网络

使用更通用的图模型来表示特征之间的依赖关系。

10.3 特征选择

通过特征选择减少特征间的依赖,提高分类性能。

参考资料