分类、交叉熵,以及如何不被自己骗倒
用第 2 章的损失与第 3 章的下降构建 logistic 分类器,并看清为何 98% 准确率也可能什么都没找到。
本页内容
一个模型对传送带上下来的每个零件都回答 这个零件没问题,它有 98.15% 的时间是对的。它也毫无价值:测试集里的 74 个缺陷件,它一个都没抓到。
这两句话描述的是同一个模型。它们之间的距离,就是本章要讲的内容。
前半部分构建分类器。它几乎不需要新东西:第 2 章给出了从“数据如何产生”的假设推导出损失函数的配方,第 3 章给出了沿着这个配方交给你的任意损失往下走的机器。把两者用到一个是/否问题上,logistic 回归就自然出现了,外加一个新概念 —— logit —— 它会在第 17 章再次收费。
后半部分更难。从课程的这一点往后,一切都由某个人测出来的数字来评判;如果你分不清真正的改进和测量伪影,那么后面的每一章都只是装饰。所以:混淆矩阵、精确率和召回率、三份数据切分、泄漏,以及那个几乎没人诚实回答的问题 —— 我到底需要多少测试样本?
这里的算术跑在 20,000 行数据上,所以全程都向量化 —— NumPy 从第 2 章起就在干活,从这里开始也不值得再特别说明。
传送带,以及一个更稀有的问题
链接到此部分:传送带,以及一个更稀有的问题还是第 1 章的那家工厂,但问题更难。现在不再问 接收还是拒收,而是问 这个零件是否有缺陷 —— 缺陷很少见,这让本章的测量部分很难,也让建模部分显得具有欺骗性的简单。
import numpy as np
rng = np.random.default_rng(4)
N = 20_000
width = rng.normal(22.0, 0.9, N) # millimetres
weight = rng.normal(57.0, 3.0, N) # grams
z_true = -5.90 + 1.90 * (width - 22.0) + 0.42 * (weight - 57.0)
y = (rng.random(N) < 1 / (1 + np.exp(-z_true))).astype(float)
perm = rng.permutation(N)
train, val, test = perm[:12_000], perm[12_000:16_000], perm[16_000:]N = 20000 defects = 337 base rate = 0.0169
defects per split = 203 60 74三份切分,不是两份。原因值得单独讲一节,下面就会讲;现在先在第一份上训练,在第二份上调参,不要看第三份。
特征经过标准化 —— 减去均值,再除以标准差 —— 而且只使用训练集统计量。原因就是第 1 章用感知机的收敛界展示过的:未居中的数据会让几何形状变得不友好。你被允许从哪些行里计算这个均值,会在本章后面变成一个真实问题。
从判决到概率
链接到此部分:从判决到概率感知机返回一个符号。符号无法区分 拒收 和 拒收,但只是差一点,而这正是工厂决定哪些零件应该优先由人工复检时需要的差异。
所以照着第 2 章的配方来。写下你声称标签如何产生,取似然,取对数,取负数,你就得到了一个损失。对于是/否结果,这个声称是一个 Bernoulli 分布:零件有一个概率 是缺陷件,并且
这只是把“如果 就是 ,如果 就是 ”写得紧凑一些。对它取对数再取负,一个样本的损失就是
这就是二元交叉熵。它不是因为方便才被选中;它是一次硬币投掷唯一可能分布的负对数似然。没有别的选择。
还缺的是 从哪里来。模型计算一个加权和 ,这是一个实数,范围覆盖整条实数轴,而概率必须落在 中。负责在两者之间转换的函数是 logistic sigmoid:
logit -4.0 -> p = 0.0180 loss when y=1 and p=0.9 : 0.1054
logit -1.0 -> p = 0.2689 loss when y=1 and p=0.5 : 0.6931
logit 0.0 -> p = 0.5000 loss when y=1 and p=0.01 : 4.6052
logit 4.0 -> p = 0.9820把右栏读成价格表。以 90% 置信度答对,成本是 0.105。拒绝表态,成本是 0.693 —— 也就是 ,耸肩的价格。自信地答错,成本是 4.6,高出四十四倍;而且随着模型对错误越来越确定,价格会无限上升。交叉熵不只是数错误:它会向傲慢收费。
gradient 是预测减真值
链接到此部分:gradient 是预测减真值第 3 章说过:要训练任何东西,就求损失对每个参数的导数。对一个样本来做。令 且 :
查看详情
让混乱相消的两行。 sigmoid 有一个异常好用的导数,。而损失求导得到
按链式法则把两者相乘, 在分子和分母各出现一次。它们完全相消,留下的是 。这种相消不是巧合 —— 只要损失是某个分布的负对数似然,且输出函数正是该分布自然使用的函数,就会发生这种事。这一配对有个名字 —— 广义线性模型 —— 而整洁的 gradient 就是它的指纹。1
所以更新就是预测减真值,再乘以输入。没有别的。下面是完整训练器,也就是第 3 章的 descent,只改了一行:
def sigmoid(z):
return np.where(z >= 0, 1.0 / (1.0 + np.exp(-z)),
np.exp(np.minimum(z, 0)) / (1.0 + np.exp(np.minimum(z, 0))))
def fit_logistic(X, y, lr=0.5, epochs=4000):
w, b = np.zeros(X.shape[1]), 0.0
for _ in range(epochs):
p = sigmoid(X @ w + b)
g = p - y
w -= lr * (X.T @ g) / len(y)
b -= lr * g.sum() / len(y)
return w, bsigmoid 里的 np.where 不是装饰。直接计算 会在很大的负 上溢出;这个分支会选择代数上等价、但能让指数保持为负的形式。这是第 2 章的浮点数盒子第一次讨债,两节之后它还会收一笔更大的。
为什么不用平方误差,以及为什么答案关乎 gradient
链接到此部分:为什么不用平方误差,以及为什么答案关乎 gradient偏好交叉熵而不是平方误差的标准解释,是上面的似然论证:平方误差来自高斯噪声假设,而标签不是高斯的,所以不要用。这个解释是对的,但说服不了任何人,因为你完全可以在 sigmoid 上写 ,它也会训练。
真正有说服力的论证在 gradient。把平方误差放在 sigmoid 上,链式法则给出
额外的这个 ,就是前面相消掉的那个。现在它没有相消,并且只要模型很自信,它就会趋近于零 —— 包括模型自信地错了的时候。对一个真实标签为 1 的样本,在几个分数上评估两者:
| 分数 | 交叉熵 | 平方误差 | 比值 | |
|---|---|---|---|---|
| 0.000335 | 1,491 | |||
| 0.017986 | 28.3 | |||
| 0.119203 | 4.8 | |||
| 0.500000 | 2.0 | |||
| 0.880797 | 4.8 |
在 处,模型已经错到不能再错,而平方误差给出的 gradient 比交叉熵小 1,491 倍。错误越严重,模型从中学到的越少。相反,交叉熵的 gradient 会在 处饱和:最大程度地错误产生最大程度的信号,但不会更大。
跑一场比赛。两千个平衡样本,相同的初始权重,而且这些权重被选成自信地错误(),相同的学习率,只有损失不同。两次运行都用交叉熵评分,所以列之间可比。
| epoch | 交叉熵损失 | 准确率 | 平方误差损失 | 准确率 |
|---|---|---|---|---|
| 1 | 5.4865 | 0.2300 | 5.9499 | 0.2290 |
| 10 | 1.5525 | 0.2460 | 5.9042 | 0.2290 |
| 50 | 0.4642 | 0.7780 | 5.6913 | 0.2320 |
| 100 | 0.4639 | 0.7770 | 5.3955 | 0.2410 |
| 200 | 0.4639 | 0.7770 | 4.6311 | 0.2745 |
| 500 | 0.4639 | 0.7770 | 0.5291 | 0.7660 |
| 1,000 | 0.4639 | 0.7770 | 0.4640 | 0.7765 |
交叉熵到 epoch 50 就完成了。平方误差在 epoch 100 仍然只有 24% 准确率 —— 而且从 epoch 10 的 23% 起几乎没动 —— 比猜还差,因为它一开始就自信地错了,而本该拯救它的 gradient 被乘上了 0.0007。它大约到 epoch 500 才逃出来,并最终落到同一个位置。所以诚实的总结是:sigmoid 上的平方误差并非不正确;它是恰好在速度最重要的地方很慢。在一个双参数模型上,你损失 450 个 epoch。在一个百层网络里,总会有某个地方的某个单元自信地错着,于是你损失的是整次训练。
Entropy、交叉熵和 KL,一页讲完
链接到此部分:Entropy、交叉熵和 KL,一页讲完三个量,在第 8 章讲困惑度时需要正确使用,在第 11 章讲让 fine-tuned policy 靠近其 reference 的惩罚时也需要。它们比名声中听起来容易。2
Entropy 是:如果你为一个分布使用可能的最佳编码,那么传递从该分布抽出的一次结果,平均需要花多少比特:
交叉熵 是:当数据实际来自 ,但你使用为 构建的编码时,要花多少:
KL 散度 是多出来的部分 —— 因为相信 而真相是 所造成的浪费,单位是比特:
在传送带上检查这三个量:
test defect rate = 0.0185
entropy of that coin = 0.1329 bits
cross-entropy of the constant predictor on test = 0.1330 bits
KL(test coin || fair coin) = 0.8671 bits
H + KL = 1.0000 bits
cross-entropy of the p=0.5 predictor on test = 1.0000 bits这里能看到两件事。第一,一个只报告训练集基准率 1.69% 的模型,取得了 0.1330 bit 的交叉熵,几乎正好等于测试标签的 entropy —— 也必然如此,因为它有正确的分布,却没有关于个体的其他信息。Entropy 是“对个体一无所知”能买到的下限。第二,一个耸耸肩说 0.5 的模型,正好支付 1 bit;两者之间的差距 0.8671 bit,正是 KL 散度。 不是一个要背的恒等式;它是一张你能看着逐项累加起来的账单。
再连回训练:当标签是某个已知的单一类别时,“真实”分布是 one-hot,它的 entropy 为零,因此交叉熵等于 KL 散度。最小化交叉熵与把模型分布拉向真相,是同一件事。
不止两个答案:softmax,以及不花代价的平移
链接到此部分:不止两个答案:softmax,以及不花代价的平移缺陷不是一种东西。在注塑中,一个零件可能是 short shot(材料不足)、flash(材料过多,被挤出模具),或 burn。四种结果,所以有四个 logits,它们必须变成四个加起来为一的概率。这就是 softmax:
它有一个看起来像意外、其实是整个实现核心的性质:
对任意常数 都成立,因为 和 在上下相消。只有 logits 之间的差异才有意义。绝对水平不是信息。
幸好如此,因为绝对水平正是会让计算机坏掉的东西:
logits = [800. 801. 799.]
naive softmax = [nan nan nan]
shifted by -max = [0.2447 0.6652 0.09 ]
same softmax after adding 1000 to every logit: True 会让 64 位浮点数溢出,和变成无穷大,而无穷大除以无穷大是 nan —— 不是错误,不是崩溃,只是三个概率原本所在的位置变成了一个静默的洞。减去最大 logit 在数学上不改变任何东西,但在数值上改变一切,因为最大的指数会正好变成 。这是第 2 章的 logsumexp 技巧穿上工装,所有严肃实现都会这么做:
def softmax(Z):
Z = Z - Z.max(axis=1, keepdims=True)
E = np.exp(Z)
return E / E.sum(axis=1, keepdims=True)
def fit_softmax(X, Y, lr=1.0, epochs=6000):
W, b = np.zeros((X.shape[1], Y.shape[1])), np.zeros(Y.shape[1])
for _ in range(epochs):
G = (softmax(X @ W + b) - Y) / len(X)
W -= lr * (X.T @ G)
b -= lr * G.sum(0)
return W, bgradient 又一次是预测减真值,只不过现在 是 one-hot。二元情形原来一直只是一个特例。
在 3,000 个零件上训练、1,000 个零件上测试,每个零件有三项测量(宽度、重量、熔体温度),它达到 94.00% 准确率。下面是这个数字藏起来的东西:
| 真实 ↓ / 预测 → | ok | short shot | flash | burn | 召回率 |
|---|---|---|---|---|---|
| ok | 850 | 5 | 9 | 0 | 0.984 |
| short shot | 22 | 21 | 0 | 0 | 0.488 |
| flash | 20 | 0 | 30 | 1 | 0.588 |
| burn | 3 | 0 | 0 | 39 | 0.929 |
| 精确率 | 0.950 | 0.808 | 0.769 | 0.975 |
模型找到的 short shot 不到一半。准确率看不见这一点,因为 86% 的零件是好的,把这些做对就足以撑起平均值。Macro F1 —— 各类别 F1 分数的平均值,它对稀有类别和常见类别赋予同等权重 —— 是 0.7983,而 micro F1 是 0.9400,按定义它与准确率相同。只要有人报告一个 F1 数字,就问是哪一种。
建模部分到这里结束。剩下的章节都在讲数字。
三个模型,一个准确率
链接到此部分:三个模型,一个准确率取训练好的二元模型,再把每个 logit 乘以一个常数,做出两个变体:0.35 表示犹豫版,4 表示过度自信版。乘以正数不会改变任何符号,所以三个模型对全部 4,000 个测试零件预测出完全相同的标签。准确率无法区分它们。交叉熵却毫不费力:
| 模型 | 准确率 | 交叉熵 | 答对时的平均损失 | 答错时的平均损失 | 最大单项损失 |
|---|---|---|---|---|---|
| 犹豫版(logits × 0.35) | 0.9830 | 0.1549 | 0.1369 | 1.1990 | 2.80 |
| 按训练结果 | 0.9830 | 0.0564 | 0.0147 | 2.4689 | 7.82 |
| 过度自信版(logits × 4) | 0.9830 | 0.1563 | 0.0009 | 9.1427 | 27.63 |
犹豫的模型在每个零件上都交一点小税,包括它答对的数千个零件。过度自信的模型答对时几乎免费,答错时灾难性昂贵 —— 测试集里有一个零件单独就让它付出 27.63 nats。两者从相反路径抵达几乎相同的总数,而经过训练、概率与数据校准的模型,比两者都低三倍。
这是说明损失和指标差异最锋利的方式。损失是你优化的东西:它必须可微,并且能看到模型说出的全部内容,包括它有多确定。指标是你被评判的东西:它可以是阶跃函数、业务规则、漏检缺陷数。它们不是同一个对象,也不总是一致 —— 所以你要在开始前定义两者,绝不要因为损失恰好显示在屏幕上,就让它代替指标。
愚蠢的 baseline 先上场
链接到此部分:愚蠢的 baseline 先上场在任何模型之前,先问要求:最懒的答案能得多少分? 在这条传送带上,就是永远说没问题:
always-say-fine baseline: accuracy = 0.9815
confusion (tn, fp, fn, tp) = (3926, 0, 74, 0)98.15%。现在看训练好的 logistic 模型,在默认阈值 0.5 下:
logistic @0.5: accuracy=0.9830 precision=0.8000 recall=0.1081 F1=0.1905
confusion (tn, fp, fn, tp) = (3924, 2, 66, 8)98.30%。它比 baseline 高了 0.15 个百分点,任何只停在准确率的报告都会称之为胜利。混淆矩阵说明实际发生了什么:
| 预测没问题 | 预测有缺陷 | |
|---|---|---|
| 实际没问题 | 3,924 | 2 |
| 实际有缺陷 | 66 | 8 |
它在 74 个缺陷件里找到了 8 个,放过了 66 个。有三个数字对应这张表的三种读法:
- 精确率 。它标记的零件中,有多少真的有缺陷。这是浪费检查的成本。
- 召回率 。缺陷件中,它抓到了多少。这是把坏零件发给客户的成本。
- F1 ,两者的调和平均,会贴近较小的那个,因此拒绝被单独一个好看的数字奉承。
哪个重要取决于工厂,而不是数学:一次检查只花几秒,一个发出去的缺陷件却可能带来召回通知,所以这里召回率占主导,0.108 是失败。
但模型不是问题。阈值才是,而阈值不是模型的一部分 —— 它是在概率之后施加的业务决策。扫描它:
| 阈值 | TP | FP | FN | 准确率 | 精确率 | 召回率 | F1 |
|---|---|---|---|---|---|---|---|
| 0.500 | 8 | 2 | 66 | 0.9830 | 0.800 | 0.108 | 0.190 |
| 0.200 | 27 | 28 | 47 | 0.9812 | 0.491 | 0.365 | 0.419 |
| 0.100 | 42 | 118 | 32 | 0.9625 | 0.263 | 0.568 | 0.359 |
| 0.050 | 54 | 236 | 20 | 0.9360 | 0.186 | 0.730 | 0.297 |
| 0.020 | 67 | 570 | 7 | 0.8558 | 0.105 | 0.905 | 0.188 |
| 0.005 | 71 | 1,360 | 3 | 0.6593 | 0.050 | 0.959 | 0.094 |
向下读准确率这一列。它一路下降 —— 从 98.30% 到 65.93% —— 同时模型从抓到 8 个缺陷变成抓到 74 个中的 71 个。这个模型能做的每一件有用的事,都会让准确率变差。 一个优化标题数字的团队,会发布那个什么都找不到的版本。
查看详情
类别加权不会创造信号,它只会移动操作点。 面对类别不平衡,通常的第一反应是在损失中给稀有类别加权。对正例分别使用权重 1、10 和 60:
| 正例权重 | 准确率 | 精确率 | 召回率 | F1 | AUC |
|---|---|---|---|---|---|
| 1 | 0.9830 | 0.800 | 0.108 | 0.190 | 0.9363 |
| 10 | 0.9605 | 0.253 | 0.581 | 0.352 | 0.9361 |
| 60 | 0.8290 | 0.091 | 0.919 | 0.166 | 0.9361 |
精确率和召回率移动了很远。AUC —— 模型把一个随机缺陷件排在一个随机好件之上的概率,它完全忽略阈值 —— 只移动了 0.0002,等于没动。重新加权只是把同一个模型沿着同一条权衡曲线滑动。这通常正是你想要的,但它从来不是新信息:如果排序本身很差,没有任何加权方案能救它。
三份切分,以及你马上要发现的泄漏
链接到此部分:三份切分,以及你马上要发现的泄漏为什么是三份切分而不是两份?因为你一旦使用一组样本来选择任何东西 —— 阈值、学习率、六个模型里要发布哪个 —— 这组样本就已经被用于拟合,它的分数就不再无偏。3 在这条传送带上测量:在验证集上扫描阈值选出 0.196,然后模型在未触碰的测试集上得到 F1 = 0.4122。如果直接在测试集上扫描,那里能达到的最佳值是 0.4186 —— 这是任何人都无权报告的数字。
这里的差距很小,0.006,因为这只是一个超参数,在 4,000 个验证样本上扫描了一次。每多做一个决定、验证集每缩小一点,它都会增大。还要注意,单次运行里的方向并不保证:被选中的阈值在验证集上得 0.3902,在测试集上得 0.4122,所以这一次验证集反而低估了它。偏差是在许多决策上的系统现象,不一定在一次中可见。4
现在来练习。传送带日志带来了第三列,station_seconds:每个零件在检查站停留了多久。把它加入预处理只需要改一行。效果如下:
| 模型 | 准确率 | 精确率 | 召回率 | F1 | 交叉熵 | AUC |
|---|---|---|---|---|---|---|
| 宽度 + 重量 | 0.9830 | 0.800 | 0.108 | 0.190 | 0.0564 | 0.9363 |
| + station_seconds | 0.9920 | 0.792 | 0.770 | 0.781 | 0.0236 | 0.9970 |
召回率从 10.8% 到 77.0%。F1 提高了四倍多。还要注意准确率做了什么:98.30% → 99.20%,提升 0.9 个点,这种数字在总结幻灯片里很容易被四舍五入成“两边都差不多 99%”。准确率之前没能看出失败,现在也没能看出欺诈。
继续读之前:模型在作弊。找出它怎么作弊。
如何追查泄漏,按最快找到它的顺序。
-
比较训练和测试。 过拟合会表现为很大的差距。这里:诚实模型 0.9838 train / 0.9830 test;泄漏模型 0.9936 train / 0.9920 test。两个差距都低于 0.2 个点。泄漏看起来不像过拟合 —— 泄漏特征在测试时同样可用,所以模型能漂亮地泛化到一个并不存在的世界。
-
每次只用一个特征单独训练一个模型。 任何携带答案的东西都会自己跳出来:
单独特征 准确率 召回率 F1 AUC width 0.9815 0.014 0.026 0.8691 weight 0.9815 0.000 0.000 0.7914 station_seconds0.9850 0.405 0.500 0.9960 只有一列,单独使用时就能以 AUC 0.9960 排序缺陷件。卡尺和秤测出的两个量只能做到 0.87 和 0.79。这种不对称就是警报。
-
问每个数字是什么时候写下来的。 平均停留时间:通过的零件 2.23 秒,失败的零件 15.56 秒。 当然如此。零件停在检查站,是因为检查员把它从传送带上拿了下来 —— 这发生在之后,而且只因为,有人已经判定它有缺陷。这一列不是零件的测量值。它是判决的测量值。
station = 1.8 + rng.exponential(0.35, N) # a part just passing through
audited = rng.random(N) < 0.006 # random spot checks
station[audited] += rng.uniform(6.0, 26.0, audited.sum())
station[y == 1] = 9.0 + rng.exponential(7.0, (y == 1).sum()) 高亮的那一行就是泄漏:缺陷件的停留时间来自不同分布,因为人工把它从传送带上拿走了。这是应用机器学习中最常见的严重 bug,而且它有名字:target leakage —— 训练特征中包含了在必须做出预测的时刻并不可用的信息。5 它不会抛异常。它会产生更好的数字。项目里的每一种激励都会指向把它留下来。
防御方法是对每一列都问一个问题:在我需要这个预测的那一刻,这个值已经存在了吗? 在一条实时传送带上,station_seconds 要等到零件被检查之后才知道 —— 而这正是模型本该替代的事情。
我需要多少测试样本?
链接到此部分:我需要多少测试样本?假设你在 20 个样本上给模型打分,它答对 17 个。你报告 85%。
17 correct out of 20 -> accuracy 0.8500
Wilson 95% CI : [0.6396, 0.9476]
bootstrap 95% CI : [0.7000, 1.0000]
P(a 65% model scores 17 or more out of 20) = 0.0444
P(an 85% model scores 17 or more out of 20) = 0.6477对 17/20 的诚实解读是:大概在 64% 到 95% 之间。一个真实水平为 65% 的模型,有 4.4% 的概率产生这个结果 —— 二十三次里大约一次 —— 如果你试了几个 prompts 然后报告最好的那个,你就是自己制造了这次运行。二十个里对十七个,无法区分一个 85% 的模型和一个 65% 的模型。
有两种给比例加区间的方法,两者都应该在你的工具箱里:
def wilson(k, n, z=1.959963985):
"""95% interval for k successes in n trials. Correct at small n; no simulation."""
ph, d = k / n, 1 + z * z / n
centre = (ph + z * z / (2 * n)) / d
half = z * (ph * (1 - ph) / n + z * z / (4 * n * n)) ** 0.5 / d
return centre - half, centre + half
def bootstrap_ci(correct, n_resamples=10_000, alpha=0.05, seed=0):
"""95% interval for the mean of any per-example score array. Works on F1 too."""
rng = np.random.default_rng(seed)
correct = np.asarray(correct, dtype=float)
draws = correct[rng.integers(0, len(correct), size=(n_resamples, len(correct)))]
lo, hi = np.quantile(draws.mean(axis=1), [alpha / 2, 1 - alpha / 2])
return float(correct.mean()), float(lo), float(hi)对普通成功率使用 Wilson6;它在任意 下都表现良好,而且不需要随机性。注意上面当 时,bootstrap 的上端是 1.0000 —— 对 20 个点重采样,很容易抽到 20 个全对,因此它无法表示比自身粒度更窄的区间。在没有公式的地方使用 bootstrap7,而这包括大多数有趣的情况:F1、macro 平均、BLEU、pass@1、基于 rubric 的 judge 分数。在这条传送带上,调好阈值的模型 F1 为 0.4122,对应的 bootstrap 区间是 [0.3009, 0.5156] —— 这才应该出现在报告里,因为单独的点估计会邀请一种它无法支持的比较。
再做一次测量,因为它会改变你比较两个模型的方式。两个模型在同样的 500 个样本上打分:
model A: 0.8580 95% CI [0.8260, 0.8880]
model B: 0.8120 95% CI [0.7780, 0.8460]
the two intervals overlap: True
paired difference A-B: 0.0460 95% CI [0.0260, 0.0680]
they disagree on 31 of 500 examples (A right 27, B right 4)它们的区间重叠了,民间规则 —— 误差条重叠就表示没有显著差异 —— 会把这个比较判为没有结论。但这不对。两个模型跑在同一批样本上,所以正确的量是逐样本差异,它的区间是 [0.0260, 0.0680],稳稳高于零。它们只在 500 项中的 31 项上意见不一致,而 A 赢下了其中 27 项;共享的样本,无论容易还是困难,都会相互抵消,而不是增加噪声。成对比较模型,你可以用一小部分数据得出同样结论。
接下来去哪里
链接到此部分:接下来去哪里你现在有了一个输出校准概率的模型,一个从数据主张推导而来、不是为了方便而选择的损失,一个字面上就是预测减真值的 gradient,并且 —— 更重要的是 —— 有了判断这一切是否有效的机器。上面的十行 Wilson 区间会原封不动地复用:它承载第 15 章里的 prompt 变体、第 19 章里的检索表,以及第 29 章里的 golden set。没有公式时,就该拿出 bootstrap。
但模型仍然只有一层。它画的是一条线,而第 1 章已经用 XOR 的四行证明过,一条线不够。修复方法是堆叠:第一层弯曲空间,第二层在弯曲后的空间里画线。
本章整洁的 gradient 到这里就用完了。上面所有东西之所以能工作,是因为对于输入和损失之间只有一层的模型, 可以手写出来,而且只要写一次。把第二层放到中间后,问题就变了形:对一个根本不直接接触输出的权重,损失的导数是什么 —— 这个权重的影响只通过另一层传来,甚至可能同时沿着多条路径传来?
这个导数存在。对任何大于玩具的东西,手工计算都没希望;而一次算一个参数,在另一个尺度上也没希望。需要的是一个过程:它能沿着 forward pass 刚刚走过的同一张图,做一次 backward pass,就得到网络中的每一个导数。
这就是第 5 章,也是本课程余下部分运行其上的引擎。
来源与方法
链接到此部分:来源与方法也值得与本章一起阅读:Bishop,Pattern Recognition and Machine Learning §1.2、§1.5、§1.6 和 §4.3,它按本章遵循的顺序覆盖概率、决策理论、信息论和线性分类;Murphy,Probabilistic Machine Learning: An Introduction,第 6 和第 10 章;Prince,Understanding Deep Learning §5.4–5.7;以及 Saito 和 Rehmsmeier,The Precision-Recall Plot Is More Informative than the ROC Plot When Evaluating Binary Classifiers on Imbalanced Datasets(PLOS ONE,2015)—— 当只有 1.7% 的零件有缺陷时,为什么上面引用的 AUC 不应该是你查看的唯一一个无阈值数字。
参考资料
链接到此部分:参考资料-
Ma, T. 和 Ng, A. CS229 Lecture Notes,Stanford University,第 2 和第 3 章。让产生 的相消不再像运气的地方:选择与你的输出匹配的指数族分布,使用它的 canonical link,gradient 就永远是预测减真值。 ↩
-
Olah, C. Visual Information Theory(2015),
colah.github.io/posts/2015-09-Visual-Information。这是目前最清晰的讲解之一:把 entropy、交叉熵和 KL 散度解释为比特成本,而不是公式。 ↩ -
Abu-Mostafa, Y. S., Magdon-Ismail, M. 和 Lin, H.-T. Learning From Data(AMLBook,2012),Caltech 课程第 13 和第 17 讲。第 13 讲是验证;第 17 讲讨论三条学习原则,并在其中命名了 data snooping。两者共同构成本章的纪律来源:每一次查看数据集都是一次拟合决策,无论你是否运行了优化器。 ↩
-
James, G., Witten, D., Hastie, T. 和 Tibshirani, R. An Introduction to Statistical Learning,第 2 版(Springer,2021),第 2 和第 5 章,关于 bias–variance 分解和重采样。配套卷明确写出了选择陷阱:Hastie, Tibshirani 和 Friedman,The Elements of Statistical Learning,第 2 版,§7.10.2,The Wrong and Right Way to Do Cross-validation。 ↩
-
Kaufman, S., Rosset, S., Perlich, C. 和 Stitelman, O. Leakage in Data Mining: Formulation, Detection, and Avoidance. ACM Transactions on Knowledge Discovery from Data 6(4),2012。对上面演示的失败给出了形式化处理,并包含一些竞赛案例:获胜模型学到的是数据如何组装的伪影。 ↩
-
Wilson, E. B. Probable Inference, the Law of Succession, and Statistical Inference. Journal of the American Statistical Association 22(158),第 209–212 页(1927)。上文
wilson()使用的 score interval,至今仍是比例的正确默认选择。教科书区间 才是应该避开的那个:它在接近 0 和 1 时会给出荒唐结果,并且在小 下严重 undercovers。 ↩ -
Efron, B. Bootstrap Methods: Another Look at the Jackknife. The Annals of Statistics 7(1),第 1–26 页(1979)。这个思想让你能为任何可计算的统计量加上区间,包括那些没有抽样理论的统计量。 ↩