目录标题
对于输出层,一般会直接将结果代入损失函数的计算。对于我们之前介绍的分类问题,这里选择交叉熵误差(Cross Entropy Error)作为损失函数,就可以得到一个 Softmax-with-Loss 层,它包含了 Softmax 和 Cross Entropy Loss 两部分。

一、交叉熵推导(推导只以x1这一条数据为例)
回顾交叉熵blog 深度学习4:神经网络学习1:损失函数,交叉熵误差
t i t_i ti和 y i y_i yi的意义
不回忆看下面截图应该就明白差不多了

这里只是算一条数据,所以没有除以n
倒数第一步: L初始导数肯定是1,
倒数第二步: ∑ y log e x S \sum y \log \frac{e^x}{S} ∑ylogSex 乘以-1, 最终还是后面导数乘以当前自变量导数,算出当前自变量对整体L的导数
得到 -1 * 1 = -1,
倒数第三步:加法,当前局部导数就是1,没变化
-1
倒数第四步: 由于自变量 log e x 1 S \log \frac{e^{x_1}}{S} logSex1 乘以y1 , 所以 log e x 1 S \log \frac{e^{x_1}}{S} logSex1的局部导数是
-1 * y1 = -y1 前向传播需要记录这个y1的真实值
倒数第五步: 经过一个log,仔细看下面
e x 1 S \frac{e^{x_1}}{S} Sex1看作u, log e x 1 S \log \frac{e^{x_1}}{S} logSex1看作y. 前向传播需要记录这个u的真实值 (这里在前向传播的过程就要记住得到的u是多少,y是多少,并保存。因为这些值在反向传播的时候,且是乘法节点时候会影响当前局部导数。)
∂ L ∂ u = ∂ L ∂ y ⋅ ∂ y ∂ u = − y ⋅ 1 u \frac{\partial L}{\partial u} = \frac{\partial L}{\partial y} \cdot \frac{\partial y}{\partial u} = -y \cdot \frac{1}{u} ∂u∂L=∂y∂L⋅∂u∂y=−y⋅u1
将u带入,最后算出 e x 1 S \frac{e^{x_1}}{S} Sex1对L的局部导数 − y 1 S e x 1 -\frac{y_1 S}{e^{x_1}} −ex1y1S

交叉熵的反向传播推导暂时结束
至于 e x 1 S \frac{e^{x_1}}{S} Sex1 怎么前向传播算出来,和 − y 1 S e x 1 -\frac{y_1 S}{e^{x_1}} −ex1y1S 再下面softmax反向传播作用 -------------->看softmax推导
二、softmax推导(推导只以x1这一条数据为例)
前置知识softmax blog:深度学习2:激活函数(ReLU和softmax)及最终选择
y k = e x k ∑ i = 1 n e x i y_k = \frac{e^{x_k}}{\sum_{i=1}^{n} e^{x_i}} yk=∑i=1nexiexk
前向传播分两步:
第一步: e x k e^{x_k} exk 得到 e x 1 e^{x_1} ex1
第二步: 1 ∑ i = 1 n e x i \frac{1}{\sum_{i=1}^{n} e^{x_i}} ∑i=1nexi1 这第二步也经过EXP,然后累加后(这里累加用S表示) 取-1次方, 得到 1 S \frac{1}{S} S1

最终 e x 1 S \frac{e^{x_1}}{S} Sex1 就是 1 S \frac{1}{S} S1 乘以 e x 1 e^{x_1} ex1
反向传播第一步(对应前向传播第一步):
推导倒数第一步:链式法则(乘法节点的反向传播),最后L关于 e x 1 e^{x_1} ex1的局部导数为 − y 1 S e x 1 -\frac{y_1 S}{e^{x_1}} −ex1y1S * 1 S \frac{1}{S} S1 = − y 1 e x 1 -\frac{y_1}{e^{x_1}} −ex1y1
推导倒数第二步:x1 经过EXP 得到 e x 1 e^{x_1} ex1
L对于x1的局部导数就是 e x 1 e^{x_1} ex1 * − y 1 e x 1 -\frac{y_1}{e^{x_1}} −ex1y1 = − y 1 -y_1 −y1

反向传播第二步:(对应前向传播第二步)
推导倒数第一步:链式法则(乘法节点的反向传播),最后L关于 1 S \frac{1}{S} S1的局部导数为 − y 1 S e x 1 -\frac{y_1 S}{e^{x_1}} −ex1y1S * e x 1 e^{x_1} ex1 = − y 1 S -y_1 S −y1S
推导倒数第二步: − y 1 S -y_1 S −y1S * 1 S 2 \frac{1}{S^2} S21 得到 − y 1 S \frac{-y_1}{S} S−y1
因为是累加,( y 1 , y 2 , y 3 y_1, y_2, y_3 y1,y2,y3为独热编码,和为1) 最终得到 1 S \frac{1}{S} S1
推导倒数第三步::x1 经过EXP 得到 e x 1 e^{x_1} ex1
L对于x1的局部导数就是 x i x_i xi * 1 S \frac{1}{S} S1 = x i S \frac{x_i}{S} Sxi

最终反向传播两个步骤汇总
x i S \frac{x_i}{S} Sxi + − y 1 -y_1 −y1 就是L对x1的局部导数
简化
− y 1 -y_1 −y1 是目标值(真实分类标签)
x i S \frac{x_i}{S} Sxi 就是 y k = e x k ∑ i = 1 n e x i y_k = \frac{e^{x_k}}{\sum_{i=1}^{n} e^{x_i}} yk=∑i=1nexiexk 是预测值

根据 Softmax + 交叉熵损失(Cross-Entropy Loss)的数学推导,最终传给输入 (x) 的梯度(也就是反向传播的输出 (dx))有一个极其优美的公式:
∂ L ∂ x = y − t \frac{\partial L}{\partial x} = y - t ∂x∂L=y−t
- ( y (y (y):模型预测的概率分布,比如
[0.1, 0.7, 0.2]。 - ( t (t (t):真实标签(通常是独热编码),比如真实类别是第 2 类,那么 ( t (t (t) 就是
[0, 1, 0]。
所以,如果严格按照数学公式,代码只需要写一行:
python
dx = y - t
三、Softmax-with-Loss代码实现
涉及到softmax引用参考blog: 深度学习2:激活函数(ReLU,softmax和恒等函数)及激活函数的最终选择
cross_entropy引用参考blog: 深度学习4:神经网络学习1:损失函数,交叉熵误差 (一定看这篇blog才能理解下面代码)
python
class SoftmaxWithLoss:
# 初始化
def __init__(self):
self.loss = None
self.y = None
self.t = None
# 前向传播
def forward(self, X, t):
self.t = t
self.y = softmax(X)
self.loss = cross_entropy(self.y, self.t)
return self.loss
# 反向传播
def backward(self, dy=1):
n = self.t.shape[0]
# 如果是独热编码的标签,就直接代入公式计算
if self.t.size == self.y.size:
dx = self.y - self.t
# 如果是顺序编码的标签,就需要找到分类号对应的值,然后相减
else:
dx = self.y.copy()
dx[np.arange(n), self.t] -= 1
return dx / n