Backpropagation

后向传播(backpropagation)算法在深度学习中扮演了非常重要的角色,它能够从损失函数开始链式地对网络层中的权重进行梯度计算与更新。可以先参考维基百科: Backpropagation,文章很好地还原了深度神经网络(DNN)每一层网络从后往前的链式梯度计算关系。然后再结合下面这几张图理解每一层网络中每个单元的梯度具体计算与推导过程,假设前向计算公式$wx + b$.


举个例子

上图是一个两层的全连接神经网络,其中是输入,是输出,输出在softmax之后计算交叉熵损失。下面给出详细计算隐藏层对应的权重梯度的过程,其中是batch size,是真实标签,是softmax激活后的输出

又因为

所以

除此之外,还有一个问题,交叉熵损失函数计算值只与标签1对应的输出相关,那么标签0对应输出的相关权重就无需计算梯度并进行更新了么?跑了一下上图两层全连接神经网络的demo程序,发现无论输出对应标签是0还是1,其相关权重梯度都不为0。通过对权重梯度的核算发现,对于标签0对应输出,虽然在交叉熵损失函数计算结果中没有得到体现,但是在后向传播梯度计算过程中,会采用$-(1-y_0)\log{(1-O_0)}$作为0标签相关权重梯度计算的损失,这点类似于逻辑回归损失函数,其中$y_0$为0,$O_0$为对应softmax输出。demo程序如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import torch
import torch.nn as nn

class mymodel(nn.Module):
def __init__(self):
super(mymodel, self).__init__()
self.l1 = nn.Linear(10, 20, bias=False)
nn.init.constant_(self.l1.weight, 0.1)
self.l2 = nn.Linear(20, 2, bias=False)
nn.init.constant_(self.l2.weight, 0.1)

def forward(self, x):
t = self.l1(x)
print(t)
y_out = self.l2(t)
print(y_out)
return y_out

x = torch.rand(1, 10)
y = torch.tensor([0])
m = mymodel()
optimizer = torch.optim.SGD(m.parameters(), lr=0.1)
loss_func = nn.CrossEntropyLoss()
print(x)
print(m.l1.weight)
print(m.l2.weight)
y_out = m(x)
loss = loss_func(y_out, y)
print(loss)
optimizer.zero_grad()
loss.backward()
optimizer.step()
for name, param in m.named_parameters():
print(name, param.grad, param)

Tips:

  1. 在实际应用场景中,可能由于硬件设备落后而无法支持大batch的数据训练,但是现在的深度学习框架一般都会支持梯度累积,可以用多个mini batch模拟big batch的训练,假设累积步数为k,那么每个mini batch在后向梯度计算之前要将loss除以k再做后向梯度计算。
---------------- The End ----------------

作者: brooksjay
联系邮箱: jaypark@smail.nju.edu.cn
本文地址: https://brooksj.com/2019/11/13/Backpropagation/
本文基于 知识共享署名-相同方式共享 4.0 国际许可协议发布
转载请注明出处, 谢谢!