Pytorch(5)-梯度反向传播

1. 函数对自变量x求梯度–ax^2+b

torch提供了简便的求梯度方法:带入变量值x,求解目标函数f(x),将目标函数的值反向传播f.backward(),就能计算对应变量的导数。

  1. 以二次函数为例: f ( x ) = a x 2 + b f(x)=ax^2+b f(x)=ax2+b
  2. 其中a,b是已知的参数,对x 求导: f ′ ( x ) = 2 a x f'(x)=2ax f(x)=2ax
  3. a = 1 , x = 1 时 a=1,x=1时 a=1,x=1 f ′ ( x ) = 2 f'(x)=2 f(x)=2
import torch
from torch.autograd import Variable
def f(x):
    return x**2
x1 = Variable(torch.Tensor([1]),requires_grad=True)  # 使用variable封装待求导的参数
print("初始化", x1.grad)

y=f(x1)
y.backward()
print("一次传播", x1.grad)

x1.grad.data.zero_()
print("梯度置零", x1.grad)

y=f(x1)
y.backward()
y=f(x1)              # #如果x1.grad 每次计算前梯度不归零,那么当前的梯度会与原来的梯度累加
y.backward()         # #每次loss 反向传播前需要将梯度设置为0
print("二次传播", x1.grad)

输出

初始化 None
一次传播 tensor([2.])
梯度置零 tensor([0.])
二次传播 tensor([4.])

2. 网络对参数w求梯度- loss(w,x)

神经网络训练:通过调整神经网络参数使网络输出尽可能接近于想要的对象。具体通过损失函数的梯度下降实现的,即损失函数对网络参数求梯度,利用该梯度调整网络参数。

模型的损失函数实际是输入x 和模型参数w 的函数:loss(w,x)。依据part1demo流程,随机初始化w后,输入观测数据x(图像 或者 其他需要分类或者回归的数据)。loss(w, x).backward()就可以求出对应的网络参数w的梯度。再利用各种优化算法(SGD,admn)更新各个网络参数w即可。
pytorch 网络训练的流程

  1. 搭建好网络:net()
  2. 数据x输入网络:y=net(x,w)
  3. 网络输出y与标签label计算标量损失函数:loss(y,label,w)
  4. 损失函数loss()反向传播:loss.backward()->通过自动求导Adagrad实现
  5. 网络参数更新:net.parameters().step()->通过优化器实现

注意事项:在每次损失函数反向传播计算梯度时,原本的参数梯度必须设置为0。否则,各个参数的梯度会累加之前的梯度。网络参数梯度置0:

optimizer=torch.optim.SGD(net.parameters(),lr=0.001,momentum=0.1)
optimizer.zero_grad()
loss.backward()

3. 自动求梯度的底层支持–torch.autograd

torch.autograd 是一套自动求导引擎,能够根据输入前向传播过程自动构建计算图,并执行反向传播. 计算图是深度学习框架的核心。

[本节为陈云<<深度学习框架-pytorch 入门与实践>>一书第3.2章的内容autograd 读书笔记.]

pytorch 中 autograd模块 实现了 计算图 相关功能. autograd 的核心数据结构Variable。
pytorch 中 autograd模块 底层采用 计算图 实现梯度计算。

3.1 Variable

Variable中封装了以下三个部分内容

  1. data : 保存数据tensor
  2. grad : 保存data对应的梯度,形状与data一致,也是一个Variable
  3. grad_fn : 指向一个Function,记录Variable操作历史,如果一个变量是由用户创建的,则他为叶子节点,对应的grad_fn为None.

3.1.1 Variable构造函数

Variable的构造函数–需要传入tensor,还有两个可选参数:

  1. requires_grad : bool 是否需要对该variable进行求导
  2. volatile : bool 为True , 那么基于该variable 构建的图都不会求导,转为推理阶段设计.

volatile优先级别比requires_grad高。在计算路径上同时存在requires_grad=True 和 volatile=True 的变量,那么其后的通过计算得出的变量的requires_grad 为False.

3.1.2 Variable链式求导–backward()

Variable支持大部分的tensor操作,但不支持部分inplace函数。如果想要计算各个Variable的梯度,只需要调用根节点variable的backward方法,autograd 会自动沿计算图反向传播,计算每一个叶子节点的梯度.

