查看全课大纲(第 100 / 101 节)
  1. 1.数据分析基本概念
  2. 2.学习数据分析的一般路线
  3. 3.数据分析的流程
  4. 4.数据类型
  5. 5.环境部署(1)
  6. 6.环境部署(2)
  7. 7.课程介绍
  8. 8.TXT文件操作
  9. 9.JSON文件操作
  10. 10.CSV文件操作
  11. 11.Excel文件操作
  12. 12.数据库及SQL常用语法
  13. 13.数据库基本操作
  14. 14.数据库多表连接
  15. 15.实战:欧洲职业足球数据库分析
  16. 16.爬虫简介
  17. 17.URL管理模块
  18. 18.网页下载模块
  19. 19.网页解析模块(1)
  20. 20.网页解析模块(2)
  21. 21.Scrapy简介
  22. 22.Scrapy使用步骤(1)
  23. 23.Scrapy使用步骤(2)
  24. 24.Scrapy使用步骤(3)
  25. 25.Scrapy使用步骤(4)
  26. 26.实战:获取国内城市空气质量指数数据
  27. 27.NumPy和SciPy介绍
  28. 28.多维数组
  29. 29.多维数组操作
  30. 30.NumPy的常用方法
  31. 31.向量化介绍
  32. 32.向量化及通用函数
  33. 33.实战:2016美国大选分析
  34. 34.数据结构-Series
  35. 35.数据结构-DataFrame
  36. 36.数据结构-Index
  37. 37.Series的索引操作
  38. 38.DataFrame的索引操作
  39. 39.索引操作总结
  40. 40.运算与对齐
  41. 41.函数应用操作(1) -- map
  42. 42.函数应用操作 (2) -- apply applymap
  43. 43.文件读写操作
  44. 44.排序操作
  45. 45.数据清洗--处理缺失数据
  46. 46.数据清洗--处理重复数据
  47. 47.数据清洗--替换数据
  48. 48.常用统计方法(1) -- describe quantile
  49. 49.常用统计方法(2) -- sum mean median count
  50. 50.常用统计方法(3) -- max min idxmax idxmin
  51. 51.常用统计方法(4) -- mad var std cumsum
  52. 52.实战:全球食品数据分析
  53. 53.层级索引
  54. 54.分组与聚合介绍
  55. 55.分组操作(1) -- GroupBy对象及常用聚合操作
  56. 56.分组操作(2) -- 自定义分组及聚合操作
  57. 57.透视表介绍
  58. 58.透视表操作
  59. 59.数据规整(1) -- 数据合并concat
  60. 60.数据规整(2) -- 数据连接merge
  61. 61.数据重构(3) -- 数据重构stack unstack
  62. 62.实战:互联网电影资料库分析
  63. 63.探索性数据分析EDA介绍
  64. 64.EDA的目的
  65. 65.EDA常用工具
  66. 66.Matplotlib绘图基本介绍
  67. 67.Matplotlib画布
  68. 68.散点图和柱状图的绘制
  69. 69.直方图的绘制
  70. 70.矩阵绘图
  71. 71.子图的使用
  72. 72.Matplotlib颜色、标记、线型
  73. 73.Matplotlib坐标刻度、标签、图例、标题
  74. 74.Seaborn介绍
  75. 75.数据集分布可视化(1) -- 单变量分布、双变量分布
  76. 76.数据集分布可视化(2) -- 变量关系可视化
  77. 77.类别数据可视化 -- 类别散布图、类别内数据分布、类别内统计图
  78. 78.交互式数据可视化工具Bokeh介绍
  79. 79.Bokeh绘制散点图、柱状图、盒子图、弦图
  80. 80.Bokeh绘制常用图形元素
  81. 81.3D绘图 -- mplot3d
  82. 82.3D曲线可视化
  83. 83.3D散点图可视化
  84. 84.3D柱状图可视化
  85. 85.Pandas绘图
  86. 86.实战:Lending Club借贷数据探索性分析及可视化
  87. 87.机器学习介绍及应用场景
  88. 88.机器学习建模介绍 (1) -- 分类
  89. 89.机器学习建模介绍 (2) -- 回归
  90. 90.机器学习建模介绍 (3) -- 聚类
  91. 91.机器学习分类
  92. 92.机器学习工具scikit-learn
  93. 93.使用scikit-learn的流程
  94. 94.数据集准备及划分
  95. 95.模型选择
  96. 96.数据预处理及特征工程
  97. 97.过拟合与欠拟合
  98. 98.模型调参介绍
  99. 99.模型调参方法
  100. 100.模型测试及评价
  101. 101.实战:通过移动设备行为数据预测性别和年龄

