AI 工程地基 09|越意外越值钱:损失函数的单位是比特
「AI 工程地基」系列第九篇。啃的是 GitHub 58k star 的开源课 ai-engineering-from-scratch,Rohit Ghumare 写的,一课一课来。今天第 09 课:信息论,Learn 型,60 分钟,前置是第 06 课概率。这一课把 CrossEntropyLoss() 拆开——你…
「AI 工程地基」系列第九篇。啃的是 GitHub 58k star 的开源课 ai-engineering-from-scratch,Rohit Ghumare 写的,一课一课来。今天第 09 课:信息论,Learn 型,60 分钟,前置是第 06 课概率。这一课把 CrossEntropyLoss() 拆开——你每天最小化的那个数,原来有单位。
拿一枚偏到 99% 的硬币。正面朝上,没人惊讶;反面朝上,全场哗然。信息论给「惊讶」这件事标了精确的价签:概率 0.5 的事值 1 比特,概率 1/6 的事值 2.58 比特,千分之一的小事值 9.97 比特,必然事件值 0。公式就一行,负对数。越不可能的事发生了,越值钱。偏硬币的正面只值 0.0145 比特,反面值 6.64 比特——同一枚硬币,两个结果差了四百多倍。
惊喜可以单算,更常见的是打包算平均。熵就是一个分布的平均惊喜:公平硬币每次翻开都值 1 比特,偏硬币平均只值 0.08 比特。熵低意味着你基本猜得中,每次揭晓都学不到什么。这门课给了一个工程读法:熵是不可消除的不确定性,你想压都压不掉。
然后是你每天在用的那个。交叉熵:世界按真实分布 P 出牌,你按模型的报价单 Q 结账,平均多付的惊喜就是它。分类任务里 P 是 one-hot,公式塌缩成一行:负对数乘上真实类的预测概率。课程 Step 3 的实测数字:logits [2.0, 1.0, 0.1] 过完 softmax 是 [0.659, 0.242, 0.099],真实类是第 0 类,损失 0.417 纳特。纳特就是换了底的比特,一纳特等于 1.4427 比特,PyTorch 内部记的全是纳特。
交叉熵和熵之间夹着 KL 散度:多付的那部分。账本关系一行写完,交叉熵减熵等于 KL。训练时真实分布不动,熵是常数,所以最小化交叉熵和最小化 KL 是同一件事——你在把模型的分布往真实分布上推。课程 Step 2 的两组数字很能说明问题:好模型 [0.6, 0.25, 0.15] 对真实 [0.7, 0.2, 0.1],KL 只多付 0.033 比特;坏模型 [0.1, 0.1, 0.8] 要多付 1.87 比特。KL 还不对称,我实测了一遍:同一对分布,正着算 1.8651 比特,反着算 2.0193 比特。它不是距离,是两个方向各有各的浪费。
往上再叠一层是互信息:知道一个变量,能把对另一个变量的不确定性砍掉多少。独立变量互信息为零;课程给的对照矩阵里,一组成对概率 [[0.45, 0.05], [0.05, 0.45]],互信息 0.531 比特。特征选择看的就是这个数,而且它比相关系数狠:皮尔逊只抓线性关系,互信息抓任何统计依赖。互信息对称、非负,这两个性质 KL 都没有。
课程还给了一个现代配方:标签平滑。把硬标签 [0, 0, 1, 0] 换成 [0.025, 0.025, 0.925, 0.025],意思是「真实类大概率是对的,但别把话说死」。从信息论看,这是给目标分布的熵抬价,从零抬到正数。为什么有用:交叉熵下想完美匹配 one-hot 需要无穷大的 logits,模型被逼着把话说死;给个软目标,它就不用一路推到无穷。
为什么交叉熵是分类的标配损失?课程给了三个视角。信息论视角:它度量你的模型当编码器比真实分布多浪费多少比特。最大似然视角:负对数似然逐字逐句就是交叉熵,最小化它等于最大化数据的似然。梯度视角:softmax 配交叉熵,梯度是预测减真实,一行干净利落。三个视角同一件事,这门课的结构感就在这。
最后是困惑度,交叉熵取个指数。它把抽象的损失翻译成人话:一个困惑度 50 的语言模型,平均而言像在 50 个候选词里均匀地猜。论文里报的 perplexity 就是这个。
过一遍海关。先说干净的部分。这门课的数字我全部复跑了一遍:偏硬币熵 0.0808 比特,公平骰子 2.585,好模型交叉熵 1.1896,坏模型 3.0219,KL 0.0328 和 1.8651,互信息 0 和 0.5310——逐个对上,零误差。标签平滑那个损失公式的恒等式,两种写法算到小数点后六位完全一致。惊喜表格、单位换算、one-hot 化简,全对。六十分钟的课程,公式层面没有一处水分。
第一处在那句「你没法压缩到熵以下」。这句话丢了两个字:无损。有损压缩天天在熵以下跑,JPEG 就是,付的代价是失真。无损这句本身还藏着第二层:它是渐近意义上的下界,要靠块编码才贴得住。偏硬币的熵是 0.08 比特,但任何单符号编码方案每翻一次都至少花 1 比特——差了十二倍,不违反下界,因为你得把一千次翻转捆成一件事来编,才能把平均码长压到熵附近。Huffman 单符号只保证不超过熵加一。这句话的完整版是:无损压缩的平均码长不低于熵,有损可以任意低,代价是失真。课程省掉的两个字,恰好是 JPEG 和 ZIP 的分界线。
第二处在接口。cross_entropy 接收两个分布,用 zip 对齐——Python 的 zip 遇到长度不等的输入会静默截断。我试了:真分布三个数,模型分布只给了两个,函数安静地返回 0.9159 比特,第三项无声消失,不报错。熵函数也不检查归一化:传 [0.5, 0.5, 0.5] 进去,和是 1.5,照样出数,给出 1.5 比特——「二值变量熵最大 1 比特」这个刚学到的结论,被非法输入当场突破,没人拦。information_content(1.5) 返回 0.0,非法概率被静默当成必然事件。函数签名允许的世界,比断言成立的世界宽。这不是这一课的错,是教学代码的通病,但恰好值得在这课点名:一个度量不确定性的函数,自己就不确定输入是不是合法。
第三处,Step 4 标题写着「交叉熵等于负对数似然」,做法是把同一公式跑了两遍。两路代码调的是同一个 softmax、同一个负对数,输出差值精确为 0——连浮点噪声都没有。恒等式本来就该相等,但这个演示没有对账力,它是把一个式子抄了两遍。真要验证恒等式,得跨实现:手写的 NLL 对上 torch.nn.CrossEntropyLoss,两个独立的实现给出同一个数,那才叫对上了账。
第四处是个小口径:GPT-2 困惑度约 30。论文的口径是 WikiText-103 零样本,GPT-2 家族四个尺寸分别是 37.50、23.33、17.48、16.34。「约 30」落在最小的两档之间,没说尺寸也没说数据集。方向没错,现代模型的困惑度确实在个位数,但这个 30 是个没有户口的数。
最后是口号审计。这课开篇说训练神经网络是一个通信问题,模型在把正确标签通过有噪信道传出去。这个帽子站得住,而且课程后文自己就把三顶帽子摆齐了:通信、似然、梯度。真正藏话的是互信息性质列表里那句口语解释:「观察一个东西永远不会丢信息。」I(X;Y) 非负没错,但这话容易听成「多看总不亏」。数据处理不等式说的是另一回事:观察不丢,加工会丢。信号 X 经过 Y 再加工成 Z,I(X;Z) 不会超过 I(X;Y),只降不升。「多加特征总没坏处」这个特征工程里的经典错觉,就是被这句口语喂大的——垃圾特征不是负贡献,是零贡献加计算成本,而且一过模型这个有损加工环节,有效信号还可能被稀释。
练习 2 的答案可以先剧透,因为手算出来比题目本身有画面。logits [5.0, 2.0, 0.5],真实类是 1。softmax 给出 [0.9426, 0.0469, 0.0105]——模型把 94% 的信心押给了第 0 类,真类只分到 4.7%。损失是负对数乘 0.0469,等于 3.06 纳特,困惑度取个指数是 21.3。三个选项的题,模型的困惑像在二十一个里挑。至于什么样的 logits 给出零损失:有限数字给不出来,需要真类的 logit 和其余的差距趋向无穷。零损失是渐近线,不是终点站。