人人都能学编程
English 开始学习
专项 · 人工智能第 5 课 / 共 6 课免费阅读

衡量一个模型 · 人工智能基础

你的模型准确率 95%。它好用吗?

课程目录

为什么需要它

一个分数就可能掩盖一个没用的模型。如果你关心的情况(比如欺诈或者疾病)只占很少一部分,一个永远回答「不是」的模型看起来非常出色,却一个也抓不到。选对衡量标准,并在没见过的数据上检查它,你才能分清真正有用的模型和只是看起来不错的模型。

直觉理解

假设 100 笔交易里有 5 笔是欺诈。一个永远预测「不是欺诈」的模型对了 95 次:准确率 95%,抓到的欺诈:0 笔。

把结果分进四个格子,这就是混淆矩阵(1 = 欺诈,即「正类」):

预测为 1预测为 0
实际为 1真正例 (TP)假负例 (FN)
实际为 0假正例 (FP)真负例 (TN)

然后有两个问题很重要:

  • 精确率 = TP / (TP + FP):我们标记出来的里面,有多少是真的?
  • 召回率 = TP / (TP + FN):真的那些里面,我们抓到了多少?

它们互相拉扯。把所有东西都标记出来,召回率是完美的,精确率却很糟。F1 = 2 × P × R / (P + R) 把两者合成一个数,只有两个都高时它才高。

最后是过拟合:模型可能把训练数据连同噪声一起背下来。它的信号是差距很大,比如训练集上 99%,测试集上 60%。

定义

准确率

The share of all predictions that were right. Misleading when one class is rare.
所有预测中正确的比例。当某一类很少见时会误导人。

精确率

Of the cases predicted positive, the share that really were: TP / (TP + FP).
被预测为正类的里面,真正是正类的比例:TP / (TP + FP)。

召回率

Of the real positive cases, the share the model found: TP / (TP + FN).
真正的正类里面,被模型找出来的比例:TP / (TP + FN)。

过拟合

Learning the training data so closely, noise included, that the model does worse on new data.
把训练数据(连同噪声)学得太死,导致模型在新数据上表现更差。

示例

准确率陷阱

动手试试

永远回答「不是」能拿 95%,却什么也抓不到。

actual = [0] * 95 + [1] * 5
always_no = [0] * 100
correct = sum(1 for a, p in zip(actual, always_no) if a == p)
print("accuracy:", correct / len(actual))
caught = sum(1 for a, p in zip(actual, always_no) if a == 1 and p == 1)
print("fraud caught:", caught, "of", actual.count(1))
输出
accuracy: 0.95
fraud caught: 0 of 5
逐步执行
  1. L195 笔正常交易,5 笔欺诈(编的数据)。
  2. L4100 个里对了 95 个。accuracy: 0.95
  3. L6召回率是 0:一笔欺诈都没找到。fraud caught: 0 of 5

混淆矩阵

动手试试

数出四个格子,再算精确率和召回率。

actual    = [1, 1, 1, 0, 0, 0, 0, 1, 0, 0]
predicted = [1, 0, 1, 1, 0, 0, 0, 1, 0, 1]
pairs = list(zip(actual, predicted))
tp = pairs.count((1, 1))
fp = pairs.count((0, 1))
fn = pairs.count((1, 0))
tn = pairs.count((0, 0))
print("TP", tp, "FP", fp, "FN", fn, "TN", tn)
print("precision", tp / (tp + fp))
print("recall", tp / (tp + fn))
输出
TP 3 FP 2 FN 1 TN 4
precision 0.6
recall 0.75
逐步执行
  1. L3每一对是 (实际, 预测)。
  2. L5(0, 1):我们说是,其实不是。
  3. L9我们标记的 5 个里有 3 个是真的。precision 0.6
  4. L104 个真的里面我们抓到了 3 个。recall 0.75

一个靠死记的模型

动手试试