模型测试及评价

约 18 分钟

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

模型测试及评价

小象实战讲义 · Python数据分析实战

在机器学习项目中,构建模型只是第一步,如何科学地评估模型性能才是关键。本节我们将深入探讨模型测试与评价的多种指标,理解为何“准确率”并非万能,并学会在分类任务中根据实际需求选择合适的评价标准。

💡 核心导读

  • 准确率的局限性:理解为何在类别不平衡数据集中,高准确率可能具有误导性。
  • 二分类评价核心概念:掌握真正例(TP)、假正例(FP)、真反例(TN)、假反例(FN)的定义与混淆矩阵。
  • 关键评价指标:学会计算和解读精确率(Precision)、召回率(Recall)以及综合指标F1值。
  • 多分类评估工具:了解混淆矩阵在多分类问题中的应用,直观分析模型在各类别上的表现。

准确率并非万能

在之前的课程中,我们通常使用准确率(Accuracy) 来评价分类模型。准确率计算简单,易于理解,公式为预测正确的样本数除以总样本数:

准确率=预测正确的样本数总样本数\text{准确率} = \frac{\text{预测正确的样本数}}{\text{总样本数}}

然而,准确率是一个“整体”指标,在某些场景下会掩盖模型的关键问题。最典型的场景就是类别不平衡数据集。

一个极端的例子

假设我们有一个包含1000个样本的数据集,其中999个是正样本(例如,正常交易),只有1个是负样本(例如,欺诈交易)。如果我们构建一个“愚蠢”的模型,它总是预测“正样本”,那么它的预测结果会是:

  • 999个正样本全部预测正确。
  • 1个负样本预测错误。

此时,模型的准确率高达 999/1000=99.9%999/1000 = 99.9\%。单看准确率,这似乎是一个极其优秀的模型。但实际上,这个模型完全无法识别我们最关心的欺诈交易(负样本)。

现实中的不平衡任务

类似场景在现实中很常见:

  • 信用卡欺诈检测:绝大多数交易是正常的,欺诈交易极少。
  • 离职员工预测:大部分员工会稳定工作,只有少数会提前离职。
  • 疾病筛查:健康人群远多于患病人群。

在这些任务中,我们往往更关心少数类(正样本或负样本)的识别能力。因此,仅凭准确率评价模型是远远不够的,我们需要更精细的评价指标。

二分类评价指标详解

为了深入评估模型,我们首先需要定义四个基础概念,它们都源于混淆矩阵(Confusion Matrix) 的四个象限。

基础概念:TP, FP, TN, FN

对于一个二分类问题(假设正类为1,负类为0):

  • 真正例 (True Positive, TP):真实值为正(1),预测值也为正(1)。模型正确预测的正样本。
  • 假正例 (False Positive, FP):真实值为负(0),但预测值为正(1)。模型误将负样本预测为正样本(误报)。
  • 真反例 (True Negative, TN):真实值为负(0),预测值也为负(0)。模型正确预测的负样本。
  • 假反例 (False Negative, FN):真实值为正(1),但预测值为负(0)。模型误将正样本预测为负样本(漏报)。
混淆矩阵结构(行:真实值,列:预测值):
              预测为正(1)     预测为负(0)
真实为正(1)      TP              FN
真实为负(0)      FP              TN

对角线上(TP, TN)是预测正确的样本,对角线外(FP, FN)是预测错误的样本。

