CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
机器学习入门/课时/模型评估
RawBlame
课时目录12 节

机器学习入门

  1. 01机器学习是什么
  2. 02数学基础
  3. 03Python 与数据栈
  4. 04数据预处理
  5. 05监督学习
  6. 06无监督学习
  7. 07模型评估
  8. 08Scikit-learn 工作流
  9. 09深度学习基础
  10. 10NLP 与 Transformer
  11. 11TensorFlow 与 PyTorch
  12. 12综合练习

模型评估

准确率、F1、ROC-AUC、K-Fold。

35 分钟

模型评估

这一课你将能做到

  1. 看懂准确率、精确率、召回率、F1、AUC 各自衡量什么,遇到具体业务知道选哪个
  2. 用混淆矩阵和分类报告定位模型「错在哪一类」
  3. 通过调整决策阈值,在精确率和召回率之间做出符合业务代价的取舍
  4. 用交叉验证稳定评估模型,并识别过拟合与欠拟合

为什么要学

模型训练出来只是个数字对象,能不能上线取决于评估。评估做错,后面全错:一个把所有用户都判为「未掌握」的模型,在 92% 的人确实未掌握时准确率高达 92%,但它毫无用处——这正是本站做题场景会遇到的真数据形态(多数用户大部分题未掌握)。

很多初学者第一次翻车就翻在「用准确率评估不平衡数据」。本课把常用指标全部过一遍,并建立关键认知:指标不是背出来的,是按业务代价权衡出来的。

先建立图像

混淆矩阵(二分类,以「是否掌握」为例):

                 预测: 掌握         预测: 未掌握
真实: 掌握        TP(说对了)       FN(漏报:该推的题没推)
真实: 未掌握      FP(误报:乱推难题) TN(说对了)

精确率 = TP / (TP+FP)   「说掌握的里面有多少真掌握」→ 关乎推题体验
召回率 = TP / (TP+FN)   「真掌握的里面找出了多少」  → 关乎别漏掉用户

跟着做

步骤 1:制造不平衡数据,先踩一次坑

import numpy as np
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.dummy import DummyClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score

# 模拟做题记录:仅 8% 的样本是「已掌握」
X, y = make_classification(n_samples=1000, n_features=6,
                           weights=[0.92, 0.08], flip_y=0.02, random_state=42)
X_tr, X_te, y_tr, y_te = train_test_split(
    X, y, test_size=0.25, stratify=y, random_state=42)  # 不平衡必须 stratify

dummy = DummyClassifier(strategy="most_frequent").fit(X_tr, y_tr)
print("全猜多数类的准确率:", accuracy_score(y_te, dummy.predict(X_te)))
# 输出约 0.92 —— 一个什么都不学的模型准确率也很高!

步骤 2:逻辑回归 + 混淆矩阵 + 分类报告

from sklearn.metrics import classification_report, confusion_matrix

clf = LogisticRegression(max_iter=1000).fit(X_tr, y_tr)
y_pred = clf.predict(X_te)

print(confusion_matrix(y_te, y_pred))
print(classification_report(y_te, y_pred,
      target_names=["未掌握", "已掌握"], digits=3))

重点看「已掌握」那一行:precision、recall、f1。比例失真时 accuracy 无意义,这三列才是判断依据。

步骤 3:调阈值,做精确率/召回率权衡

proba = clf.predict_proba(X_te)[:, 1]      # 预测「已掌握」的概率

for th in [0.2, 0.5, 0.8]:
    pred = (proba >= th).astype(int)
    cm = confusion_matrix(y_te, pred)
    tp, fp, fn = cm[1, 1], cm[0, 1], cm[1, 0]
    print(f"阈值={th}  精确率={tp/(tp+fp):.3f}  召回率={tp/(tp+fn):.3f}")

你会看到:阈值调高 → 精确率升、召回率降;调低则相反。业务翻译:推难题场景要精确率(乱推会伤体验),挽回流失用户场景要召回率(宁可多推不错漏)。阈值是产品开关,不是模型参数。

步骤 4:ROC 曲线与 AUC

import matplotlib.pyplot as plt
from sklearn.metrics import roc_auc_score, RocCurveDisplay

print("AUC =", roc_auc_score(y_te, proba).round(3))
RocCurveDisplay.from_estimator(clf, X_te, y_te)
plt.show()

AUC 的含义:随机抽一个正样本和一个负样本,模型给正样本打分更高的概率。0.5 等于瞎猜,0.8 以上算不错。它的优点是与阈值无关,适合比较不同模型的整体排序能力。

步骤 5:交叉验证与过拟合诊断

from sklearn.model_selection import cross_val_score
from sklearn.tree import DecisionTreeClassifier

