Skip to content

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

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

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  # 导入numpy库,用于数值计算
import matplotlib.pyplot as plt  # 导入matplotlib库,用于数据可视化
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  # 导入seaborn库,用于美化可视化
import time  # 导入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}个特征的随机数据...")
        # 使用make_classification生成随机分类数据
        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("对数据进行标准化处理...")
        # 创建StandardScaler对象
        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 evaluate_with_cross_validation(model, X, y, cv=5):
    """
    使用交叉验证评估模型
    参数:
        model: 训练好的模型
        X: 特征矩阵
        y: 标签数组
        cv: 交叉验证折数
    返回:
        交叉验证分数
    """
    try:
        print("\n执行交叉验证评估...")
        # 执行交叉验证
        scores = cross_val_score(model, X, y, cv=cv)
        print(f"交叉验证分数: {scores}")
        print(f"平均分数: {scores.mean():.4f} (+/- {scores.std() * 2:.4f})")
        return scores
    except Exception as e:
        print(f"交叉验证评估时出错: {str(e)}")
        return None

6.2.4 特征重要性分析

添加特征重要性分析功能:

python
def analyze_feature_importance(model, feature_names=None):
    """
    分析特征重要性
    参数:
        model: 训练好的模型
        feature_names: 特征名称列表
    返回:
        特征重要性得分
    """
    try:
        # 如果没有提供特征名称,使用默认名称
        if feature_names is None:
            feature_names = [f"特征{i+1}" for i in range(model.n_features_in_)]
        
        # 计算每个特征的方差作为重要性指标
        importance = np.var(model.theta_, axis=0)
        importance = importance / np.sum(importance)  # 归一化
        
        # 绘制特征重要性图
        plt.figure(figsize=(10, 6))
        plt.bar(feature_names, importance)
        plt.title('特征重要性分析')
        plt.xlabel('特征')
        plt.ylabel('重要性得分')
        plt.xticks(rotation=45)
        plt.tight_layout()
        plt.show()
        
        return dict(zip(feature_names, importance))
    except Exception as e:
        print(f"特征重要性分析时出错: {str(e)}")
        return None

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.6 主函数

最后,我们将所有功能整合到主函数中:

python
def main():
    # 生成数据
    X, y = generate_data(n_samples=1000, n_features=2, n_classes=2)
    if X is None or y 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.2, random_state=42
    )
    
    # 训练和评估模型
    model, metrics = train_and_evaluate_model(X_train, X_test, y_train, y_test)
    if model is None or metrics is None:
        return
    
    # 打印评估指标
    print_metrics(metrics)
    
    # 绘制混淆矩阵
    plot_confusion_matrix(metrics['confusion_matrix'])
    
    # 绘制决策边界
    plot_decision_boundary(model, X_scaled, y)
    
    # 交叉验证评估
    evaluate_with_cross_validation(model, X_scaled, y)
    
    # 特征重要性分析
    analyze_feature_importance(model)

if __name__ == "__main__":
    main()

6.3 算法特点

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

  1. 优点:

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

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

6.4 应用场景

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

  1. 文本分类

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

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

    • 信用评估
    • 欺诈检测

6.5 总结

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

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

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

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

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

用心打造的机器学习教程