精确率 (Precision) 与 召回率 (Recall)

基于以上四个基本计数,我们定义两个核心指标:

  1. 精确率 (Precision) - 查准率 问题:在所有被模型预测为正的样本中,有多少是真正的正样本? 公式:Precision=TPTP+FP\text{Precision} = \frac{TP}{TP + FP} 意义:衡量模型预测正类的准确性。精确率高意味着模型“不乱报”,预测出的正类可信度高。

  2. 召回率 (Recall) - 查全率 问题:在所有真实的正样本中,有多少被模型成功找出了? 公式:Recall=TPTP+FN\text{Recall} = \frac{TP}{TP + FN} 意义:衡量模型找出正类样本的全面性。召回率高意味着模型“不漏报”,能找出大部分正类。

实例解析:学生性别预测

假设一个班级有100名学生,真实情况是男生70人,女生30人。我们用一个分类器(根据身高体重)预测性别,得到如下结果(图示):

  • 被预测为男生的有60人。
  • 在这60人中,实际是男生的有40人(TP),实际是女生的有20人(FP)。
  • 在真实的70名男生中,有40人被正确预测为男生(TP),30人被错误预测为女生(FN)。
  • 在真实的30名女生中,有10人被正确预测为女生(TN),20人被错误预测为男生(FP)。

根据定义计算:

  • 准确率 = (TP+TN)/总数 = (40+10)/100 = 50%
  • 精确率 = TP/(TP+FP) = 40/(40+20) = 66.67%
  • 召回率 = TP/(TP+FN) = 40/(40+30) = 57.14%

这个例子清晰地展示了三个指标的不同侧重点。

F1值:精确率与召回率的调和平均

精确率和召回率通常相互制约:提高阈值可能提升精确率但降低召回率,降低阈值则相反。为了用一个数值综合衡量两者,我们使用F1值,它是精确率和召回率的调和平均数。

公式:F1=2×Precision×RecallPrecision+RecallF1 = 2 \times \frac{\text{Precision} \times \text{Recall}}{\text{Precision} + \text{Recall}}

调和平均数对极端值更敏感。F1值越高,说明模型在精确率和召回率之间取得了更好的平衡。

使用Scikit-learn计算评价指标

sklearn.metrics 模块提供了计算上述所有指标的便捷函数。让我们通过一个完整的代码示例来演示。我们将使用一个模拟的、小规模的乳腺癌数据集(良性/恶性二分类)来对比K近邻(KNN)和支持向量机(SVM)模型。

# 导入必要库
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import LinearSVC
from sklearn.metrics import (accuracy_score, precision_score,
                             recall_score, f1_score, confusion_matrix)

# 构造一个小型模拟数据集(替代原课中的breast_cancer.csv)
# 特征数量:5(模拟原数据集的30个特征),样本数:150(便于演示)
np.random.seed(42) # 固定种子,确保可复现,仅用于构造数据,不用于评估
n_samples = 150
n_features = 5

# 生成特征数据
X = np.random.randn(n_samples, n_features)
# 生成标签:引入一些可预测的模式,使模型学习有意义
# 简单规则:如果前两个特征之和大于某个阈值,则标签为1(恶性),否则为0(良性)
coef = np.array([1.5, 1.0, 0.0, 0.0, 0.0]) # 只有前两个特征有效
y_proba = 1 / (1 + np.exp(-(X @ coef + 0.5))) # 模拟逻辑回归概率
y = (y_proba > 0.5).astype(int)

# 查看类别分布
print("类别分布:")
print(pd.Series(y).value_counts())
print(f"特征矩阵形状: {X.shape}")
类别分布:
1    92
0    58
Name: count, dtype: int64
特征矩阵形状: (150, 5)
# 划分训练集和测试集,并训练两个对比模型
# 注意:每个代码块完全独立,重复导入和构造数据是为了满足自包含要求
import numpy as np
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import LinearSVC

