Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

1.4 损失函数:把任务翻译成优化目标

1.3 节解决了「网络有没有能力表达复杂函数」,但一个能表达任意函数的网络,仍然不知道该向哪个方向学。损失函数承担的就是这件事:它把「任务做得好不好」翻译成一个标量,而梯度下降只认标量。

这一步看起来简单,却是实际项目里错得最多的环节之一。三个典型症状:分类任务用了 MSE,训练初期损失几乎不降;网络里写了 Softmax 又用了 CrossEntropyLoss,梯度被额外系数改写导致收敛变慢;多任务训练时几个损失的权重拍脑袋定,最后某个任务的主导了训练、其余任务被牺牲。它们都不是「网络结构」问题,而是目标定义问题。

这一节要做三件事:给出一个统一的视角(损失 = 负对数似然),把最常见的两种损失(交叉熵与 MSE)放进这个视角里;推导 softmax + 交叉熵那个异常简洁的梯度,说明它为什么是分类任务的标准搭配;最后处理真实数据里的三种糟糕情况——类别不均衡、标签噪声、多任务加权。

1.4.1 输出层与损失的配对

定义 1.4.1 · 损失函数(loss function)

损失函数是把网络输出 y^=f(x;θ)\hat{\boldsymbol y}=f(\boldsymbol x;\theta) 与目标 y\boldsymbol y 映射为非负标量的函数 L(y^,y)\mathcal L(\hat{\boldsymbol y},\boldsymbol y);训练目标为

θ⋆=arg⁡min⁡θ E(x,y)∼D[L(f(x;θ),y)]≈arg⁡min⁡θ 1N∑i=1NL(f(xi;θ),yi).\theta^{\star}=\arg\min_{\theta}\ \mathbb E_{(\boldsymbol x,\boldsymbol y)\sim\mathcal D}\bigl[\mathcal L\bigl(f(\boldsymbol x;\theta),\boldsymbol y\bigr)\bigr]\approx\arg\min_{\theta}\ \frac1N\sum_{i=1}^{N}\mathcal L\bigl(f(\boldsymbol x_i;\theta),\boldsymbol y_i\bigr).

要做对这件事,必须先回答两个问题:输出层的值域是什么(网络吐出什么形式的数)、任务的概率模型是什么(这些数在解释什么)。两者决定了损失的形式,配对表如下。

任务输出层内部量损失对应的概率模型
多分类(互斥 CC 类)Softmaxlogits z∈RC\boldsymbol z\in\mathbb R^C交叉熵类别分布 Categorical\mathrm{Categorical}
二分类Sigmoid(或单 logit + BCE)单个 logit二元交叉熵(BCE)伯努利分布
回归(实值)恒等(无激活)y^∈R\hat y\in\mathbb RMSE(或 L1 / Huber)高斯(噪声为加性正态)
带计数 / 正值的回归Softplus / exp正的尺度参数Poisson / Gamma 负对数似然相应分布
多标签分类每个标签独立 SigmoidCC 个 logit各项 BCE 之和CC 个独立伯努利
配对表的用法:从「输出什么数」倒推「概率模型」

一类做法是死记「分类用交叉熵、回归用 MSE」,容易在遇到新任务时失效。更稳的做法是问三个问题:① 网络的输出在参数化哪个分布的什么参数? ② 该分布下观测到真值的对数似然是多少?③ 负对数似然就是损失。多分类:输出参数化类别分布的概率 → 负对数似然即交叉熵。回归:输出参数化高斯的均值、方差固定 → 负对数似然即 MSE(差一个常数与系数)。多标签:输出参数化 CC 个独立伯努利 → 各项 BCE 之和。表里的每一行都可以这样现场推出来,不必背。

1.4.2 交叉熵与均方误差

定义 1.4.2 · 交叉熵与均方误差

交叉熵(cross-entropy):设 CC 类,真值以 one-hot 或软标签 y\boldsymbol y 给出,网络经 softmax 输出 y^\hat{\boldsymbol y},则

LCE(y^,y)=−∑c=1Cyclog⁡y^c.\mathcal L_{\mathrm{CE}}(\hat{\boldsymbol y},\boldsymbol y)=-\sum_{c=1}^{C}y_c\log\hat y_c .

one-hot 时它退化为 LCE=−log⁡y^ytrue\mathcal L_{\mathrm{CE}}=-\log\hat y_{y_{\text{true}}},即只有正确类别的概率被惩罚。

均方误差(mean squared error, MSE):设预测 y^i\hat y_i、真值 yiy_i,则

