第 44 课 · 实战 · 19 分钟

Python 模型评估与准确率

只有知道指标测量了什么,分数才有意义。

训练/测试划分准确率精确率召回率混淆矩阵

分类器报告 92% 的准确率,可它漏掉的 8% 全是投诉。准确率把失败藏起来了。哪个指标能把它露出来?

模型在记住的数据上表现好,并不能证明什么。评估需要独立的测试集、明确的正类,以及与错误代价匹配的指标。准确率只是起点,不是完整报告。 你还会用 F1 综合精确率和召回率,并检查分组切片。

留出测试集

在拟合或调参之前先划分样本。测试集不应影响特征选择、缩放或阈值选择。数据很小时,应使用多折交叉验证并报告波动,而不是相信一次幸运划分。

rows = [("a", 1), ("b", 0), ("c", 1), ("d", 0), ("e", 1)]
cut = 3
train = rows[:cut]
test = rows[cut:]
print(len(train), len(test))
print(test)

明确、固定的划分

统计四种结果

二分类预测会落入真正例、真负例、假正例或假负例。要显式构造混淆矩阵。类别名称取决于你把哪一类称为正类,因此报告里要写清楚。

actual = [1, 1, 0, 0]
predicted = [1, 0, 0, 1]
tp = sum(a == 1 and p == 1 for a, p in zip(actual, predicted))
tn = sum(a == 0 and p == 0 for a, p in zip(actual, predicted))
fp = sum(a == 0 and p == 1 for a, p in zip(actual, predicted))
fn = sum(a == 1 and p == 0 for a, p in zip(actual, predicted))
print(tp, tn, fp, fn)

四个计数,而不是一个

计算精确率与召回率

精确率问的是预测为正的样本中有多少正确;召回率问的是真实正例中找到了多少。两者都可能被退化分类器操纵,因此要同时报告类别比例和判定阈值。

tp, fp, fn = 8, 2, 4
precision = tp / (tp + fp)
recall = tp / (tp + fn)
print(round(precision, 2))
print(round(recall, 2))

两个不同的问题

警惕不平衡数据上的准确率

如果一百个样本里只有五个正例,全部预测为负会得到 95% 准确率,却一个正例也没找到。任何亮眼数字都应先与简单基线比较,并检查每个类别的结果。

actual = [0] * 95 + [1] * 5
predicted = [0] * 100
accuracy = sum(a == p for a, p in zip(actual, predicted)) / len(actual)
print(round(accuracy, 2))
print(sum(1 for a, p in zip(actual, predicted) if a == 1 and p == 1))

高准确率,零召回

F1 平衡精确率与召回率

F1 是精确率和召回率的调和平均。只有两者都高时 F1 才高,因此当假阳性和假阴性都不能忽视时很有用。也要报告组成指标;F1 会掩盖到底是哪一侧失败。

tp, fp, fn = 8, 2, 4
precision = tp / (tp + fp)
recall = tp / (tp + fn)
f1 = 2 * precision * recall / (precision + recall)
print(round(precision, 2), round(recall, 2), round(f1, 2))

一个数字,两个成分

检查分组切片,而不只看总分

总体指标看起来尚可时,某个群体可能表现很差。按相关切片分组统计,但每组要有足够样本,避免过度解读噪声。切片是诊断手段,不是收集不必要个人数据的许可。

rows = [
    ("new", 1, 1), ("new", 1, 0), ("new", 0, 1),
    ("returning", 1, 1), ("returning", 0, 0), ("returning", 0, 0),
]
for group in sorted({row[0] for row in rows}):
    subset = [row for row in rows if row[0] == group]
    correct = sum(actual == predicted for _, actual, predicted in subset)
    print(group, correct, "/", len(subset), round(correct / len(subset), 2))

切片会藏在漂亮的平均值里

本课要点

  • 在训练和调参都没有使用过的数据上评估。
  • 混淆矩阵展示四种可能结果。
  • 精确率衡量预测为正的准确程度,召回率衡量正例被找到的比例。
  • 类别不平衡时,准确率可能看起来很高。
  • 报告分数时同时说明类别比例、阈值和简单基线。
  • F1 概括精确率与召回率的权衡;分组检查能暴露隐藏的失败。

常见错误

  • 反复在测试集上调参,直到它不再是测试集。
  • 报告准确率却不说明哪一类是正类。
  • 漏掉一个正例代价很高,却忽略假负例。
  • 只报告一个总体准确率,忽视一个虽小但重要的失败群体。
轮到你

给定真实和预测的二分类标签,计算混淆矩阵、准确率、精确率和召回率,并处理除零情况。

动手试试

改改变量、先预测输出,再运行代码。第一次点击“运行代码”时会下载 Python 运行时,可能需要几秒钟。

浏览器内 Python本地运行 · 无需账号
首次运行会从 CDN 下载 Pyodide。修改后点击“运行代码”。

练习本课

12 道题,作答后立即看到解释。

查看 12 道题答案开始练习
← 最近邻分类提示词校验 →