# 再次构造相同的数据(自包含要求)
np.random.seed(42)
n_samples = 150; n_features = 5
X = np.random.randn(n_samples, n_features)
coef = np.array([1.5, 1.0, 0.0, 0.0, 0.0])
y_proba = 1 / (1 + np.exp(-(X @ coef + 0.5)))
y = (y_proba > 0.5).astype(int)

# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=1/3, random_state=42)

# 初始化模型
knn_model = KNeighborsClassifier()
svm_model = LinearSVC(max_iter=10000, random_state=42) # 增加迭代次数确保收敛

# 使用网格搜索为KNN寻找最优的K值
knn_params = {'n_neighbors': [1, 3, 5, 7, 9]}
knn_clf = GridSearchCV(knn_model, knn_params, cv=3, scoring='accuracy')
knn_clf.fit(X_train, y_train)
best_knn = knn_clf.best_estimator_
print(f"KNN最优参数: {knn_clf.best_params_}")

# 使用网格搜索为SVM寻找最优的正则化参数C
svm_params = {'C': [0.01, 0.1, 1, 10, 100]}
svm_clf = GridSearchCV(svm_model, svm_params, cv=3, scoring='accuracy')
svm_clf.fit(X_train, y_train)
best_svm = svm_clf.best_estimator_
print(f"SVM最优参数: {svm_clf.best_params_}")
KNN最优参数: {'n_neighbors': 7}
SVM最优参数: {'C': 1}
# 使用最优模型进行预测并计算各类指标
import numpy as np
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score

# 为了自包含,重新构造数据、划分、训练最优模型(模拟流程)
np.random.seed(42)
n_samples = 150; n_features = 5
X = np.random.randn(n_samples, n_features)
coef = np.array([1.5, 1.0, 0.0, 0.0, 0.0])
y_proba = 1 / (1 + np.exp(-(X @ coef + 0.5)))
y = (y_proba > 0.5).astype(int)

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=1/3, random_state=42)

# 使用上面网格搜索得到的最优参数直接初始化模型
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import LinearSVC
best_knn = KNeighborsClassifier(n_neighbors=7)
best_svm = LinearSVC(C=1, max_iter=10000, random_state=42)

best_knn.fit(X_train, y_train)
best_svm.fit(X_train, y_train)

y_pred_knn = best_knn.predict(X_test)
y_pred_svm = best_svm.predict(X_test)

# 1. 计算准确率
acc_knn = accuracy_score(y_test, y_pred_knn)
acc_svm = accuracy_score(y_test, y_pred_svm)
print("【准确率 Accuracy】")
print(f"KNN: {acc_knn:.3f}")
print(f"SVM: {acc_svm:.3f}")
print()

# 2. 计算精确率
pre_knn = precision_score(y_test, y_pred_knn)
pre_svm = precision_score(y_test, y_pred_svm)
print("【精确率 Precision】")
print(f"KNN: {pre_knn:.3f}")
print(f"SVM: {pre_svm:.3f}")
print()

# 3. 计算召回率
rec_knn = recall_score(y_test, y_pred_knn)
rec_svm = recall_score(y_test, y_pred_svm)
print("【召回率 Recall】")
print(f"KNN: {rec_knn:.3f}")
print(f"SVM: {rec_svm:.3f}")
print()

# 4. 计算F1值
f1_knn = f1_score(y_test, y_pred_knn)
f1_svm = f1_score(y_test, y_pred_svm)
print("【F1值】")
print(f"KNN: {f1_knn:.3f}")
print(f"SVM: {f1_svm:.3f}")
【准确率 Accuracy】
KNN: 0.700
SVM: 0.960

【精确率 Precision】
KNN: 0.821
SVM: 1.000

【召回率 Recall】
KNN: 0.697
SVM: 0.939

【F1值】
KNN: 0.754
SVM: 0.969

