模型一心想要变小的那个数字
你已经知道,一个模型接收输入、产生预测,并在训练过程中调整自己的参数以表现得更好。但「表现得更好」对机器来说实在太模糊了。计算机没法去追逐一种「在进步」的感觉,它只能追逐一个数字。损失函数就是这个数字:一个单一的标量,用来给模型此刻对某一个样本的预测打分,衡量它错得有多离谱。越低越好;为零就意味着完全命中。
有三个词常被几乎不加区分地混用,把它们各自钉清楚会很有帮助。损失(loss)通常指单个样本上的误差。代价(cost)是一个批次或整个数据集上损失的平均值——也就是我们真正想要最小化的东西。目标(objective)则是优化器最终处理的那个表达式,它往往是代价再加上一些额外项(比如对复杂模型的惩罚)。日常交流里,人们用这三个词都指「我们要弄小的那个东西」,这没问题——只要记住:优化器最终总会落到一个具体的数字上,去把它往下推。
回归问题:用 MSE 把差距平方
当模型预测的是一个数值——房价、明天的气温、一个人的年龄——我们就用预测值和真实标签之间的差距来衡量错误。最经典的选择是均方误差(MSE):取每一个差距,把它平方,再对所有样本求平均。平方一下子干了两件事。它让每个误差都变成正数(这样一个偏高 3 的预测就不会和一个偏低 3 的预测相互抵消),并且它对大偏差的惩罚远比小偏差狠得多——误差为 10 要付出 100 的代价,误差为 1 却只付出 1。
这种狠劲是把双刃剑,对此保持诚实很重要。因为单个巨大的误差会主导整个平均值,MSE 对离群点非常敏感——一个被标错的或诡异的数据点,就能把整个模型往它那边拽。当你为此担心时,平均绝对误差(它对未平方的距离求平均)会更平等地对待所有误差,并对离群点不以为然。这里没有放之四海皆准的最佳答案;没有免费午餐定理提醒我们:合适的损失取决于你的数据,以及犯错在你这个世界里要付出的代价。
MSE 还有一个不动声色的优点,使它成为线性回归以及成千上万其他模型的默认选择:它光滑且呈碗状。它的斜率变化得温和而可预测,而这正是下一篇要讲的梯度下降找到下山之路所需要的。一个让优化器容易导航的损失,价值不可小觑。
分类问题:为什么我们不直接数错了几个
现在假设模型必须选一个类别:是不是垃圾邮件、是猫还是狗、是十个数字中的哪一个。你真正在意的诚实成绩单是准确率——它答对了几成?但准确率作为学习用的损失却糟糕透顶。它几乎处处是平的:把某个参数轻微挪动一点,答对的数量通常根本不变,于是没有斜率能告诉优化器该往哪个方向迈步。我们需要一个损失,它对模型「当时有多自信」做出平滑的反应,而不只是看它最后对没对。
解决办法是让模型输出概率——比如,「这封邮件是垃圾邮件」的概率为 0.9——通常二分类用 sigmoid、多分类用 softmax 来实现。然后我们用交叉熵(也叫对数损失)给它打分。这个想法美得很简单:只看模型分配给正确答案的那个概率,损失就是这个概率的负对数。自信且正确(概率接近 1)几乎不付出代价。自信却错误(它给真实类别的概率接近 0)则要付出巨大代价,因为一个极小数字的对数会向负无穷俯冲。
# cross-entropy for one example
p_true = model_probs[ correct_class ] # e.g. 0.9
loss = -log( p_true ) # 0.9 -> 0.105 (cheap)
# 0.01 -> 4.6 (painful)
# average this over all examples to get the cost这并不是一个随意拼凑的公式。最小化交叉熵在数学上等价于极大似然估计——让观测到的数据在模型下尽可能地有可能发生——而且它本质上就是真实标签与模型猜测之间的 KL 散度中真正起作用的那部分。正是这一深层联系,让交叉熵(而非原始的准确率)训练了几乎所有分类器,从逻辑回归一直到庞大的语言模型。
从单个样本到一条学习法则:经验风险
损失给单个样本打分,但我们想要的是一个全面表现良好的模型。在理想世界里,我们会去最小化模型在所有可能输入构成的整个宇宙上的平均损失——也就是它的真实风险。可我们做不到:我们从来看不到那整个宇宙,只看得到我们收集来的数据集。于是我们退而求其次,去最小化我们实际拥有的数据上的平均损失。这个平均值就是经验风险,而最小化它正是整个游戏的核心,这套做法有个气派的名字:经验风险最小化。
- 挑一个与任务匹配的损失函数(数值用 MSE,类别用交叉熵)。
- 让训练数据流过模型,并为每个样本计算损失。
- 把这些损失求平均,得到经验风险——一个总结了「总错误量」的数字。
- 调整参数让这个数字变小,然后重复。
损失能告诉你什么,又不能告诉你什么
一条不断下降的损失曲线让人感觉是在进步,通常也确实如此——但要带着怀疑的眼光去读它。损失是你拿来优化的量,它很少是人类最终真正在意的那个量。一个垃圾邮件过滤器也许刷出一条漂亮的交叉熵曲线,却悄悄放过了那封唯一要命的诈骗邮件,因为损失把每封邮件都看得同等重要。当类别严重失衡时——比如 99% 都是正常邮件这样的类别不平衡——模型完全可以靠彻底无视那个稀有类别来取得很低的损失。
正是这道鸿沟,让损失和评估指标各过各的日子。你针对一个光滑、可微的损失来训练,因为优化器需要斜率;你却用你真正在意的指标来评判——准确率、精确率、召回率、省下的钱——哪怕这些指标坑坑洼洼、根本没法直接拿来优化。一个明智的工作流程总会先立一个基线(一个胡乱瞎猜的预测器能拿到多少损失?),这样你才能看出,一个花哨的模型究竟是真的物有所值,还是不过在复述显而易见的东西。
所以请对损失抱以恰如其分的敬重:它是那个不可或缺的指南针,把模糊的「表现得更好」变成机器能够遵循的具体方向,而你现在已经认识了驱动大多数现代学习的那两个损失。但指南针不等于目的地。损失一旦选定,剩下唯一的问题就是:到底该如何挪动参数才能把它弄小——而这恰恰是下一篇要踏上的那段下坡之旅。