LMSE(y^,y)=1N∑i=1N(y^i−yi)2.\mathcal L_{\mathrm{MSE}}(\hat{\boldsymbol y},\boldsymbol y)=\frac1N\sum_{i=1}^{N}\bigl(\hat y_i-y_i\bigr)^2 .

平均绝对误差(MAE / L1):LMAE=1N∑i∣y^i−yi∣\mathcal L_{\mathrm{MAE}}=\frac1N\sum_i\lvert\hat y_i-y_i\rvert。Huber 损失在 ∣y^−y∣|\hat y-y| 小于阈值 δ\delta 时用平方、超过时用线性,兼得 MSE 的平滑与 MAE 的抗离群性。

交叉熵在惩罚什么:错得越自信,罚得越狠

one-hot 下只需盯一个数 −log⁡p-\log p。p=0.9p=0.9 时罚 0.1050.105,p=0.5p=0.5 时罚 0.6930.693,p=0.1p=0.1 时罚 2.3032.303,p→0p\to0 时罚到 +∞+\infty。对数把「差一点点」和「完全颠倒」区分得很开:模型把 99% 的置信度给了错误类别,会收到一个很大的梯度,逼它立刻改口。相比之下 MSE 对概率误差的惩罚是平方级的,在两端太平缓:pp 从 0.10.1 降到 0.010.01,MSE 只多罚了不到 0.010.01。

为什么回归不用交叉熵、分类不用 MSE

两个方向各说一次:

回归不能用交叉熵:交叉熵的定义要求第二个参数是概率分布(非负且和为 11)。连续量没有天然的类别概率,硬套离散化会引入人为的 bin 边界。而 MSE 来自一个明确的概率假设——观测噪声为加性高斯、且方差与输入无关。观测 y=y^+ϵy=\hat y+\epsilon、ϵ∼N(0,σ2)\epsilon\sim\mathcal N(0,\sigma^2) 时,负对数似然为

−log⁡p(y∣y^)=(y−y^)22σ2+12log⁡(2πσ2),-\log p(y\mid\hat y)=\frac{(y-\hat y)^2}{2\sigma^2}+\frac12\log(2\pi\sigma^2),

关于 y^\hat y 最小化它,等价于最小化 (y−y^)2(y-\hat y)^2,即 MSE。因此「回归用 MSE」不是约定俗成,是高斯似然假设的直接结果;若噪声是重尾的(有离群点),高斯假设就不成立,此时该用 MAE(对应拉普拉斯噪声)或 Huber。

分类不用 MSE:一是概率模型不匹配(类别是离散分布,不是高斯);二是梯度质量差。即使把 softmax 输出接上 MSE,梯度里会带一个 y^(1−y^)\hat y(1-\hat y) 因子,在模型最该被纠正的两个极端(y^→0/1\hat y\to0/1)梯度反而最小,训练被拖慢;交叉熵配 softmax 的梯度恰好是 y^−y\hat{\boldsymbol y}-\boldsymbol y(下一小节的推导),在最需要修正的地方最大。

关于「分类的 MSE」有一个需要说清的事实

在分类误差这个指标上,softmax + MSE 有时也能收敛到一个精度不错但是置信度校准很差的解:因为它不强迫概率逼近 0/1,只强迫预测接近标签。这类模型在需要校准概率的场景(排序、阈值决策、集成加权)会明显吃亏,而且训练早期更慢。工程判据是:只要任务是「从有限类别里选一个」,就用交叉熵;只有在做「回归到一个 [0,1][0,1] 区间内的软分数」且明确不需要概率语义时,才考虑 MSE。

1.4.3 统一视角:损失就是负对数似然

定理 1.4.1 · 最大似然与损失函数

设数据由条件分布 p(y∣x;θ)p(\boldsymbol y\mid\boldsymbol x;\theta) 生成,NN 个样本独立同分布。则最小化平均负对数似然(NLL)等价于最大化似然:

θMLE=arg⁡max⁡θ∏i=1Np(yi∣xi;θ)=arg⁡min⁡θ −1N∑i=1Nlog⁡p(yi∣xi;θ).\theta_{\mathrm{MLE}}=\arg\max_{\theta}\prod_{i=1}^{N}p(\boldsymbol y_i\mid\boldsymbol x_i;\theta)=\arg\min_{\theta}\ -\frac1N\sum_{i=1}^{N}\log p\bigl(\boldsymbol y_i\mid\boldsymbol x_i;\theta\bigr).