结果分析:

  • 在这个模拟数据集上,SVM全面优于KNN:准确率0.960对0.700,精确率1.000对0.821(SVM没有把任何良性样本误判为恶性)。
  • 召回率上SVM同样领先(0.939对0.697),漏判的恶性样本明显更少。
  • 综合来看,SVM的F1值(0.969对0.754)更高,表明其整体表现更均衡。
  • 注意:实际任务中,我们需要根据业务目标选择重点关注的指标。例如,在癌症筛查中,我们可能宁愿多一些误报(低精确率),也要确保极少漏报(高召回率)。

混淆矩阵与多分类评估

上述精确率、召回率、F1值主要针对二分类问题。对于多分类问题,一个非常直观且强大的工具是混淆矩阵。

混淆矩阵的作用

混淆矩阵是一个N×N的方阵(N为类别数),其中:

  • 行代表数据的真实类别。
  • 列代表模型的预测类别。
  • 对角线上的元素表示各类别被正确分类的样本数。
  • 非对角线上的元素则表示被错误分类的样本数,并清晰显示了被误分到了哪个类别。

解读混淆矩阵

假设我们对鸢尾花数据集(3个类别)进行分类后得到如下混淆矩阵(示意):

混淆矩阵(行:真实类别,列:预测类别):
          预测:类0  预测:类1  预测:类2
真实:类0     10        0        0
真实:类1      0        9        1
真实:类2      0        0       10

解读:

  • 类0:10个样本全部预测正确(准确率100%)。
  • 类1:10个样本中,9个预测正确,1个被错误预测为类2(准确率90%)。
  • 类2:10个样本全部预测正确(准确率100%)。

通过混淆矩阵,我们可以一目了然地发现模型的短板。例如,上例中模型主要混淆了类1和类2。后续优化工作就可以聚焦于如何提升模型对类1和类2的区分能力。

代码示例:输出混淆矩阵

# 计算并输出混淆矩阵
import numpy as np
from sklearn.metrics import confusion_matrix

# 沿用上一个代码块中已经生成的预测结果 y_test, y_pred_knn, y_pred_svm
# 为满足自包含,这里重新生成一次(实际应用中不需要)
np.random.seed(42)
n_samples = 150; n_features = 5
X = np.random.randn(n_samples, n_features)
coef = np.array([1.5, 1.0, 0.0, 0.0, 0.0])
y_proba = 1 / (1 + np.exp(-(X @ coef + 0.5)))
y = (y_proba > 0.5).astype(int)

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=1/3, random_state=42)

from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import LinearSVC
best_knn = KNeighborsClassifier(n_neighbors=7)
best_svm = LinearSVC(C=1, max_iter=10000, random_state=42)

best_knn.fit(X_train, y_train)
best_svm.fit(X_train, y_train)

y_pred_knn = best_knn.predict(X_test)
y_pred_svm = best_svm.predict(X_test)

# 计算混淆矩阵
cm_knn = confusion_matrix(y_test, y_pred_knn)
cm_svm = confusion_matrix(y_test, y_pred_svm)

print("【混淆矩阵 Confusion Matrix】")
print("KNN混淆矩阵:")
print(cm_knn)
print("\nSVM混淆矩阵:")
print(cm_svm)

# 添加标签以便于阅读 (假设类别名为'良性'(0)和'恶性'(1))
print("\n--- 带标签的KNN混淆矩阵 (行:真实值, 列:预测值) ---")
import pandas as pd
cm_df_knn = pd.DataFrame(cm_knn,
                         index=['真实_良性', '真实_恶性'],
                         columns=['预测_良性', '预测_恶性'])
print(cm_df_knn)
【混淆矩阵 Confusion Matrix】
KNN混淆矩阵:
[[12  5]
 [10 23]]

SVM混淆矩阵:
[[17  0]
 [ 2 31]]

--- 带标签的KNN混淆矩阵 (行:真实值, 列:预测值) ---
       预测_良性  预测_恶性
真实_良性     12      5
真实_恶性     10     23

