Skip to content

第六章:朴素贝叶斯分类器

朴素贝叶斯分类器是一种基于贝叶斯定理的概率分类算法。它假设特征之间相互独立,这个"朴素"的假设虽然在实际应用中很少成立,但该算法仍然在许多实际应用中表现出色。

6.1 算法原理

朴素贝叶斯分类器基于贝叶斯定理:

[ P(y|x) = \frac{P(x|y)P(y)}{P(x)} ]

其中:

  • (P(y|x)) 是后验概率:给定特征 x 时类别 y 的概率
  • (P(x|y)) 是似然概率:给定类别 y 时特征 x 的概率
  • (P(y)) 是先验概率:类别 y 的概率
  • (P(x)) 是归一化因子

朴素贝叶斯假设所有特征相互独立,因此:

[ P(x|y) = \prod_{i=1}^n P(x_i|y) ]

6.2 代码实现

让我们通过一个具体的例子来实现朴素贝叶斯分类器。首先,我们需要导入必要的库:

python
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.naive_bayes import GaussianNB
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
from sklearn.preprocessing import StandardScaler
import seaborn as sns
import time

6.2.1 数据生成和预处理

首先,我们实现数据生成函数:

python
def generate_data(n_samples=1000, n_features=2, n_classes=2, n_clusters_per_class=2, random_state=42):
    """
    生成随机分类数据
    参数:
        n_samples: 样本数量
        n_features: 特征数量
        n_classes: 类别数量
        n_clusters_per_class: 每个类别的聚类数量
        random_state: 随机种子
    返回:
        X: 特征矩阵
        y: 标签数组
    """
    try:
        print(f"生成{n_classes}类,{n_samples}个样本,{n_features}个特征的随机数据...")
        X, y = make_classification(
            n_samples=n_samples,
            n_features=n_features,
            n_classes=n_classes,
            n_clusters_per_class=n_clusters_per_class,
            random_state=random_state
        )
        return X, y
    except Exception as e:
        print(f"生成数据时出错: {str(e)}")
        return None, None

接下来,我们实现数据预处理函数:

python
def preprocess_data(X):
    """
    数据预处理:标准化
    参数:
        X: 特征矩阵
    返回:
        标准化后的特征矩阵
    """
    try:
        print("对数据进行标准化处理...")
        scaler = StandardScaler()
        X_scaled = scaler.fit_transform(X)
        return X_scaled
    except Exception as e:
        print(f"数据预处理时出错: {str(e)}")
        return None

6.2.2 模型训练和评估

现在,我们实现模型训练和评估函数:

python
def train_and_evaluate_model(X_train, X_test, y_train, y_test, var_smoothing=1e-9):
    """
    训练和评估朴素贝叶斯模型
    参数:
        X_train, X_test: 训练和测试特征
        y_train, y_test: 训练和测试标签
        var_smoothing: 方差平滑参数
    返回:
        model: 训练好的模型
        metrics: 评估指标
    """
    try:
        print("\n开始训练朴素贝叶斯模型...")
        start_time = time.time()
        
        model = GaussianNB(var_smoothing=var_smoothing)
        model.fit(X_train, y_train)
        
        training_time = time.time() - start_time
        print(f"模型训练完成,用时: {training_time:.2f}秒")
        
        train_score = model.score(X_train, y_train)
        test_score = model.score(X_test, y_test)
        
        y_pred = model.predict(X_test)
        
        metrics = {
            'train_score': train_score,
            'test_score': test_score,
            'training_time': training_time,
            'classification_report': classification_report(y_test, y_pred),
            'confusion_matrix': confusion_matrix(y_test, y_pred)
        }
        
        return model, metrics
    except Exception as e:
        print(f"训练模型时出错: {str(e)}")
        return None, None

6.2.3 交叉验证评估

为了更全面地评估模型性能,我们使用交叉验证:

python
def cross_validate_model(model, X, y, cv=5):
    """
    使用交叉验证评估模型
    参数:
        model: 待评估的模型
        X: 特征矩阵
        y: 标签数组
        cv: 交叉验证折数
    返回:
        scores: 交叉验证得分
    """
    try:
        print(f"\n开始进行{cv}折交叉验证...")
        start_time = time.time()
        
        scores = cross_val_score(model, X, y, cv=cv)
        
        cv_time = time.time() - start_time
        print(f"交叉验证完成,用时: {cv_time:.2f}秒")
        
        return scores
    except Exception as e:
        print(f"交叉验证时出错: {str(e)}")
        return None

6.2.4 特征重要性分析

对于朴素贝叶斯模型,特征重要性通常不是直接可用的,但我们可以通过分析每个特征的方差来间接理解其对模型的影响:

python
def analyze_feature_importance(model, feature_names):
    """
    分析特征重要性(针对高斯朴素贝叶斯)
    参数:
        model: 训练好的高斯朴素贝叶斯模型
        feature_names: 特征名称列表
    """
    try:
        print("\n分析特征重要性...")
        # 高斯朴素贝叶斯没有直接的feature_importances_属性
        # 可以通过查看每个特征的方差来间接理解其重要性
        # 较小的方差可能意味着该特征在区分类别上更重要
        
        # 获取每个类别的特征均值和方差
        theta = model.theta_  # 每个类别的特征均值
        sigma = model.sigma_  # 每个类别的特征方差
        
        print("\n特征均值 (theta_):")
        for i, name in enumerate(feature_names):
            print(f"  {name}: {theta_[:, i]}")
            
        print("\n特征方差 (sigma_):")
        for i, name in enumerate(feature_names):
            print(f"  {name}: {sigma_[:, i]}")
            
        print("\n注意:对于高斯朴素贝叶斯,特征重要性不是直接可用的。这里展示的是每个特征在不同类别下的均值和方差,可以间接反映特征的区分能力。较小的方差可能意味着该特征在区分类别上更重要。")
            
    except Exception as e:
        print(f"分析特征重要性时出错: {str(e)}")