在类别分布假设下,上式右侧即交叉熵;在高斯假设下即 MSE(差常数项)。从这个视角看,所有的损失函数选择都是「假设数据服从哪种分布」的选择。

推导(从乘积到求和)

(i) 似然函数是各样本概率的乘积 ∏ip(yi∣xi;θ)\prod_i p(\boldsymbol y_i\mid\boldsymbol x_i;\theta)。直接对乘积求导不便(乘积的导数是多因子求和),且大量小数相乘容易下溢。

(ii) 取对数:log⁡∏ipi=∑ilog⁡pi\log\prod_i p_i=\sum_i\log p_i。对数函数严格单调递增,因此最大化乘积与最大化对数和给出同一个 θ\theta。这解决了数值问题(把乘积变成和)与求导问题(求和可逐项求导)。

(iii) 取平均并加负号:−1N∑ilog⁡pi-\frac1N\sum_i\log p_i。取平均使目标函数的量级与样本数无关(便于跨数据集比较、便于设定学习率);取负号把最大化变成最小化,以适配梯度下降。■\blacksquare

∎
为什么「取对数」这一步能一劳永逸

因为它把乘法结构变成了加法结构。独立样本的概率要相乘,而「相乘」在优化里是最难处理的结构之一:一个样本的概率接近零,整条似然就接近零,梯度被它独占。取对数后每个样本的贡献是独立一项,可以逐样本求导、逐样本加权——这也是为什么工程上可以「对批内样本的损失取平均」,以及为什么可以针对单个样本重新加权(focal loss、样本重加权都靠这条性质)。

1.4.4 softmax + 交叉熵的梯度:一个值得记住的结果

定理 1.4.2 · softmax 复合交叉熵的梯度

设 logits z∈RC\boldsymbol z\in\mathbb R^{C},p^c=ezc∑c′ezc′\hat p_c=\dfrac{e^{z_c}}{\sum_{c'}e^{z_{c'}}},L=−∑cyclog⁡p^c\mathcal L=-\sum_c y_c\log \hat p_c(yy 可为 one-hot 或软标签,只要 ∑cyc=1\sum_c y_c=1)。则

∂L∂zk=p^k−yk,即∇zL=p^−y.\frac{\partial\mathcal L}{\partial z_k}=\hat p_k-y_k,\qquad\text{即}\quad \nabla_{\boldsymbol z}\mathcal L=\hat{\boldsymbol p}-\boldsymbol y .

梯度不依赖 softmax 的显式导数,只依赖「预测减真值」这个残差——这就是这一对组合成为分类标准做法的根本原因。

推导(逐项展开,两步)

第一步:softmax 的雅可比。 记 S=∑cezcS=\sum_{c}e^{z_c},则 p^c=ezc/S\hat p_c=e^{z_c}/S。对 zkz_k 求导要分两种情况:当 c=kc=k 时 ∂p^k/∂zk=ezkS−ezkezkS2=p^k(1−p^k)\partial \hat p_k/\partial z_k=\frac{e^{z_k}S-e^{z_k}e^{z_k}}{S^2}=\hat p_k(1-\hat p_k);当 c≠kc\neq k 时 ∂p^c/∂zk=0−ezcezkS2=−p^cp^k\partial \hat p_c/\partial z_k=\frac{0-e^{z_c}e^{z_k}}{S^2}=-\hat p_c\hat p_k。合并写作 ∂p^c∂zk=p^c(δck−p^k)\dfrac{\partial \hat p_c}{\partial z_k}=\hat p_c(\delta_{ck}-\hat p_k)。

第二步:代入链式法则。 由 ∂L∂zk=∑c∂L∂p^c⋅∂p^c∂zk\dfrac{\partial\mathcal L}{\partial z_k}=\sum_c\dfrac{\partial\mathcal L}{\partial \hat p_c}\cdot\dfrac{\partial \hat p_c}{\partial z_k},而 ∂L∂p^c=−ycp^c\dfrac{\partial\mathcal L}{\partial \hat p_c}=-\dfrac{y_c}{\hat p_c},故

∂L∂zk=−∑cycp^c⋅p^c(δck−p^k)=−∑cyc(δck−p^k)=−yk+p^k∑cyc=p^k−yk,\frac{\partial\mathcal L}{\partial z_k}=-\sum_c\frac{y_c}{\hat p_c}\cdot\hat p_c(\delta_{ck}-\hat p_k)=-\sum_c y_c(\delta_{ck}-\hat p_k)=-y_k+\hat p_k\sum_c y_c=\hat p_k-y_k ,