解读KNN混淆矩阵:

  • 真实为“良性”(0)的样本有17个(12+5),其中12个被正确预测,5个被错误预测为“恶性”(1)(误报,FP)。
  • 真实为“恶性”(1)的样本有33个(10+23),其中23个被正确预测,10个被错误预测为“良性”(0)(漏报,FN)。
  • 对照SVM的混淆矩阵[[17, 0], [2, 31]]:同样17+33的测试分布下,SVM仅漏报2例、零误报——这正是它召回率0.939、精确率1.000的来源。混淆矩阵让我们对模型“错在哪、错多少”一目了然。

📝 动手练一练

  1. 概念辨析:在医疗诊断模型中(将患病预测为正类),“假正例(FP)”和“假反例(FN)”分别对应哪种临床错误?哪种错误在癌症早期筛查中可能后果更严重?
  2. 指标计算:根据下面提供的微型混淆矩阵,请手动计算精确率(Precision)、召回率(Recall)和F1值。
    真实\预测   正类   负类
       正类     8      2
       负类     1      9
👉 点击查看参考答案
  1. 概念辨析:

    • 假正例(FP):真实健康,但模型预测为患病。对应临床上的误诊(假阳性)。
    • 假反例(FN):真实患病,但模型预测为健康。对应临床上的漏诊(假阴性)。
    • 在癌症早期筛查中,漏诊(FN)的后果通常更严重,因为患者会因未能及时治疗而延误病情。因此,这类模型往往追求更高的召回率(Recall),即使这会以降低一些精确率(增加误诊)为代价。
  2. 指标计算:

    • 从矩阵可得:TP=8, FP=1, FN=2, TN=9。
    • 精确率 = TP / (TP+FP) = 8 / (8+1) ≈ 0.889
    • 召回率 = TP / (TP+FN) = 8 / (8+2) = 0.800
    • F1值 = 2 * (PrecisionRecall) / (Precision+Recall) = 2 * (0.8890.8) / (0.889+0.8) ≈ 0.842

本章小结

本节我们深入探讨了机器学习模型,特别是分类模型的评价体系。我们认识到,准确率虽然直观,但在类别不平衡或业务代价不对称的场景下具有很大局限性。

📋 行动清单

  • 理解核心概念:确保能清晰解释TP, FP, TN, FN,并能从混淆矩阵中找出它们。
  • 掌握指标计算与应用场景:能计算精确率、召回率和F1值,并思考在你的目标项目中,哪个指标应作为首要优化目标。
  • 实践工具使用:使用sklearn.metrics中的accuracy_score, precision_score, recall_score, f1_score, confusion_matrix函数,对你训练的任何一个分类模型进行多维度评估。
  • 分析模型短板:对于多分类问题,养成查看混淆矩阵的习惯,快速定位模型表现最差的类别,指导后续优化方向。

记住,没有放之四海而皆准的“最佳指标”。最好的评价指标始终与你的具体业务目标紧密相连。选择合适的指标,才能驱动模型向真正有价值的方向进化。

—— 小象教研组

配套资料 · 直接下载

课件、代码、数据集等配套资料全部免费,点击即可直接下载,无需分享或解锁。

  • 本节课件:模型测试及评价
    PDF · 304KB
    下载
  • 全套课件打包(第1-5章)
    ZIP · 12.8MB
    下载
  • 全套课件打包(第6-8章)
    ZIP · 15MB
    下载
  • 实战数据集:AppleStore 应用商城分析
    ZIP · 329KB
    下载
  • 实战数据集:女性服装电商分析
    ZIP · 2.8MB
    下载
  • Python 数据分析环境搭建指南
    PDF · 2MB
    下载
  • Scrapy 安装教程
    PDF · 12.7MB
    下载
  • 附加实战项目:AppleStore 应用商城数据分析
    ZIP · 0.3MB · ipynb + CSV 数据
    下载
  • 附加实战项目:银行电话营销数据分析
    ZIP · 0.4MB · ipynb + CSV 数据
    下载
  • 附加实战项目:女性服装电商评论数据分析
    ZIP · 2.7MB · ipynb + CSV 数据
    下载
  • 附加实战项目:美国化学学会杂志数据分析
    ZIP · 34.2MB · ipynb + SQLite 数据库
    下载