在训练数据上完美,在新数据上跟抛硬币差不多。

train = {(1, 1): "red", (2, 1): "red", (8, 8): "blue", (9, 8): "blue"}
test = {(1, 2): "red", (8, 9): "blue"}

def memoriser(point):
    return train.get(point, "red")

def accuracy(model, data):
    return sum(model(p) == label for p, label in data.items()) / len(data)

print("train", accuracy(memoriser, train))
print("test", accuracy(memoriser, test))
输出
train 1.0
test 0.5
逐步执行
  1. L4查表找这个点;如果是新点,就猜红色。
  2. L10每个训练点都在表里。train 1.0
  3. L11(8, 9) 显然是蓝色,可它不在表里。test 0.5

常见错误

只报告准确率

当某一类很少见时,即使是没用的模型,准确率也很高。要报告你关心的那一类的精确率和召回率。

除以零

一个从不预测正类的模型,TP + FP = 0。事先规定这时精确率是多少(通常是 0),而不是让程序崩溃。

在测试集上调参

如果你试了 50 种设置,留下测试分数最高的那个,测试集就变成了训练数据。调参要用单独的验证集。

小测验

1. 这段代码会输出什么?

actual    = [1, 0, 1, 1]
predicted = [1, 1, 0, 1]
tp = sum(1 for a, p in zip(actual, predicted) if a == 1 and p == 1)
fp = sum(1 for a, p in zip(actual, predicted) if a == 0 and p == 1)
print(round(tp / (tp + fp), 2))
  1. A
    0.67
  2. B
    0.5
  3. C
    0.75
查看答案

答案:A

TP = 2,FP = 1:精确率 2 / 3 ≈ 0.67。

2. 垃圾邮件过滤器必须几乎从不把正常邮件放进垃圾箱。哪个数最重要?

  1. A「垃圾邮件」这一类的精确率
  2. B「垃圾邮件」这一类的召回率
  3. C训练邮件上的准确率
查看答案

答案:A

精确率问的是:我们判为垃圾邮件的里面,有多少真的是?一个假正例就是一封丢掉的正常邮件。

3. 训练集准确率 99%,测试集准确率 60%。这是怎么回事?

  1. A过拟合
  2. B学习率太小
  3. C测试集太简单
查看答案

答案:A

模型学会的是训练数据本身,而不是规律。

动手练习

3 道练习,分三层:先预测程序的输出,再按新需求改程序,最后从零写一个。代码在浏览器里运行,提交马上判题。

  1. 预测输出数格子

    准确写出它打印的内容。

  2. 改需求修正召回率

    recall 用错了格子。把它改成 TP / (TP + FN)。如果根本没有真正的正类(TP + FN 为 0),返回 0.0。

  3. 从零写完整的报告

    写一个 report(actual, predicted)(两个由 0 和 1 组成的列表),返回一个包含 "precision"、"recall" 和 "f1" 的字典,每个都四舍五入到 3 位小数。

    • 分母为 0 的指标都是 0.0
    • F1 = 2 × P × R / (P + R),用的是没有四舍五入的 P 和 R
    report([1, 1, 1, 0, 0, 0, 0, 1, 0, 0], [1, 0, 1, 1, 0, 0, 0, 1, 0, 1])
    # {'precision': 0.6, 'recall': 0.75, 'f1': 0.667}

在互动课里练这一课

打开互动课,这些例子都能直接改、直接运行,练习题提交马上判题。免费开始,不用注册。

互动课按顺序解锁:前面的课还没学,会先带你到课程地图。

本课术语

术语中文英文定义
Accuracy准确率The share of predictions that were right.
Confusion matrix混淆矩阵Counts of TP, FP, FN and TN.
Precision精确率TP / (TP + FP): how many flagged cases were real.
Recall召回率TP / (TP + FN): how many real cases were found.
Overfitting过拟合Doing well on training data and badly on new data.