末步用到 ∑cyc=1\sum_c y_c=1(真值是分布)。■\blacksquare

∎
这个结果为什么「干净得反常」

一般复合函数求导会留下两个因子的乘积(外层导数 × 内层导数),而这里内层导数 p^c(δck−p^k)\hat p_c(\delta_{ck}-\hat p_k) 与外层导数 −yc/p^c-y_c/\hat p_c 里都含 p^\hat p,恰好除法把乘法约掉了。这不是巧合:softmax 是「指数族分布的规范形式」,交叉熵是它对数似然的自然配对,二者的导数在数学上必然互相抵消。工程含义非常实用:无论 logits 多大多小,梯度都落在 [−1,1][-1,1] 附近,不存在饱和放大或消失——分类任务的训练比回归任务稳定,一半功劳在这里。

例 1.4.1 · 手算一次 softmax + 交叉熵与它的梯度

三分类,某样本真值为类别 22(即 y=(0,1,0)\boldsymbol y=(0,1,0)),某轮 logits 为 z=(2.0, 1.0, 0.1)\boldsymbol z=(2.0,\,1.0,\,0.1)。

(1) 求 softmax 输出 p^\hat{\boldsymbol p};(2) 求交叉熵损失;(3) 求梯度 ∇zL\nabla_{\boldsymbol z}\mathcal L;(4) 若把 z\boldsymbol z 换成 (2.0, 0.1, 1.0)(2.0,\,0.1,\,1.0)(把第 3 类的分数抬到第 2 类之上),重算损失与梯度,并解释数值变化的含义。

解

(1) e2.0=7.3891e^{2.0}=7.3891,e1.0=2.7183e^{1.0}=2.7183,e0.1=1.1052e^{0.1}=1.1052,和 S=11.2126S=11.2126。故

p^=(0.6590, 0.2424, 0.0986).\hat{\boldsymbol p}=(0.6590,\ 0.2424,\ 0.0986).

(2) 真值为第 2 类,L=−log⁡0.2424=1.4170\mathcal L=-\log 0.2424=1.4170。(同一值在 (1) 中可直接读出:−log⁡(0.2424)-\log(0.2424)。)

(3) 梯度 p^−y=(0.6590, 0.2424−1, 0.0986)=(0.6590, −0.7576, 0.0986)\hat{\boldsymbol p}-\boldsymbol y=(0.6590,\ 0.2424-1,\ 0.0986)=(0.6590,\ -0.7576,\ 0.0986)。读法:第 1、3 类的 logit 都会被调低(正梯度,梯度下降沿负方向走),第 2 类会被调高(负梯度),且调整幅度与「错得有多离谱」成正比——第 1 类的 p^\hat p 最大,所以它被压得最狠。

(4) 若 z=(2.0,0.1,1.0)\boldsymbol z=(2.0,0.1,1.0):S=7.3891+1.1052+2.7183=11.2126S=7.3891+1.1052+2.7183=11.2126(不变),p^=(0.6590,0.0986,0.2424)\hat{\boldsymbol p}=(0.6590,0.0986,0.2424),L=−log⁡0.0986=2.3170\mathcal L=-\log 0.0986=2.3170,梯度为 (0.6590,−0.9014,0.2424)(0.6590,-0.9014,0.2424)。损失从 1.4171.417 涨到 2.3172.317、第 2 类的梯度项从 −0.76-0.76 增到 −0.90-0.90,说明把错误类别的分数抬高会同时加剧惩罚与修正力度。这正是交叉熵「错得越自信、罚得越狠」的数值形态。■\blacksquare

∎

1.4.5 交叉熵与 KL 散度

定理 1.4.3 · 散度分解

对任意两个分布 p,qp,q,KL 散度满足

KL(p ∥ q)=∑cpclog⁡pcqc=(−∑cpclog⁡qc)⏟交叉熵 H(p,q) − (−∑cpclog⁡pc)⏟p 的熵 H(p).\mathrm{KL}(p\,\|\,q)=\sum_c p_c\log\frac{p_c}{q_c}=\underbrace{\Bigl(-\sum_c p_c\log q_c\Bigr)}_{\text{交叉熵 }H(p,q)}\ -\ \underbrace{\Bigl(-\sum_c p_c\log p_c\Bigr)}_{p\text{ 的熵 }H(p)} .

