Pytorch 之 backward

首先看这个自动求导的参数:

  • grad_variables:形状与variable一致,对于y.backward(),grad_variables相当于链式法则dz/dx=dz/dy × dy/dx 中的 dz/dy。grad_variables也可以是tensor或序列。
  • retain_graph:反向传播需要缓存一些中间结果,反向传播之后,这些缓存就被清空,可通过指定这个参数不清空缓存,用来多次反向传播。
  • create_graph:对反向传播过程再次构建计算图,可通过backward of backward实现求高阶导数。

注意variables 和 grad_variables 都可以是 sequence。对于scalar(标量,一维向量)来说可以不用填写grad_variables参数,若填写的话就相当于系数。若variables非标量则必须填写grad_variables参数。下面结合参考示例来解释一下这个参数怎么用。

先说一下自己总结的一个通式,适用于所有形式:

      对于此式,x的梯度x.grad为 

1.scalar标量

注意参数requires_grad=True让其成为一个叶子节点,具有求导功能。

手动求导结果:

代码实现:

import torch as t
from torch.autograd import Variable as v

a = v(t.FloatTensor([2, 3]), requires_grad=True)    # 注意这里为一维,标量
b = a + 3
c = b * b * 3
out = c.mean()
out.backward(retain_graph=True) # 这里可以不带参数,默认值为‘1’,由于下面我们还要求导,故加上retain_graph=True选项

结果:

a.grad
Out[184]:
Variable containing:
  15    18[torch.FloatTensor of size 1x2]

结果与手动计算一样

backward带参数呢?此时的参数为系数

将梯度置零:

a.grad.data.zero_()

再次求导验证输入参数仅作为系数:

n.backward(torch.Tensor([[2,3]]), retain_graph=True)

结果:(2和3应该分别作为系数相乘)

a.grad
Out[196]:
Variable containing:
  30  54[torch.FloatTensor of size 1x2]

验证了我们的想法。

2.张量

import torch
from torch.autograd import Variable as V

m = V(torch.FloatTensor([[2, 3]]), requires_grad=True)   # 注意这里有两层括号,非标量
n = V(torch.zeros(1, 2))
n[0, 0] = m[0, 0] ** 2
n[0, 1] = m[0, 1] ** 3

求导 :(此时的[[1, 1]]为系数,仅仅作为简单乘法的系数),注意 retain_graph=True,下面我们还要求导,故置为True。

n.backward(torch.Tensor([[1,1]]), retain_graph=True)

结果:

m.grad
Out[184]:
Variable containing:
  4  27
[torch.FloatTensor of size 1x2]

将梯度置零:

m.grad.data.zero_()

再次求导验证输入参数仅作为系数:

n.backward(torch.Tensor([[2,3]]))

结果:4,27 × 2,3 =8,81  验证了系数这一说法

 m.grad
Out[196]:
Variable containing:
  8  81
[torch.FloatTensor of size 1x2]

注意backward参数,由于是非标量,不填写参数将会报错。

3.  另一种重要情形

之前我们求导都相当于是loss对于x的求导,没有接触中间过程。然而对于下面的链式法则我们知道如果知道中间的导数结果,也可以直接计算对于输入的导数。而grad_variables参数在某种意义上就是中间结果。即上面都是z.backward()之类,那么考虑y.backward(b) 或 y.backward(x)是什么意思呢?

下面给出一个例子解释清楚:

import torch
from torch.autograd import Variable
x = Variable(torch.randn(3), requires_grad=True)
y = Variable(torch.randn(3), requires_grad=True)
z = Variable(torch.randn(3), requires_grad=True)
print(x)
print(y)
print(z)

t = x + y
l = t.dot(z)

结果:

# x
Variable containing:
 0.9168
 1.3483
 0.4293
[torch.FloatTensor of size 3]

# y
Variable containing:
 0.4982
 0.7672
 1.5884
[torch.FloatTensor of size 3]

# z
Variable containing:
 0.1352
-0.4037
-0.2425
[torch.FloatTensor of size 3]

在调用 backward 之前,可以先手动求一下导数,应该是: 

当我们打印x.grad和y.grad时都是 x.grad = y.grad = z。 当我们打印z.grad 时为 z.grad = t = x + y。这里都没有问题。重要的来了:

先置零:

x.grad.data.zero_()
y.grad.data.zero_()
z.grad.data.zero_()

看看下面这个情况:

t.backward(z)
print(x.grad)
print(y.grad)
print(z.grad)

此时的结果为:

x和y的导数仍然与上面一样为z。而z的导数为0。解释:t.backward(z): 若求x.grad: z * dt/dx   即为dl/dt × dt/dx=z
               若求y.grad: z * dt/dy   即为dl/dt × dt/dy=z
               若求z.grad: z * dt/dz   即为dl/dt × dt/dz = z×0 = 0

再验证一下我们的想法:

清零后看看下面这种情况:

