📑 查看全课大纲(第 9 / 20 节)

判别分析代码实战与误判率评估

约 33 分钟

📺 正在播放小象官方高清录播(支持倍速与清晰度调节)

小象实战讲义 · 多元统计分析

本节我们将聚焦于判别分析的实战应用,学习如何利用 Python 现代数据科学工具库,将 Fisher 判别和贝叶斯判别的理论付诸实践。我们将以经典的鸢尾花数据集为例,完整演示从数据准备、模型构建、预测到误判率评估的全过程。学完本节,你将能够独立运用判别分析解决多分类问题,并科学评估模型的分类性能。

💡 核心导读

  • 实战环境搭建:了解如何使用 Python 的 scikit-learn 等库实现判别分析。
  • Fisher 判别 (LDA) 实战:掌握 LinearDiscriminantAnalysis 的用法,解读判别函数系数、组均值、先验概率等关键输出。
  • 贝叶斯判别 (QDA) 实战:理解 QuadraticDiscriminantAnalysis 如何通过指定先验概率引入总体信息。
  • 模型评估与比较:学习计算混淆矩阵、样本表观误判率和交叉验证准确率,科学比较 LDA 与 QDA 的性能。
  • 可视化与洞察:通过绘制样本在判别函数上的投影图,直观理解分类边界与错分原因。

实战环境与数据准备

判别分析旨在根据已知类别的样本构建判别规则,用于对新样本分类。Python 中主要使用 scikit-learn 库的 discriminant_analysis 模块实现。

我们使用 sklearn 内置的鸢尾花数据集演示,该数据集包含150个样本、3个类别(山鸢尾、变色鸢尾、维吉尼亚鸢尾),每个样本有4个特征(萼片长、萼片宽、花瓣长、花瓣宽)。

import numpy as np
from sklearn.datasets import load_iris
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis, QuadraticDiscriminantAnalysis
from sklearn.model_selection import cross_val_score, StratifiedKFold
from sklearn.metrics import confusion_matrix, accuracy_score
import matplotlib.pyplot as plt

# 加载数据
iris = load_iris()
X = iris.data  # 特征矩阵 (150, 4)
y = iris.target  # 类别标签 (0, 1, 2)
feature_names = iris.feature_names
target_names = iris.target_names

print(f"数据形状: {X.shape}")
print(f"特征: {feature_names}")
print(f"类别: {target_names}")
print(f"各类别样本数: {np.bincount(y)}")

Fisher 线性判别分析 (LDA) 实战

Fisher 判别的核心思想是寻找投影方向,使得投影后类间方差最大、类内方差最小。对于 kk 个总体,判别函数为线性组合 Y=uXY = \mathbf{u}'\mathbf{X},其中系数向量 u\mathbf{u} 是以下广义特征值问题的解:

maxuuBuuEu\max_{\mathbf{u}} \frac{\mathbf{u}'\mathbf{B}\mathbf{u}}{\mathbf{u}'\mathbf{E}\mathbf{u}}

其中 B\mathbf{B} 为组间离差阵,E\mathbf{E} 为组内离差阵(即 4.2 节 Fisher 判别中的组内散度矩阵 W\mathbf{W},二者定义完全相同,仅记号不同)。scikit-learnLinearDiscriminantAnalysis 默认用奇异值分解 (SVD) 求解,效率高且数值稳定。

# 创建并拟合 LDA 模型
lda = LinearDiscriminantAnalysis()
lda.fit(X, y)

# 查看模型关键参数
print("先验概率 (priors):", lda.priors_)
print("组均值 (group means):\n", lda.means_)
print("判别函数系数 (scaling):\n", lda.scalings_)
print("各判别函数解释的方差比例:", lda.explained_variance_ratio_)

# 模型预测
y_pred_lda = lda.predict(X)
y_score_lda = lda.transform(X)  # 获取样本在判别函数上的坐标

# 计算混淆矩阵
cm_lda = confusion_matrix(y, y_pred_lda)
print("\nLDA 混淆矩阵:")
print(cm_lda)

关键输出解读

  • priors_:各类别先验概率,默认用样本频率估计。
  • means_:每个类别在各特征上的均值向量。
  • scalings_:判别函数系数矩阵,每列对应一个判别方向。对于 pp 个特征、kk 个类别,最多有 min(p,k1)\min(p, k-1) 个判别函数。
  • explained_variance_ratio_:每个判别函数解释的方差比例,衡量区分能力。

模型评估与误判率分析

评估判别模型性能的核心是计算误判率。最直接的指标是样本表观误判率 (AER),即训练集上的错分比例,但 AER 往往过于乐观,更稳健的方法是交叉验证;其中留一法(LOO,又称刀切法)kk 折交叉验证在 k=nk=n 时的特例(每次留 1 个样本作验证集),适合小样本情形。

# 计算样本表观误判率
aer_lda = 1 - accuracy_score(y, y_pred_lda)
print(f"LDA 样本表观误判率: {aer_lda:.4f}")
print(f"LDA 训练集准确率: {accuracy_score(y, y_pred_lda):.4f}")

# 5折分层交叉验证评估泛化性能
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
lda_cv_scores = cross_val_score(lda, X, y, cv=cv, scoring='accuracy')
print(f"LDA 5折交叉验证准确率: {np.mean(lda_cv_scores):.4f}{np.std(lda_cv_scores):.4f})")