因此 H(p,q)=KL(p∥q)+H(p)H(p,q)=\mathrm{KL}(p\|q)+H(p)。当 pp 是给定的目标分布(如 one-hot 标签或软标签)时,H(p)H(p) 与模型参数无关,所以

arg⁡min⁡θH(p,qθ)=arg⁡min⁡θKL(p ∥ qθ)=arg⁡min⁡θ 负对数似然.\arg\min_\theta H(p,q_\theta)=\arg\min_\theta \mathrm{KL}(p\,\|\,q_\theta)=\arg\min_\theta\ \text{负对数似然}.

因此「最小化交叉熵」到底在做什么

它在做把模型分布拉向目标分布这件事,且只做这件事——因为目标分布自己的熵 H(p)H(p) 是个常数,不影响优化方向。三个实用推论:① 两个模型在不同数据集上的交叉熵不可直接比较(H(p)H(p) 不同);② 在同一数据集上比较交叉熵与 KL 是等价的,所以「最小化 KL」和「最小化交叉熵」在训练中不必区分;③ 标签平滑把 pp 从 one-hot 改成软分布,同时改变了 H(p)H(p) 与最优解——这就是它既是正则手段、又会降低校准质量的原因(1.4.7)。

1.4.6 类别不均衡与焦点损失

定义 1.4.3 · 焦点损失(focal loss)

二分类设定下,记 p^\hat p 为模型给真值类别的预测概率(ptp_t),焦点损失为

Lfocal=−αt(1−pt)γlog⁡pt,\mathcal L_{\mathrm{focal}}=-\alpha_t\bigl(1-p_t\bigr)^{\gamma}\log p_t ,

其中 γ≥0\gamma\ge0 为聚焦参数(γ=0\gamma=0 时退化为交叉熵),αt\alpha_t 为类别的加权系数。多分类形式为对每个样本用其真值类别的 ptp_t 计算同一表达式再平均。

焦点损失改的是「这一项的权重」,不是「惩罚的形状」

交叉熵里每个样本的贡献是 −log⁡pt-\log p_t,一个「已经预测得很对」的样本(pt=0.9p_t=0.9)仍贡献 0.1050.105;当成千上万这样的易分样本堆在一起,它们的总贡献会淹没少数难分样本。因子 (1−pt)γ(1-p_t)^{\gamma} 做的事是:ptp_t 接近 11 时把这一项压到接近零(γ=2\gamma=2 时 0.010.01 的权重),ptp_t 小的时候几乎不压((1−0.1)2=0.81(1-0.1)^2=0.81)。于是训练信号被自动重新分配到「难样本」上。这正是它被提出来处理检测任务中前景 / 背景极度不均衡的原因。

机制手段适用假设风险
重采样对少数类过采样 / 多数类欠采样少数类样本量太少过采样易过拟合;欠采样丢信息
类别权重交叉熵各项乘 αt\alpha_t(逆频率)类间代价不对称破坏概率校准,需重新校准
焦点损失按「是否为易分样本」动态加权存在大量易分样本淹没信号γ\gamma 过大时对噪声样本过度关注
重加权 / 重标注学习每样本权重、清理标签标签有噪声或难易差异极大引入额外的偏差来源
焦点损失的两个常见误用
  1. 把它当通用提点手段。类别均衡的数据上,焦点损失通常没有优势,甚至因为压低易分样本权重而降低有效样本量。它的定位是不均衡问题的专用工具。
  2. γ\gamma 与 α\alpha 一起乱调。两者作用不同:α\alpha 调整类间相对权重(与逆频率重加权同类),γ\gamma 调整样本难易的相对权重。一起调会难以归因;建议先固定 α\alpha(或设为均衡值)只调 γ\gamma,观察「难样本的召回率」这一指标。

1.4.7 标签平滑与多任务损失加权

定义 1.4.4 · 标签平滑(label smoothing)

设 CC 类、平滑系数 ϵ∈[0,1)\epsilon\in[0,1),把 one-hot 标签替换为

ycLS=(1−ϵ) 1[c=ctrue]+ϵC,c=1,…,C,y_c^{\text{LS}}=(1-\epsilon)\,\mathbf 1[c=c_{\text{true}}]+\frac{\epsilon}{C},\qquad c=1,\dots,C,

然后用同一交叉熵公式 −∑cycLSlog⁡p^c-\sum_c y_c^{\text{LS}}\log\hat p_c 训练。

标签平滑在防什么

