一、反向传播
Sigmoid写一半发现BackPropagation忘地差不多了,罪过罪过
1.关于梯度
- 导数告诉了我们整个函数表达式对于某一个变量的敏感度
- 梯度就是偏导组成的一个向量
2.结合反向传播理解pytorch如何自动求导
- Loss对神经网络输出求偏导: ∂ L o s s ∂ f ( y ) = f ( y ) − y ∗ \frac{ \partial{Loss} }{\partial{f(y)}}=f(y)-y^* ∂f(y)∂Loss=f(y)−y∗(这里令偏导等于0得到 f ( y ) = y ∗ f(y)=y^* f(y)=y∗,也就得到了损失函数的极值点,也就解释了为啥有篇博客说神经网络输出的误差就是损失函数对输出的偏导。这里不禁有个想法,如果更新网络的时候令学习率等于1,那么更新之后所有参数岂不是就完全拟合了当前的这个输入样本了吗(在batch_size=1的前提下)。此刻我也理解了神经元的损失乘参数权重依次向后面的神经元传递。)
- Loss对 w 3 , 1 w_{3,1} w3,1求偏导: ∂ L o s s ∂ w 3 , 1 = ∂ l o s s ∂ f ( y ) × ∂ f ( y ) ∂ y × ∂ y ∂ w 3 , 1 \frac{\partial{Loss}}{\partial{w_{3,1}}}=\frac{\partial{loss}}{\partial{f(y)}} \times \frac{\partial{f(y)}}{\partial{y}}\times \frac{\partial{y}}{\partial{w_{3,1}}} ∂w3,1∂Loss=∂f(y)∂loss×∂y∂f(y)×∂w3,1∂y,第一个式子的值上边求出来过了,第二个式子就是一个sigmoid函数的求导是 f ( y ) ⋅ ( 1 − f ( y ) ) ∈ ( 0 , 1 4 ] f(y)\cdot (1-f(y)) \in (0,\frac{1}{4}] f(y)⋅(1−f(y))∈(0,41],第三个式子是 f ( b 1 ) f(b_1) f(b1)。这里要解释pytorch的自动求导是如何在O(n)时间(基本同前向传播一样的时间)来完成反向传播的梯度计算的,是因为对于计算图中的每一个非叶子结点,都会有一个grad_fn属性记录它是怎么计算出来的,对于f(y)它就是对y激活而来的,因此从loss开始根据loss的grad_fn回溯到与它相关计算的节点f(y),并根据loss的grad_fn计算loss对f(y)的偏导。接着从f(y)回溯到上一个节点y,根据f(y)的grad_fn计算f(y)对y的偏导,并乘上loss对f(y)的偏导得到,loss对y的偏导。同理计算y对w31的偏导,再乘上loss对y的偏导(即loss对y的梯度)得到loss对w31的偏导。不过有一点好像pytorch在backward()之后只会保存计算图中叶子节点的梯度,这点还没搞太明白。另外还有梯度累加机制,我也略懂了。所以有一个代码块之前有点不懂的,现在也明白为啥zero_grad能在backward之前了,因为它只是清零了grad属性而已。
for i,(images,target) in enumerate(train_loader):
# 1. input output
images = images.cuda(non_blocking=True)
target = torch.from_numpy(np.array(target)).float().cuda(non_blocking=True)
outputs = model(images)
loss = criterion(outputs,target)
# 2. backward
optimizer.zero_grad() # reset gradient
loss.backward()
optimizer.step()