6.2.5 可视化结果

最后,我们实现结果可视化函数:

python
def plot_decision_boundary(model, X, y):
    """
    绘制决策边界
    参数:
        model: 训练好的朴素贝叶斯模型
        X: 特征矩阵
        y: 标签数组
    """
    try:
        print("\n绘制决策边界...")
        # 创建网格点
        x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
        y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
        xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02),
                            np.arange(y_min, y_max, 0.02))
        
        # 预测网格点的类别
        Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
        Z = Z.reshape(xx.shape)
        
        # 绘制结果
        plt.figure(figsize=(10, 8))
        plt.contourf(xx, yy, Z, alpha=0.4, cmap='coolwarm')  # 绘制决策边界
        plt.scatter(X[:, 0], X[:, 1], c=y, alpha=0.8, cmap='coolwarm')  # 绘制数据点
        plt.title('朴素贝叶斯分类结果')
        plt.xlabel('特征1')
        plt.ylabel('特征2')
        plt.colorbar(label='类别')
        plt.show()
    except Exception as e:
        print(f"绘制决策边界时出错: {str(e)}")

def plot_confusion_matrix(cm):
    """
    绘制混淆矩阵
    参数:
        cm: 混淆矩阵
    """
    try:
        plt.figure(figsize=(8, 6))
        # 使用seaborn绘制混淆矩阵热力图
        sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')
        plt.title('混淆矩阵')
        plt.xlabel('预测标签')
        plt.ylabel('真实标签')
        plt.show()
    except Exception as e:
        print(f"绘制混淆矩阵时出错: {str(e)}")

6.2.5 完整示例和结果展示

最后,我们将所有函数整合到一个完整的示例中,并展示结果:

python
def main():
    print("朴素贝叶斯分类器示例")
    
    X, y = generate_data(n_samples=1000, n_features=2, n_classes=2, random_state=42)
    if X is None:
        return
    
    X_scaled = preprocess_data(X)
    if X_scaled is None:
        return
    
    X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, test_size=0.3, random_state=42)
    print(f"\n训练集大小: {len(X_train)},测试集大小: {len(X_test)}")
    
    model, metrics = train_and_evaluate_model(X_train, X_test, y_train, y_test)
    if model is None:
        return
    
    print("\n模型评估结果:")
    print(f"训练集准确率: {metrics['train_score']:.4f}")
    print(f"测试集准确率: {metrics['test_score']:.4f}")
    print("分类报告:\n", metrics['classification_report'])
    print("混淆矩阵:\n", metrics['confusion_matrix'])
    
    cv_scores = cross_validate_model(model, X_scaled, y)
    if cv_scores is not None:
        print("\n交叉验证分数:", cv_scores)
        print(f"交叉验证平均分数: {cv_scores.mean():.4f} (+/- {cv_scores.std() * 2:.4f})")
        
    feature_names = [f'Feature_{i+1}' for i in range(X.shape[1])]
    analyze_feature_importance(model, feature_names)
    
    print("\n示例运行完毕。")

if __name__ == "__main__":
    main()

6.2.6 总结

本节通过一个完整的示例,详细介绍了朴素贝叶斯分类器的实现过程,包括数据生成、预处理、模型训练、评估、交叉验证以及特征重要性分析。朴素贝叶斯模型因其简单、高效且在文本分类等领域表现出色而广受欢迎。尽管其“朴素”假设在实际中往往不成立,但在许多情况下仍能取得良好的效果。

6.3 算法特点

朴素贝叶斯分类器具有以下特点:

  1. 优点:

    • 训练速度快,计算简单
    • 对小规模数据集效果好
    • 对缺失数据不敏感
    • 可以处理多分类问题
  2. 缺点:

    • 特征独立性假设过于简单
    • 对输入数据的表达形式敏感
    • 需要先验概率
    • 对特征之间的相关性考虑不足

6.4 应用场景

朴素贝叶斯分类器在以下场景中表现良好:

  1. 文本分类

    • 垃圾邮件过滤
    • 新闻分类
    • 情感分析
  2. 医疗诊断

    • 疾病预测
    • 症状分类
  3. 金融领域

    • 信用评估
    • 欺诈检测

6.5 总结

朴素贝叶斯分类器虽然基于简单的假设,但在实际应用中表现出色。它的主要优势在于:

  1. 实现简单,计算效率高
  2. 对小规模数据集效果好
  3. 可以处理多分类问题
  4. 对缺失数据不敏感

然而,它的主要局限性在于:

  1. 特征独立性假设过于简单
  2. 对输入数据的表达形式敏感
  3. 需要先验概率
  4. 对特征之间的相关性考虑不足

在实际应用中,我们需要根据具体问题选择合适的特征表示方法,并注意特征之间的相关性,以获得更好的分类效果。

用心打造的机器学习教程