one-hot 标签要求模型对正确类别输出概率 1,也就要求正确类别的 logit 与其余类别差距趋于无穷——这只能靠把权重范数不断推大来实现。在有限数据上,这会让模型「过度自信」,并放大标签噪声的影响。标签平滑把目标从「确定分布」改成「带 ϵ\epsilon 平滑的分布」,给 logit 差距设了一个有限的最优值。

副作用同样清楚:它降低了概率校准质量。公开研究给出的解释是——平滑让不同类别的 logit 更紧地聚在一起,抹掉了「模型知道自己不确定」这一维信息,因此在知识蒸馏和置信度校准任务上,用平滑标签训练的老师可能反而不如不加平滑的老师。(该结论来自公开论文,具体实验设置需按原文核实。)

多任务损失加权:四种方案与选择依据

多任务训练时总损失常写成 L=∑kwkLk\mathcal L=\sum_k w_k\mathcal L_k。wkw_k 的取法按「需要多少额外假设」排序:

方案 形式 依据 代价 / 风险
手动权重 wkw_k 人工调 无需额外假设,可控 需要反复试;任务数一多就难调
同尺度归一化 先各自除以初始步的损失值,再加权求和 让各任务起步量级一致 只是让起点公平,不保证终点平衡
梯度量级归一化 缩放 wkw_k 使各任务的梯度范数同阶 反向传播的实际尺度是梯度而非损失 需每步统计梯度范数,有额外开销
不确定性加权 为回归任务引入可学习的噪声方差 σk2\sigma_k^2,最小化 ∑k12σk2Lk+12log⁡σk2\sum_k\frac{1}{2\sigma_k^2}\mathcal L_k+\frac12\log\sigma_k^2 把每个任务的噪声视为同方差高斯、对 σk\sigma_k 取极大似然 引入可学习参数;对分类任务的形式不同

最后一行值得记住的直觉:σk\sigma_k 是可学习的,1σk2\frac{1}{\sigma_k^2} 自动充当权重。噪声大(学不好)的任务权重自然被调小,同时 log⁡σk2\log\sigma_k^2 这一项防止 σk\sigma_k 无限增大(否则退化为「忽略所有任务」)。具体公式与符号以原文为准,使用前需核实(见延伸阅读)。

1.4.8 代码:三种损失的等价关系与不均衡对照

import torch, torch.nn as nn, torch.nn.functional as F

torch.manual_seed(0)
logits = torch.tensor([[2.0, 1.0, 0.1]])       # 例 1.4.1 的 logits
target = torch.tensor([1])                     # 真值为第 2 类(0 基索引)

# ① 交叉熵:框架把 log_softmax 与 NLL 融成一个数值稳定的算子
ce = F.cross_entropy(logits, target)
p  = logits.softmax(-1)                        # 例 1.4.1:应为 (0.6590, 0.2424, 0.0986)
manual = -p[0, target].log()                   # 手算:-log(0.2424) = 1.4170
print("CE(框架) =", ce.item(), " 手算 =", manual.item())

# ② 梯度核对:定理 1.4.2 说 ∇_z = p − y
logits2 = logits.clone().requires_grad_(True)
F.cross_entropy(logits2, target).backward()
print("grad =", logits2.grad)                  # 应为 (0.6590, -0.7576, 0.0986)
print("p - y =", (p - F.one_hot(target, 3)).detach())

# ③ 交叉熵 = KL + 常数:在目标分布固定时,两者对参数的作用完全一致
logp = F.log_softmax(logits, dim=-1)
onehot = F.one_hot(target, 3).float()
kl = F.kl_div(logp, onehot, reduction="none").sum(-1)     # KL(one-hot || p)
print("KL =", kl.item(), "  CE − KL = H(one-hot) =", (ce - kl).item())  # 后者应为 0

# ④ 标签平滑:把 one-hot 软化成 (1−ε) 加 ε/C,最优置信度因此被压低
eps = 0.1
soft = (1 - eps) * onehot + eps / 3
ls = -(soft * logp).sum(-1).mean()
print("label-smoothed CE =", ls.item())
print("达到该损失时正确类的目标概率 =", (1 - eps) + eps / 3)  # 0.9333

# ⑤ 极端不均衡:交叉熵与 focal loss 在“易分样本”上的权重差异
p_t = torch.tensor([0.9, 0.6, 0.2])            # 真值类别的预测概率
ce_each = -p_t.log()
focal_each = -(1 - p_t) ** 2 * p_t.log()       # γ = 2
print("CE   每样本损失:", ce_each.tolist())
print("Focal 每样本损失:", focal_each.tolist())
print("易分样本占比 (相对 CE):", (focal_each[0] / ce_each[0]).item())  # 0.01 量级

