首页 / 资讯中心 / 文章详情

交叉熵、香农熵与相对熵:从编码成本看机器学习损失函数

交叉熵、香农熵与相对熵:从编码成本看机器学习损失函数 ★ FEATURED ARTICLE
我平时训练模型最常被同事问到的问题之一就是分类任务的损失函数是一串负对数求和为什么它叫“交叉熵”而不是“预测误差”说实话如果只是照着框架文档写损失函数这个问题可能永远不用回答但如果你想调模型调得明白、想看懂蒸馏和标签平滑那香农熵、交叉熵和相对熵这三个概念迟早要过一遍。这篇文章我就用“编码成本”这一条主线把这三兄弟串起来讲透尽量少堆公式多讲直觉和工程经验适合刚接触机器学习的读者也适合想彻底搞懂损失函数底层逻辑的朋友。1. 从“惊讶程度”到熵信息量的三个直觉1.1 一个反直觉的起点事件越稀罕信息量越大先别管熵的公式我们从最朴素的问题开始一条消息里到底包含了多少信息拿天气预报举例。某个热带城市常年晴天气象台每天播报“今日晴”这条消息对所有人都没有价值因为它没有消除任何不确定性但如果某天突然预报“今晚台风”这个信息的价值就非常大。同样的逻辑放在股市、医疗、推荐系统里都成立一个事件越是稀罕、越出乎意料它携带的信息量就越大。所以信息量和概率是反着来的事件概率越低信息量越高。最经典的定义是单事件信息量I(x) -log₂ p(x)底数取 2单位就是比特bit取自然对数 e单位就是奈特nat。为什么偏偏是取对数而不是 1/p 或者别的函数核心原因是可加性两个独立事件同时发生的概率是各自概率的乘积而对数恰好能把乘法变成加法——两件独立的事各自带来的信息合在一起应该是各自信息量相加这才符合直觉。一个事件概率为 1信息量为 0概率趋近于 0信息量趋近于无穷大这个定义天然合理。理解到这其实你已经拿到了整篇文章最关键的钥匙所谓熵、交叉熵、相对熵本质上都在回答“信息要用多少位来编码”这个问题。1.2 香农熵所有可能结果的信息量期望值单事件的信息量有了那整个随机变量的信息量呢答案很简单把所有可能结果的信息量按概率求加权平均。这就是香农熵H(X) - Σ p(x) log₂ p(x)它描述的是在知道这个随机变量的概率分布之后平均每条消息还剩下多少不确定性。如果明天天气只有两种可能且概率各半那把结果编码成一个比特就够了这时候 H(X)1 bit如果某种天气概率接近 1其他天气几乎不可能那不确定性就很小熵也就很小因为大多数时候你根本不用猜。拿一个二元随机变量举例正面概率 p、反面概率 1-p算出来的熵如下p(正面)熵 H(X)bit0.10.4690.20.7220.51.0000.90.469可以看到p0.5 时熵最大两种情况最难猜p 偏到两端时结果几乎确定熵就变小。这就是熵最重要的行为特征概率分布越均匀熵越大越集中熵越小。如果总共有 8 个等可能结果熵就是 log₂8 3 bit因为你需要 3 个二进制位才能区分开这 8 种情况。1.3 熵的最常用解释不确定度与最小编码熵还有一个特别实用的解读方式它是无损压缩的理论极限也就是“用二进制位表示这个随机变量的平均最短位数”。举个可动手算的例子。假设某分布有四档结果概率分别是 0.5、0.25、0.125、0.125。如果朴素地用 2 位二进制数去编码 4 种结果平均码长就是 2 位。但如果你用霍夫曼编码给概率最大的结果分配 1 位次大的分配 2 位最小的两个各分配 3 位平均码长就是0.5×1 0.25×2 0.125×3 0.125×3 1.75 位算一下它本身的熵0.5×1 0.25×2 0.125×3 0.125×3结果也是 1.75 bit。也就是说霍夫曼编码的平均长度正好逼近熵。任何无损压缩算法都不可能稳定地低于这个数值这就是香农第一定理的通俗版本。对做机器学习的人来说熵给出的是一个标尺它衡量了数据本身有多少随机性、有多少信息量是你无法压缩掉的。后面的交叉熵和相对熵都是在这个标尺之上做加减法。2. 交叉熵用你的近似分布去编码真实分布2.1 一个编码场景用错误的分布设计码表会多花多少位现在引入第二个分布。假设真实数据服从分布 p比如某个场景里“下雨”概率 0.2、“不下雨”概率 0.8。如果让一个不知道真实分布的人来设计编码他以为两个结果各占一半于是都分 1 位那么在传输“真实数据”的时候平均每条消息需要 1 位但实际上最少只需要 0.722 位。多出来的那部分就是因为编码者使用了错误的分布 q 而产生的“冤枉钱”。一般的公式也很直观H(p, q) - Σ p(x) log₂ q(x)这个量就叫交叉熵。第一眼可能会觉得它有两个参数容易混乱。记住一个动作就清楚了左边下标 p 表示“这些样本来自真实分布 p”右边 log 里面放的是“你用来编码的近似分布 q”。中文里念成“用模型分布 q 去编码真实分布 p 的期望代价”就不会搞反。更小的例子p[0.2, 0.8]q[0.5, 0.5]交叉熵是H(p,q) -(0.2×log₂0.5 0.8×log₂0.5) 1 bit而真实熵只有 0.722 bit多出来的 0.278 bit 就是近似分布 q 带来的额外开销。如果你把 q 调成接近 p交叉熵就会慢慢逼近熵。当 q 和 p 完全相等时交叉熵就等于熵本身。2.2 分类任务的损失函数为什么是交叉熵机器学习里的分类问题几乎都是这个编码场景的变形真实标签的分布 p 是固定的例如某个样本的真实类别模型输出层的概率 q 是我们要优化的近似分布。我们希望 q 尽量贴近 p其实就是希望用模型去“编码”真实标签时的交叉熵尽量小。多分类中常用 one-hot 作为真实分布 p模型输出经过 softmax 后变成预测分布 q。对单个样本来说交叉熵损失就退化成L - log q(y_true)这就是负对数似然正确类别对应的预测概率越大loss 越小。所有训练样本的平均值就是整个训练集的交叉熵损失。那为什么分类问题普遍用交叉熵而不是用均方误差MSE从优化角度看softmax 输出的概率会被“夹”在 0 到 1 之间分类目标的真实分布又是离散概率。如果用 MSE当模型预测错得很离谱或者非常自信时梯度都很容易变得很小收敛速度很慢而交叉熵的梯度有一个非常好用的性质——对网络最后一层的 logits 求导结果是 q - p也就是“预测概率减去真实概率”模型直接朝着真实分布的方向修正。这也是很多框架在分类任务里默认交叉熵的根本原因。2.3 训练时的两个实际细节很多人第一次自己写交叉熵时会直接拿 softmax 后的概率取 log然后发现 loss 偶尔变成 NaN。原因很简单当某个类别的预测概率恰好被压到 0 时log(0) 是负无穷。主流深度学习框架内部的交叉熵实现一般会直接计算 log_softmax把指数运算和取对数合并成一步从数值上避开溢出。后续你自己实现模型时也最好沿用这个思路不要先算 softmax 再手动 log。另一个容易被忽略的细节是样本的加权方式。类别不均衡时通常会给少数类加一个更大的权重。从交叉熵公式看给某类样本加权本质上就是改变了真实分布 p 里该类别的概率质量权重越大模型越认为这个类别“重要”。想清楚这一点你就能理解为什么轻微调权重就能明显改变模型倾向——它不是玄学只是重新定义了那个固定的“真实分布”。3. 相对熵两个分布之间的“差额消耗”3.1 定义与拆解交叉熵减去熵是什么现在我们把上面两个概念放在一起。既然香农熵 H(p) 是真实分布 p 的最小编码代价交叉熵 H(p,q) 是使用近似分布 q 编码真实分布 p 的实际代价那么两者的差值自然就是“因为用一个不完美的近似分布而多花的代价”。这个差值就是相对熵也叫 KL 散度D_KL(p‖q) H(p,q) - H(p) Σ p(x) log₂ [p(x)/q(x)]形式上也经常写成Σ p(x) log p(x) - Σ p(x) log q(x)前一项只依赖真实分布 p后一项就是我们熟悉的交叉熵。所以 KL 散度天然是交叉熵减香农熵三者是同一个故事的三段剧情。信息论的解读是用 q 去编码来自 p 的样本时比用 p 自身编码多出来的比特数。机器学习的解读更直接模型分布 q 在拟合真实数据分布 p 时一共丢失了多少信息。KL 散度越大说明 q 与 p 偏离得越远KL 散度为 0说明两个分布完全一致。3.2 非对称的距离方向到底意味着什么提到 KL 散度第一反应是“它像距离”但这个印象很危险因为它并不对称D_KL(p‖q) 和 D_KL(q‖p) 通常不一样。这也是它叫“散度”而不是“距离”的原因。拿两个具体分布看p[0.9, 0.1]q[0.5, 0.5]。D_KL(p‖q) 0.9×log₂(0.9/0.5) 0.1×log₂(0.1/0.5) ≈ 0.602D_KL(q‖p) 0.5×log₂(0.5/0.9) 0.5×log₂(0.5/0.1) ≈ 0.380方向不同数值不同含义也不同。把 p 放左边通常代表“p 是基准、是真实答案”你要惩罚的是“用 q 去凑 p 时的偏差”这种方式会让近似分布 q 尽量覆盖真实分布的所有高概率区域行为上偏稳健被称为 mode-covering把 p 放右边意思是“以 q 为基准去匹配 p”模型会倾向于缩在最稳妥的某一个峰附近宁可丢掉一部分概率质量也不冒风险常见于变分推断中的 ELBO 优化被称为 mode-seeking。GAN 训练早期出现的 mode collapse 现象从 KL 的方向性上也能找到一部分解释。所以看到 KL 损失时第一件事不是套公式而是问一句谁在左边谁在右边谁才是那个不能被牺牲的基准分布。写反的后果通常不是数值差一点而是模型训练出的行为完全不是你想要的那种。3.3 为什么 KL 散度一定大于等于 0这个性质很多文章直接给结论但我觉得值得花两行证明一次因为它能帮你建立对“信息损失”的直觉。用到一个很基础的不等式对任意正数 t都有 log t ≤ t - 1等号只在 t1 时成立。令 tq/p则-D_KL(p‖q) Σ p log(q/p) ≤ Σ p(q/p - 1) Σ q - Σ p 0移项就是 D_KL(p‖q) ≥ 0。等号成立的唯一条件是 q 和 p 处处相等。这个不等式看起来抽象但它的工程含义非常朴素任何拿近似分布去拟合真实分布的方案都不可能比真实分布本身更“便宜”。如果一个模型试图把 KL 损失优化到 0那它唯一的出路就是把预测分布做成与目标分布一致这也从理论上解释了为什么一个偷懒的模型如果均匀输出概率它的 KL 损失一定是个正数不会“白嫖”到最优。4. 一张表看清三兄弟关系、数值和快速记忆4.1 三个公式放在一起看如果你觉得前面的推导有点绕没关系把三个公式摆在同一张表里结构其实非常清楚名称公式一句话含义香农熵 H(p)-Σ p log p真实分布自身的不确定性也是无损编码的理论下限交叉熵 H(p,q)-Σ p log q用近似分布 q 去编码真实分布 p 的平均代价相对熵 D_KL(p‖q)Σ p log(p/q)用 q 代替 p 时多付的编码成本等于交叉熵减香农熵三个概念的关系就是相对熵 交叉熵 - 香农熵。pq 时交叉熵等于香农熵相对熵等于 0。换句话说熵是“下限”交叉熵是“实际花费”相对熵是“浪费掉的那部分”。这样记面试也好、看论文也罢都不会再绕晕。4.2 一组可复现的小数值实验光看公式还是容易飘我们来实际手算一组三分类的数值。设真实分布 p[0.6, 0.3, 0.1]两个待评估的模型分布 q1[0.4, 0.4, 0.2]、q2[0.9, 0.05, 0.05]。先算香农熵H(p) -(0.6 log₂0.6 0.3 log₂0.3 0.1 log₂0.1) ≈ 1.295 bit再算两个交叉熵H(p,q1) -(0.6 log₂0.4 0.3 log₂0.4 0.1 log₂0.2) ≈ 1.422 bitH(p,q2) -(0.6 log₂0.9 0.3 log₂0.05 0.1 log₂0.05) ≈ 1.820 bit于是D_KL(p‖q1) ≈ 1.422 - 1.295 0.127 bitD_KL(p‖q2) ≈ 1.820 - 1.295 0.525 bit注意看这个结果q2 在正确类别上给出了 0.9 的高置信度看起来“很有把握”但它的 KL 散度反而比 q1 高出好几倍。为什么因为它只是把最高概率做激进却把两个低概率类压得太低导致总的信息损失更大。这正是过度自信的惩罚信号也是标签平滑等正则手段起作用的空间所在——后面我会再展开讲。4.3 一句话讲给同事或面试官听如果要在电梯里用三句话解释这三兄弟我的说法是香农熵是数据自带的复杂度交叉熵是你用一个模型分布去编码真实分布时付出的成本相对熵是这两者之间的差价。把“编码成本”这个词挂在嘴边所有公式都变得可解释而不是一堆枯燥的求和符号。我在实际沟通中还发现一个很有用的技巧先用“代价/成本”类比再引入“基准分布”这个概念。先让对方理解熵是最少花费交叉熵是真实花费相对熵是浪费他基本不会再把 KL 的方向搞错。5. 工程实战损失函数、蒸馏和我的排错记录5.1 为什么训练代码里只写交叉熵不显式写 KL很多刚从理论转入实战的人会疑惑既然相对熵看起来更“完整”为什么分类训练代码里到处都是交叉熵很少见到 KL 散度原因其实藏在前面那个关系式里D_KL(p‖q) H(p,q) - H(p)。分类任务中真实分布 p 是固定的标签分布H(p) 对模型参数来说是一个常数。最小化交叉熵和最小化 KL 散度在数学上是完全等价的优化目标——常数项不影响梯度。所以主流框架的交叉熵损失内部实际上等价于在最小化“预测分布相对真实标签分布的 KL 散度”。只有两类场景会显式用到 KL一类是模型蒸馏你需要同时比较学生分布和老师分布另一类是生成模型里的变分推断优化目标里会出现 ELBO 和 KL 项的组合。那种情况下才需要小心翼翼地把 KL 项亲手写出来。5.2 数值稳定性为什么应该用 log_softmax 而不是 softmax log这是我最常看到新手踩的坑而且一旦踩了表现往往是 loss 出现 NaN 或者 inf排查起来相当恼火。问题出在两步操作上先算 softmax再对结果取 log。softmax 的表达式里含有 exp(z)当某个 logit 数值比较大时exp(z) 可能直接溢出就算没有溢出softmax 输出的概率也可能因为浮点精度变成 0下一步 log(0) 就炸了。正确的做法是直接计算 log_softmax公式上等价于log_softmax(z_i) z_i - log(Σ exp(z_k)) z_i - logsumexp(z)logsumexp 在实现时会提取最大值再做指数运算能大幅度降低溢出风险。主流框架的交叉熵损失内部基本都做了这一步但如果你要自己实现类似蒸馏这样的复合损失就要注意凡是需要 log 概率的地方尽量用 log_softmax 输出而不是先 softmax 再 log。5.3 标签平滑与蒸馏KL 显式发力的两个场景标签平滑是分类任务里性价比很高的正则化手段原理其实也是在动“真实分布” p。传统 one-hot 把目标分布变成确定性概率 1这会导致模型为了拉低 loss 而拼命把对应类别的 logit 推向无穷大容易过拟合。标签平滑的做法是保留一部分概率给其他类别相当于把目标分布 p 改成平滑后的软分布。从 KL 的角度来看它直接改变的是你要逼近的那个基准分布而不是改模型结构所以实现起来非常简单却往往能带来稳定的泛化提升。蒸馏则是更明显地把 KL 搬到了台面上。简单说学生模型的损失通常由两部分组成一部分对着真实硬标签计算交叉熵另一部分对着老师模型的软概率计算 KL 散度。温度 T 的作用是把概率分布拉软让老师能“告诉”学生类别之间的相似关系比如一张图为什么更像猫而不是更像狗。我自己的经验是温度这个东西要按任务调偏大时软标签的信息量被稀释偏小则退化成接近硬标签并且计算 KL 时老师分布要放在左边学生分布放在右边方向写反训练结果很可能直接南辕北辙。5.4 三个我踩过的坑以及现在的处理习惯做过的蒸馏和生成模型项目多了有些坑几乎每次都能遇到这里整理成一个表方便你直接对照排查常见错误现象处理方式KL 方向写反模型收敛慢、行为异常甚至学生反而不如直接训练写 KL 前先明确谁是基准分布基准放左边预测放右边log 里出现 0loss 出现 NaN 或 inf使用 log_softmax必要时给概率加极小 epsilon但优先改实现方式平均维度不一致训练指标和验证指标对不上类别不均衡时尤其明显明确实现的 loss 是按样本平均还是按类别平均训练和验证保持一致除了这些我在每个新任务里还会多做一个动作训练前打印标签分布的香农熵、模型初始预测分布的交叉熵以及验证集上预测分布相对标签分布的 KL。这三组数字能直观告诉我任务本身有多难、模型起步偏了多少、最终拟合到了什么程度。很多时候模型效果不好不是网络结构的问题而是这几个基本量在设计阶段就已经埋下了隐患。我自己现在写任何跟概率分布相关的损失都会先画一条线真实分布谁来定、近似分布是谁、额外成本算给谁。这条线一旦画清楚香农熵、交叉熵和相对熵就不再是三个需要死记硬背的公式而是同一套编码语言里的三个标尺。
阅读完成 · 觉得有帮助?
咨询建站