# 混淆矩阵分析
print("\n混淆矩阵分析:")
for i, name in enumerate(target_names):
    correct = cm_lda[i, i]
    total = cm_lda[i].sum()
    print(f"  {name}: {correct}/{total} 正确,准确率 {correct/total:.2%}")

运行上述评估代码,你将得到与验证脚本一致的结果:

LDA 5 折交叉验证准确率: 0.9733
LDA 混淆矩阵对角线 (各类别正确数): [50, 48, 49]
LDA 样本表观误判率 (Apparent Error Rate): 0.02

贝叶斯判别分析 (QDA) 实战

贝叶斯判别引入了总体先验概率 qiq_i 和误判损失 C(ji)C(j|i),判别规则是使后验概率最大期望损失最小。当各类协方差矩阵相等时,贝叶斯判别退化为线性判别 (LDA);协方差不等时,判别函数为二次型,称为二次判别分析 (QDA)。

scikit-learnQuadraticDiscriminantAnalysis 默认用样本频率作为先验,可通过 priors 参数手动指定。

# 创建 QDA 模型(默认先验为样本比例)
qda = QuadraticDiscriminantAnalysis()
qda.fit(X, y)
y_pred_qda = qda.predict(X)

# 计算 QDA 评估指标
aer_qda = 1 - accuracy_score(y, y_pred_qda)
qda_cv_scores = cross_val_score(qda, X, y, cv=cv, scoring='accuracy')

print(f"QDA 样本表观误判率: {aer_qda:.4f}")
print(f"QDA 5折交叉验证准确率: {np.mean(qda_cv_scores):.4f}{np.std(qda_cv_scores):.4f})")

# 模型比较
print("\n模型比较 (交叉验证准确率):")
print(f"  LDA: {np.mean(lda_cv_scores):.4f}")
print(f"  QDA: {np.mean(qda_cv_scores):.4f}")

运行结果将显示:

QDA 5 折交叉验证准确率: 0.98

可视化与深入洞察

可视化可帮助直观理解分类边界和错分原因。对于 LDA,我们将样本投影到前两个判别函数构成的平面上。

# 绘制 LDA 判别空间投影图
plt.figure(figsize=(8, 6))
colors = ['navy', 'turquoise', 'darkorange']

for color, i, target_name in zip(colors, [0, 1, 2], target_names):
    plt.scatter(y_score_lda[y == i, 0], y_score_lda[y == i, 1],
                color=color, alpha=.8, label=target_name)

plt.xlabel('Linear Discriminant 1')
plt.ylabel('Linear Discriminant 2')
plt.title('鸢尾花数据集在 LDA 判别空间中的投影')
plt.legend(loc='best')
plt.grid(True, linestyle='--', alpha=0.6)
plt.tight_layout()
plt.show()

从图中可见,类别0(山鸢尾)与其他两类在LD1方向分离极好,类别1和2在二维平面有部分重叠,这是错分的主要原因。

📝 动手练一练

  1. 先验概率的影响:在 QuadraticDiscriminantAnalysis 中,将 priors 设为 [0.2, 0.6, 0.2] 重新训练,与默认先验的结果对比,准确率有何变化?
  2. 训练集测试集划分:将鸢尾花数据按7:3随机划分(用 train_test_split,固定 random_state=42),分别训练LDA和QDA并在测试集评估,哪个模型泛化能力更好?

参考答案

  1. 先验设为 [0.2, 0.6, 0.2] 时模型会倾向于判为第二类,性能变化取决于真实分布。实际中先验应基于领域知识确定。
  2. LDA参数更少(等协方差假设),小样本下泛化能力通常优于QDA,具体结果随数据划分波动。

本章小结

本节我们系统学习了判别分析的Python实战实现与评估方法:

要点回顾

  1. Fisher线性判别 (LDA) 通过最大化类间/类内方差比寻找最优投影方向,适用于各类协方差相近的情形。
  2. 贝叶斯二次判别 (QDA) 放松了等协方差假设,判别边界为二次曲面,能拟合更复杂的分类边界,但需估计更多参数。
  3. 模型评估需综合使用表观误判率、混淆矩阵和交叉验证准确率,交叉验证能更可靠地估计泛化性能。
  4. 可视化判别空间投影,能直观揭示类别可分性与错分原因。

行动清单

  • sklearn.discriminant_analysis 在自定义数据集上尝试LDA/QDA。
  • 对比交叉验证准确率,选择最优模型。
  • 绘制判别空间投影图,分析类别可分性。

— 小象教研组

配套学习资源与课件
  • 第4章课件:判别分析
    下载
  • 多元统计分析参考讲义与常用函数(多元分析与主成分常用函数速查)
    下载
  • 课程配套数据集(全课程实战数据包)
    下载
  • 课程全套源代码(课程相关代码汇总)
    下载
🎁 免费学习资源

领取《小象 11GB VIP 课件资料包与大厂真题手册》

包含全套实战 Jupyter 源码、清洗后数据集、大厂高频面试真题与专属学员答疑交流群。

  • 完整 Python / 数据分析 Jupyter 实战源码
  • 大厂真实业务数据集与练习题
  • 微信扫码添加顾问免费领取;想学什么,直接告诉顾问
微信二维码:扫码添加课程顾问微信扫码添加顾问