1.4.9 损失曲线的读法:从「损失降不降」到「损失在说什么」

损失值本身只说明目标的绝对值,曲线的形状才携带诊断信息。下表把常见形状与病因对应起来(与 1.7.1的训练/验证曲线诊断配合使用)。

曲线形状含义首选检查
平稳单调下降正常—
前几百步几乎不动,之后才开始下降warmup 不足 / 初始化尺度过小 / 首层梯度被饱和吃掉打印各层梯度范数(1.5.3);检查初始化(1.2.5)
下降中突然跳到很大值学习率过大 / 某个异常批梯度的批内范数分布(个别样本的损失可能是其余样本的百倍)
长期停在某个正值不动任务与损失不匹配(如分类用了 MSE)或标签有系统性错误用一小撮手工核对过的样本做一次过拟合测试(见下)
不断下降但验证指标不涨概率在变得更自信,尚未跨越决策边界;或评估指标与损失口径不一致同时打印准确率 / F1 等任务指标
出现 NaN数值溢出(混合精度下尤甚)、学习率过大、除零看 NaN 首次出现的位置:损失、梯度还是参数
三个必做的损失侧检查
  1. 初始损失核对:CC 类分类任务在随机初始化时,正确的初始交叉熵应接近 log⁡C\log C(如 10 类约 2.3032.303,1000 类约 6.9086.908)。若初始损失明显偏离,说明输出层的初始化、softmax 的使用方式或标签编码有问题。这是最便宜的一致性检查,几十秒就能排除一整类 bug。
  2. 过拟合一小撮数据:取 8∼328\sim32 个样本,关掉所有正则,训练几百步。损失必须能降到接近零。降不下去说明实现或容量层面有硬问题(模型太小、损失配错、梯度没传对),与「泛化」无关。
  3. 随机标签对照:把标签随机打乱后重训。若训练损失仍能降到接近零,说明模型容量足够记住随机标签(这本身正常);但若验证指标明显高于随机水平,说明评估流程里有信息泄漏(1.7.6)。

1.4.10 陷阱与勘误

本节七个高频错误
  1. Softmax + CrossEntropyLoss 同时使用。框架的 CrossEntropyLoss 内部已包含 log_softmax。再手动加一层 softmax 会让梯度被额外系数改写(等价于对已归一化的概率再做一次归一化),表现为收敛变慢、精度下降、概率严重过自信。判据:训练时 loss.backward() 前检查你喂给损失的是 logits 还是概率。
  2. 把「损失收敛」当成「任务做好了」。损失只度量训练目标,不度量任务指标(准确率、F1、BLEU、mAP)。分类精度可能早已到顶而损失还在下降(概率在变得更自信);也可能损失在降而指标不涨(如类别不均衡下被多数类主导)。
  3. 回归任务默认用 MSE,却忘了检查离群点。MSE 对离群点的惩罚是平方级的,一个错标 10 倍的样本会主导整批梯度。稳妥做法是先画出目标值分布与残差分布,再决定 MSE / MAE / Huber。
  4. 多任务损失不加任何归一化。两个任务损失量级差 100 倍时,等价于只训一个任务。至少要把各任务损失的初始量级对齐(方法 1.4.1)。
  5. 把 focal loss 用于均衡数据(1.4.6的勘误)。
  6. 用测试集挑损失函数。损失函数属于模型设计的一部分,任何基于测试集的比较选择都会让报告的结果失真;应使用验证集,并保留测试集做最终一次性评估(见 1.7.6)。
  7. 把 BCE 的「混淆命名」搞混。BCELoss 期望概率输入(需要先过 Sigmoid),BCEWithLogitsLoss 期望logits(内部含 Sigmoid)。混用是常见 bug 来源:用 BCELoss 却喂 logits,会让梯度在饱和区几乎消失。
即时自测 · 1.4

Q1 一个多分类任务,网络末尾写了 Softmax,损失用了 CrossEntropyLoss,训练能跑但收敛慢。写出正确的改法,并说明为什么错误版本会变慢。

Q2 从最大似然的角度说明:为什么「回归用 MSE」不是约定,而是高斯噪声假设的推论?如果数据里有明显离群点,你会换成什么损失,对应什么噪声假设?

Q3 写出交叉熵与 KL 的关系式,并说明为什么「在固定数据集上,最小化交叉熵与最小化 KL 等价」,但「跨数据集比较交叉熵」却不合理。