Variable.backward(grad_variables=None,retain_graph=None,create_graph=None)

  1. grad_variables : 相当与链式法则 ∂ z ∂ x = ∂ z ∂ y ∂ y ∂ x \frac{\partial z}{\partial x}=\frac{\partial z}{\partial y}\frac{\partial y}{\partial x} xz=yzxy中的 ∂ z ∂ y \frac{\partial z}{\partial y} yz 。y.backward(grad_y) 等价于 z.backward()

  2. retain_graph : 反向传播中会缓存一些中间结果,反向传播结束后缓存被清空。可以通过指定这个参数不清空缓存,用于多次反向传播。

  3. create_graph : 反向传播过程中再次构建计算图,通过backward of backward实现高阶求导.

3.1.3 Variable反向传播函数–grad_fn

variable变量的grad_fn 属性可以用来查看反向传播函数, next_functions保存grad_fn的输入,是一个tuple。每个变量在图中的位置可以通过grad_fn 属性在图中的位置推测得到。每一个前向传播函数都有一个与之对应的反向传播函数,用来计算各个输入变量的梯度;这些函数以Backward结尾。

x=Variable(torch.ones(1)) 
b=Variable(torch.rand(1,requires_grad=True)) 
w=Variable(torch.rand(1),requires_grad=True)
y=w*x
z=y+b
z.grad_fn       						# 查看z的反向传播函数                                                     
Out[16]: <AddBackward0 at 0x7f84b0a0e3c8>

z.grad_fn.next_functions               # z 反向传播函数的输入,一个为y.grad_fn(为MulBackward0),另一个是b.grad_fn(为none)                                          
Out[17]: ((<MulBackward0 at 0x7f84903a9588>, 0), (None, 0))

y.grad_fn                                                              
Out[27]: <MulBackward0 at 0x7f84903a9588>
y.grad_fn.next_functions                                                           
Out[19]: ((<AccumulateGrad at 0x7f84b0a0ee10>, 0), (None, 0))

z.backward(retain_graph=True)           # 保留中间结果                                    
w.grad                                                                 
Out[23]: tensor([1.])

z.backward(retain_graph=True)           # 保留中间结果, 才能二次传播                                  
w.grad                                                                 
Out[26]:tensor([2.])                    # 这个为2 不是保留中间结果的原因,而是两次梯度传播

3.2 计算图

pytorch 中 autograd模块 底层采用 计算图 实现梯度计算。计算图是一种特殊的有向无环图 , 用于记录算子变量 之间的关系。

在pytorch 中autograd模块会随着用户操作,记录生成当前 variable 的所有操作。以此建立一个有向无环图(每个变量都有一个有向无环图?还以一个计算过程有一个?应该是一个计算过程有一个图)。用户每进行一次操作,计算图就会变化。对应的变化会通3.1.3 的grad_fn 记录

3.2.1 动态创建计算图

pytorch 的计算图在每次前向传播的时候都是从头开始构建的 , 所以能够利用python的控制语句(for,if) 根据需求 创建动态图.

少一个例子

3.2.2 非叶子节点的梯度–autograd.grad 与 hook

在反向传播的过程中,非叶子节点的导数在计算完成之后即被清空,若想查看这些变量的梯度,有两种方法:autograd.grad 与 hook。

torch.autograd.grad()

In [28]: x=Variable(torch.ones(3),requires_grad=True)                           
In [30]: w=Variable(torch.ones(3),requires_grad=True)                           
In [31]: y=x*w                                                                  
In [32]: z=y.sum()                                                              
In [33]: z.backward()                                                           
In [34]: (x.grad,w.grad,y.grad)                                                 
Out[34]: (tensor([1., 1., 1.]), tensor([1., 1., 1.]), None)   # y.grad的梯度计算完被释放掉了
# 使用torch.autograd.grad()计算中间节点的梯度
In [35]: torch.autograd.grad(z,y)      # 求Z对Y的梯度                                      
Out[35]: (tensor([1., 1., 1.]),)

hook 呢?🤔️

3.3 复杂自建函数,自动求导支持

自己写一个复杂函数,不支持自动求导。这种情况下,定义此复杂函数为Function(), 继承自,Torch.autograd.Function。实现前向与反向传播 的代码。使用Function.apply(variable)调用实现Function。
详见:p95

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值