t.backward(x)
print(x.grad)
print(y.grad)
print(z.grad)
x和y的导数仍然相等为x。而z的导数为0。解释:t.backward(x): 若求x.grad: x * dt/dx   即为x × 1 = x
               若求y.grad: x * dt/dy   即为x × 1 = x
               若求z.grad: x * dt/dz   即为x × 0 = 0验证成功。

另:k.backward(p)接受的参数p必须要和k的大小一样。这一点也可以从通式看出来。

参考:

PyTorch 的 backward 为什么有一个 grad_variables 参数?

PyTorch 中文网

PyTorch 中文网

PyTorch中的backward [转]

Calculus on Computational Graphs: Backpropagation

原文地址:https://www.cnblogs.com/king-lps/p/8336494.html

时间: 2024-11-09 04:40:22

Pytorch 之 backward的相关文章

Pytorch学习之梯度计算backward函数

Pytorch在梯度方面提供的功能,大多是为神经网络而设计的.而官方文档给出的定义和解释比较抽象.以下将结合实例,总结一下自己对Pytorch中梯度计算backward函数的理解. 1. 简单的神经网络构建 首先我们看一个非常简单的神经网络. 假设x1,x2是神经网络的中间层,y是我们的输出层,Y是真实值,L是loss.w1和w2是对应于x1和x2的weight. 上图用数学公式表示为: \(x2= w1 * x1\) \(y = w2 * x2\) \(L = Y - y\) 通常我们会把x1

pytorch 学习笔记之编写 C 扩展,又涨姿势了

pytorch利用CFFI 进行 C 语言扩展.包括两个基本的步骤(docs): 编写 C 代码: python 调用 C 代码,实现相应的 Function 或 Module. 在之前的文章中,我们已经了解了如何自定义 Module.至于 [py]torch 的 C 代码库的结构,我们留待之后讨论: 这里,重点关注,如何在 pytorch C 代码库高层接口的基础上,编写 C 代码,以及如何调用自己编写的 C 代码. 官方示例了如何定义一个加法运算(见 repo).这里我们定义ReLU函数(见

PyTorch学习笔记之nn的简单实例

method 1 1 import torch 2 from torch.autograd import Variable 3 4 N, D_in, H, D_out = 64, 1000, 100, 10 5 x = Variable(torch.randn(N, D_in)) 6 y = Variable(torch.randn(N, D_out), requires_grad=False) 7 8 # define our model as a sequence of layers 9 m

PyTorch教程之Neural Networks

我们可以通过torch.nn package构建神经网络. 现在我们已经了解了autograd,nn基于autograd来定义模型并对他们有所区分. 一个 nn.Module模块由如下部分构成:若干层,以及返回output的forward(input)方法. 例如,这张图描述了进行数字图像分类的神经网络: 这是一个简单的前馈( feed-forward)网络,读入input内容,每层接受前一级的输入,并输出到下一级,直到给出outpu结果. 一个经典神经网络的训练程序如下: 1.定义具有可学习参

Pytorch实现卷积神经网络CNN

Pytorch是torch的Python版本,对TensorFlow造成很大的冲击,TensorFlow无疑是最流行的,但是Pytorch号称在诸多性能上要优于TensorFlow,比如在RNN的训练上,所以Pytorch也吸引了很多人的关注.之前有一篇关于TensorFlow实现的CNN可以用来做对比. 下面我们就开始用Pytorch实现CNN. step 0 导入需要的包 1 import torch 2 import torch.nn as nn 3 from torch.autograd

20170721 PyTorch学习笔记之计算图

1. **args, **kwargs的区别 1 def build_vocab(self, *args, **kwargs): 2 counter = Counter() 3 sources = [] 4 for arg in args: 5 if isinstance(arg, Dataset): 6 sources += [getattr(arg, name) for name, field in 7 arg.fields.items() if field is self] 8 else:

pytorch实现VAE

一.VAE的具体结构 二.VAE的pytorch实现 1加载并规范化MNIST import相关类: from __future__ import print_function import argparse import torch import torch.utils.data import torch.nn as nn import torch.optim as optim from torch.autograd import Variable from torchvision impor

转:pytorch版的bilstm+crf实现sequence label

http://blog.csdn.net/appleml/article/details/78664824 在理解CRF的时候费了一些功夫,将一些难以理解的地方稍微做了下标注,隔三差五看看加强记忆, 代码是pytorch文档上的example import torch import torch.autograd as autograd import torch.nn as nn import torch.optim as optim def to_scalar(var): #var是Variab

【PyTorch深度学习60分钟快速入门 】Part2:Autograd自动化微分

在PyTorch中,集中于所有神经网络的是autograd包.首先,我们简要地看一下此工具包,然后我们将训练第一个神经网络. autograd包为张量的所有操作提供了自动微分.它是一个运行式定义的框架,这意味着你的后向传播是由你的代码运行方式来定义的,并且每一个迭代都可以是不同的. 下面,让我们使用一些更简单的术语和例子来解释这个问题. 0x01 变量(Variable) autograd.Variable是autograd包的核心类,它封装了一个张量,并支持几乎所有在该张量上定义的操作.一旦完