Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

交叉熵损失函数

通过前面两个章节,编码和嵌入,我们解决了让网络模型看懂文字的难题,构建了一个简单的文本生成模型。

文本生成模型本质上是一个多元分类模型,因此我们在网络模型的输出层后使用了 Softmax 激活函数,成功将模型的数值输出转化为概率分布。但在实际训练中,我们并没有看到明显效果。问题的根源不在于 Softmax 激活函数本身,而在于我们衡量误差的方式:均方误差损失函数。

均方误差损失函数是根据预测值和标签的平方误差来衡量模型预测的差距,并据此给出不同的梯度来调整模型参数。损失值越大、梯度也越大。

但是 Softmax 激活函数将实际预测值转换成了相对的概率分布。

当预测值远离标签的时候,Softmax 激活函数的结果非常接近于 0 或者 1,曲线变得非常平缓,因此给出的概率分布变化很小,出现梯度饱和现象。这相当于对于较大的预测误差,Softmax 激活函数有明显的弱化作用。这种情况下,继续使用均方误差损失函数,会造成梯度减弱,训练停滞不前。


为了解决这种“预测越错,修正越小”的逻辑矛盾,我们为分类任务引入一套新的评价标准:交叉熵损失函数(Cross-Entropy Loss)。它能与 Softmax 激活函数完美配合,在模型预测偏差较大时提供巨大的修正动力,来弥补 Softmax 激活函数的弱化效果,而在预测接近正确时则变得温和。

张量

数据集

抽象类

字符编码数据集

模型

抽象层

线性层

顺序层

嵌入层

平均池化层

激活函数

线性整流函数(ReLU)

归一化指数函数(Softmax)

损失函数

我们首先提炼出一个损失函数的抽象类,方便扩展新的损失函数。

抽象类

抽象类只定义了一个抽象函数:

  • loss(损失函数):计算损失值。

均方误差损失函数

然后将均方误差损失函数修改为从抽象类继承。

交叉熵损失函数

交叉熵的核心思想是:

  • 预测概率越接近标签,损失值越小;

  • 预测概率越偏离真实标签,损失值急剧增长(对数级增长)。

前向传播:交叉熵损失的定义是:

L=−∑i=1nyilog⁡(ai)L = -\sum_{i=1}^{n} y_i \log(a_i)

这里:

  • yiy_i:标签的概率(独热编码);

  • aia_i:激活值的类别概率(Softmax 激活函数的输出值)。

这里的 log 运算当概率接近 1 时变化缓慢,而在概率接近 0 时变化剧烈。可以说是 Softmax 激活函数的指数运算的“天敌”。当两者结合进行求导时,原本在 Softmax 饱和区消失的梯度,会被 log 函数重新放大。

梯度计算:交叉熵损失函数的实现,通常将 Softmax 激活函数的计算包含在内。这样做的目的,就是可以大大简化梯度计算,公式为:

δ=ai−yi\delta = a_i - y_i

这里:

  • yiy_i:标签的概率(独热编码);

  • aia_i:预测值经 Softmax 计算后的类别概率。

代码中的损失值是一批样本的平均值,所以梯度还要再除以批大小。

父节点列表(parents):(无 Softmax 激活函数的)预测值。

参数列表(parameters):无。

💡 提示:交叉熵损失函数已经包括 Softmax 激活函数的计算在内。因此,使用的时候就不再需要输出层激活函数。

二元交叉熵损失函数

在处理二元分类问题时,我们通常只关心一个预测概率。这时我们就不能再使用 Softmax 这样的向量激活函数了,而是使用 Sigmoid 激活函数将输出映射到 (0,1)(0, 1) 的区间里。

前向传播:二元交叉熵损失的定义是:

L=−(ylog⁡(a)+(1−y)log⁡(1−a))L = -(y \log(a) + (1 - y) \log(1 - a))

这里:

  • yy:标签的概率(0 或者 1);

  • aa:激活值的概率(Sigmoid 激活函数的输出值)。

这个公式衡量预测概率与标签之间的差异,当 aa 越接近 yy 时损失值越小。

梯度计算:对激活值 aa 求导:

δ=a−ya(1−a)\delta = \frac{a - y}{a (1 - a)}

这里:

  • yy:标签的概率(0 或者 1);

  • aa:激活值的概率(Sigmoid 激活函数的输出值)。

二元交叉熵损失函数总是和 Sigmoid 激活函数配合使用。这个公式和 Sigmoid 激活函数的梯度公式正好相抵消,从而解决 Sigmoid 激活函数在遇到大的预测值时梯度饱和的问题。

父节点列表(parents):激活值。

参数列表(parameters):无。

💡 提示:二元交叉熵损失函数不包括 Sigmoid 激活函数的计算在内。因此,使用的时候仍然需要输出层激活函数。

优化器(随机梯度下降)

训练器

超参数

学习率

交叉熵损失函数对大的梯度变化比较敏感,容易造成震荡、发散或者不收敛的情况。我们可以选择使用小一些的学习率。

批大小

轮数

上下文尺寸

嵌入尺寸

建模

让我们尝试使用一下交叉熵损失函数,看看训练效果会如何。

这里要记得去掉输出层的 Softmax 激活函数,因为我们的交叉熵损失函数已经包含 Softmax 激活函数的计算了。

训练

评估

prediction:	(6970, 65)
loss:	Tensor(3.3545101560265147)

我们并不能直接和上一章对比损失值,因为使用的是不同的损失函数。

推理

ROMEO:taots uaa e AVrsQsso nunaelf Urs uamoruUs nomAs  oesc sh ogiesso snnbto et vnlLEuSpd 
hwaaagetff yA middp rldeAofUiyeta 'd,
tso   ote fablo 
y:   draengss
peyeyoAhys na
d:rar
R:-  hJdasteY u OOW p l  t&rh uEs s
gottrt,PRcdh n afrol h bd Iuua  du o wdpal' u

换了新的交叉熵损失函数后,从模型生成的文本并不能看出太大的区别。这是因为我们还有一个重要的问题没有解决:就是我们使用词袋或者平均池化来构建特征,忽略了文本的词序。

课后练习

  1. 交叉熵损失函数已经包括输出层 Softmax 激活函数,试一试如果在模型的输出层后仍然加上了 Softmax 激活函数,损失值和最后的推理结果会有怎样的表现?