这里主要参考了深肚学习 - 超详细的softmax的反向传播梯度计算推导并扩展为n分类的情况。
对于深度学习多分类问题,神经网络最后一层常用softmax处理多项分布,这里我们设定n分类任务,推导其正向传播和反向传播中最后一层的相关计算公式。
正向传播
对于n分类任务,这里我们设定输出层的相关信息如下:
- 输入:
- 输出层神经元的输入是Z=[z1,z2,⋅,zn]
- 分类真实标签是Y=[y1,y2,⋅,yn],其中Y是
one-shot分类标签,只有真实类别yk=1,其余均为0。
- 输出:
- Y^=softmax(Z)=[y^1,y^2,…,y^n],其中y^i代表属于第i类的条件概率P(y^i∣X)。
则对于n分类问题,其Softmax表达式为:
y^i=∑j=1nezjezi
注意:这里为了后续计算方便,我们令S=∑j=1nezj。
对应的Cost function:
J=−i=1∑nyilny^i
这里代价函数函数选择了使用交叉熵,关于交叉熵的解释,请参考一文读懂信息熵、交叉熵和相对熵(KJ散度)
反向传播
这里我们假设第k个神经元对应的是正确的标签,也就是yk=1,且注意Y是one-shot分类标签。
第一步:求取J对y^i的偏导数
∂y^i∂J=−y^iyi
第二步:求取J对zi的偏导数
∂zi∂J=j=1∑n∂y^j∂J×∂zi∂y^j=j=1∑n(−y^jyj×∂zi∂y^j)
这里之所以要使用∑j=1n,是因为在上述y^i的Softmax表达式中,能清晰看到其分母的求和中一定含有ezi这一项。
又因为只有yk=1,其余的yi=0(i=k),所以又可以写为如下表达式:
∂zi∂J=−y^kyk×∂zi∂y^k=−y^k1×∂zi∂y^k
第三步:求取∂zi∂y^k,这里需要分类讨论
∂zi∂y^k=∂zk∂y^k=S2ezk×S−(ezk)2=Sezk−(Sezk)2=y^k×(1−y^k)
∂zi∂y^k=S20−ezk×ezi=−y^k×y^i
第四步:直接代入整个∂z∂J表示
∂Z∂J=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡∂z1∂J⋮∂zk∂J⋮∂zn∂J⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡∑i=1n∂y^i∂J⋅∂z1∂y^i⋮∑i=1n∂y^i∂J⋅∂zk∂y^i⋮∑i=1n∂y^i∂J⋅∂zn∂y^i⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡∂y^k∂J⋅∂z1∂y^k⋮∂y^k∂J⋅∂zk∂y^k⋮∂y^k∂J⋅∂zn∂y^k⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤=⎣⎢⎢⎢⎢⎢⎢⎢⎡−y^k1⋅(−y^ky^1)⋮−y^k1⋅y^k(1−y^k)⋮−y^k1⋅(−y^ky^n)⎦⎥⎥⎥⎥⎥⎥⎥⎤=⎣⎢⎢⎢⎢⎢⎢⎢⎡y^1⋮y^k−1⋮y^n⎦⎥⎥⎥⎥⎥⎥⎥⎤=Y^−Y
简单验证代码(PyTorch)
1 2 3 4 5 6 7 8 9 10 11
| import torch
x = torch.randn(4, requires_grad=True) t = torch.tensor([0, 0, 1, 0.], dtype=torch.float)
y = torch.softmax(x, dim=0) loss = -(t * torch.log(y)).sum()
loss.backward() print("y:", y.detach().numpy()) print("grad z = y - t:", x.grad.numpy())
|
运行后可看到 x.grad 与公式y−t一致,即为推导结果的直接验证。