deep    = DecisionTreeClassifier(random_state=42)              # 不限深度
shallow = DecisionTreeClassifier(max_depth=3, random_state=42)

for name, m in [("不限深度", deep), ("max_depth=3", shallow)]:
    tr = m.fit(X_tr, y_tr).score(X_tr, y_tr)
    cv = cross_val_score(m, X_tr, y_tr, cv=5).mean()
    print(f"{name}  训练集准确率={tr:.3f}  5折CV={cv:.3f}")

不限深度的决策树训练集能到 1.000,CV 却低不少——典型的过拟合(把训练集的噪声也背下来了);两个都低则是欠拟合(模型太简单)。

讲清楚要点

  • 指标家族分工:F1 是精确率/召回率的调和平均,适合单一汇总;AUC 衡量「排序能力」与阈值无关;混淆矩阵回答「错在哪」。
  • 不平衡数据切分必须 stratify=y,否则少数类在测试集里占比不稳,指标无法复现。
  • 测试集是预算:每看一次、调一次,信息就泄漏一点。选超参数用验证集或交叉验证,测试集只在最终验收时碰一次。
  • 过拟合是默认风险:模型容量越大越会背答案,正则化、限深、更多数据都是解药。

你可能会懵的地方

Q1:AUC = 0.75 到底算好还是不好? 看基线和业务。本站难度评分这类强信号问题里 0.75 偏弱;而「预测用户是否会流失」这种弱信号问题里 0.75 已可用。别背标准,跟「瞎猜 0.5」和现有规则基线比。

Q2:什么时候 PR 曲线比 ROC 更靠谱? 极端不平衡时(少数类 < 5%)。ROC 的分母含大量 TN,曲线容易「虚胖」好看;PR 曲线只关注少数类,更诚实。风控、疾病筛查场景优先看 PR。

Q3:阈值 0.5 能改吗? 能。模型输出的是概率,predict 只是「概率 ≥ 0.5 就判正」的默认规则。上线后完全可以根据业务代价改阈值,这是最廉价的模型优化手段。

Q4:测试集能反复用吗? 不能。反复用测试集调参等于把答案抄给了模型。正确做法:训练/验证/测试三分,或交叉验证选参 + 测试集终验。

Q5:过拟合和欠拟合信号怎么记? 「训练差、验证差」= 欠拟合 → 换更强模型、加特征;「训练好、验证差」= 过拟合 → 正则化、减特征、加数据。

错法对照

错法后果正确做法
不平衡数据看 accuracy92% 的假繁荣看 precision/recall/F1/AUC + 混淆矩阵
在训练集上报告效果严重高估train/test 分离 + 交叉验证
反复用测试集调参测试集变相进训练集验证集或 CV 选参,测试集只碰一次
切分不分层指标抖动不可复现train_test_split(stratify=y)
只盯单一指标忽略错误分布先看混淆矩阵,再定优化方向

卡点急救

  1. 报错 cannot import name 'RocCurveDisplay' → sklearn 版本 < 0.24,用 from sklearn.metrics import plot_roc_curve 或升级。
  2. classification_report 出现 UndefinedMetricWarning → 某一类的预测数为 0(精确率分母为 0),说明阈值或模型有问题,不是代码错。
  3. cross_val_score 太慢 → 先在子样本上调通流程,或减小 cv 折数。

面试常问

Q1:精确率和召回率的取舍,举个例子? 垃圾邮件拦截宁可漏判(保精确率,别把正常邮件扔进垃圾箱);癌症筛查宁可错杀(保召回率,漏诊代价远大于误报)。取舍由错误代价决定,不由模型决定。

Q2:AUC 的统计学含义是什么? 随机取一正一负样本,模型给正样本打分更高的概率。它对阈值不敏感,衡量的是整体排序能力;但对不平衡数据的敏感性不如 PR 曲线。

Q3:为什么类别不平衡时不看 accuracy? 多数类占比本身就是 accuracy 的下界——全猜多数类即可拿到 92%,指标失去区分度。要用对少数类敏感的指标。

自测三问

  1. 一个模型召回率 1.0、精确率 0.08,它最可能怎么预测的?(几乎把所有样本都判为正类)
  2. AUC = 0.5 说明什么?(模型排序能力等同于随机瞎猜)
  3. 5 折交叉验证中,每份数据各当几次测试角色?(恰好 1 次)

小结一句话

评估的本质是把「模型错在哪、错了多贵」量化成业务看得懂的数字——指标服务于代价,而不是反过来。

延伸

  • 官方文档:scikit-learn 模型评估
  • 下一课预告:把预处理、调参、评估串成一条防泄漏的自动化流水线——Scikit-learn Pipeline 与 GridSearchCV。
加载本节练习…
← 上一课无监督学习下一课 →Scikit-learn 工作流