Q4 焦点损失中的因子 (1−pt)γ(1-p_t)^{\gamma} 在 pt→1p_t\to1 与 pt→0p_t\to0 时分别做什么?为什么这个设计能缓解类别不均衡?

参考要点

Q1 正确改法:去掉网络末尾的 Softmax,直接输出 logits 给 CrossEntropyLoss(或保留 softmax 而改用 NLLLoss 并先取 log,但这不推荐,因为数值稳定性差)。变慢的原因:网络输出的 p^\hat p 再经过一次 softmax 后得到的 p~\tilde p 与 logits 的关系不再是「线性 + 指数归一化」,梯度里多出一层雅可比(p~(δ−p~)\tilde p(\delta-\tilde p)),相当于对梯度做了额外压缩;同时在数学上它已经不是原始的交叉熵目标(p~\tilde p 的熵被额外扭曲),最优解也不再是「p^\hat p 逼近真实类别分布」。

Q2 高斯似然的负对数含 (y−y^)22σ2\frac{(y-\hat y)^2}{2\sigma^2},最小化它关于 y^\hat y 等价于最小化平方误差,故 MSE = 高斯噪声下的极大似然。离群点存在时高斯假设被违反(重尾),应换成 MAE(对应拉普拉斯噪声,即绝对偏差)、或 Huber(小误差平方、大误差线性,两个假设的分段折中)。判据是残差分布的尾部形态。

Q3 H(p,q)=KL(p∥q)+H(p)H(p,q)=\mathrm{KL}(p\|q)+H(p)。目标分布 pp 由数据集给定,与参数 θ\theta 无关,因此在同一数据集上 H(p)H(p) 是常数,arg⁡min⁡H(p,qθ)=arg⁡min⁡KL(p∥qθ)\arg\min H(p,q_\theta)=\arg\min\mathrm{KL}(p\|q_\theta)。跨数据集比较时 H(p)H(p) 不同(如两个数据集的类别分布不同、标签平滑系数不同),因此交叉熵的绝对值不可比;要跨数据集比较模型,应该报告 KL 或归一化后的指标(如 perplexity 需要先说明词表与数据)。

Q4 pt→1p_t\to1(易分样本)时因子趋 00,该项损失被压到近乎消失;pt→0p_t\to0(难分样本)时因子趋 11,几乎保留原始交叉熵。因为不均衡数据中大多数样本是易分样本,交叉熵的总梯度会被它们占据;压低它们的权重后,少数难分样本与少数类样本的梯度占比显著提高,从而缓解「被易分样本淹没」的问题。γ\gamma 越大越激进,过大时会过度关注噪声样本。

延伸阅读
  • Goodfellow, Bengio & Courville, Deep Learning(2016,MIT Press)第 5 章与第 6 章——从最大似然推出常见损失的权威叙述。
  • Szegedy et al., Rethinking the Inception Architecture for Computer Vision(2016,CVPR)——标签平滑在该文中被明确引入并解释(作为正则化手段的动机与效果,细节需按原文核实)。
  • Müller, Kornblith & Hinton, When Does Label Smoothing Help?(2019,arXiv:1906.02629)——标签平滑损害置信度校准与知识蒸馏的实证研究。
  • Lin, Goyal, Girshick, He & Dollár, Focal Loss for Dense Object Detection(2017,ICCV;arXiv:1708.02002)——焦点损失的提出与类别不均衡分析。
  • Kendall, Gal & Cipolla, Multi-Task Learning Using Uncertainty to Weigh Losses for Scene Geometry and Semantics(2018,CVPR)——不确定性加权的多任务损失(公式细节需按原文核实)。

本节小结:损失函数是「任务」到「可优化标量」的翻译器,它的正确形式由输出层的值域与任务假设的概率模型共同决定;统一视角是「最小化负对数似然」。softmax + 交叉熵之所以成为分类默认,是因为它的梯度恰好是 p^−y\hat{\boldsymbol p}-\boldsymbol y——不饱和、不放大、只依赖残差。交叉熵与 KL 只差一个常数熵项,因此训练中两者等价,但跨数据集比较需要谨慎。真实数据的三种麻烦各有对应工具:类别不均衡用重加权或焦点损失,标签噪声用标签平滑(以校准质量换取稳定性),多任务冲突用归一化或不确定性加权。下一节转向这一问题链的最后一环、也是整套范式里唯一需要真正推导的部分:这些梯度是怎么被算出来的。