一、autograd.Variable简介
1、Pytorch在autograd模块中实现图计算的相关功能,autograd中的核心数据结构是Variable。Variable封装了tensor,并记录对tensor的操作记录用来构建计算图。
2、Variable结构包含data、grad和grad_fn(可以查看对应引用的Variable的反向传播函数,注意:用户创建的变量为计算图的叶子节点,对应的值为None,原因是叶子节点无法再往后进行函数算子操作比如加减乘除等)。
from torch.autograd import Variable as V import torch as t x = V(t.ones(1)) b = V(t.randn(1),requires_grad = True) w = V(t.randn(1),requires_grad = True) y = w * x z = y + b # z的前向传播函数是加法 z.backward() print(z.grad_fn) # 查看z的反向传播函数,见下图结果红色框
3、Variable的构造函数需要传入tensor,有两个可选参数分别是requires_grad(默认false表示不对该Variable求导)和volatile,并支持大部分tensor支持的函数,除inplace外。
from torch.autograd import Variable as V import torch as t a = V(t.ones(3,4),requires_grad=True) # 创建Variable c = a.sum() # 支持大部分tensor支持的函数,除inplace外,c数据类型依旧是variable
4、如果想要计算各个Variable的梯度,只需要调用根节点Variable的backward方法,autograd会自动沿着计算图反向传播,计算根节点到每一个叶子节点的梯度,采用 Variable.backward(grad_variables=None,…) 方法。
二、grad_variables参数的理解
Variable.backward(grad_variables=None,…) 中的grad_variables参数形状与Variable一致,前向传播后得到 f 是目标函数的值一般是标量(scalar),grad_variables相当于链式法则偏导运算中的一个部分运算结果,一般是从 f 开始计算偏导数到叶子节点。如下面公式:f 表示目标函数(只当作理解参数 grad_variables 的使用,实际并不出现),x 表示叶子节点,y 表示根节点,grad_variables 等于 f 对 y 的偏导的结果。
2.1、当叶子节点都是一维的tensor(此一维是专指创建tensor时参数都是1,1的数目不限)构成的variable基本等同于标量,此时的情况表示目标函数 f 就是 y,说明 grad_variables =1 ,参数grad_variables在y.backward( )中可以直接省略,那么偏导式转换成:
import torch as t from torch.autograd import Variable as V x = V(t.ones(1),requires_grad=True) # 叶子节点x为一维的tensor构成的variable y = x**2 # w = y.sum() y.backward() # y等同于目标函数f,grad_variables=1可直接省略 print(x.grad) # 输出叶子节点x的梯度
2.2、当叶子节点为非一维的tensor构成的variable,此时的情况表示目标函数 f 与 y 初始不等同,那么偏导式整体结构初始不变。但又分两种情况分析:
(1)目标函数 f 是标量,y从非一维tensor构成的variable变成标量。对y采用sum()方法实现非一维tensor构成的variable变成标量,说明 grad_variables =1 ,参数grad_variables在f.backward( )中可以直接省略。
import torch as t from torch.autograd import Variable as V x = V(t.ones(2),requires_grad=True) # 叶子节点x为非一维的tensor构成的variable y = x**2 f = y.sum() # 对y采用sum()方法实现非一维tensor构成的variable变成标量 f.backward() # 标量对标量不用求导,所以直接省略 print(x.grad) # 输出叶子节点x的梯度
(2)目标函数 f 是标量,y是非一维tensor构成的variable。那么 f 对 y 求偏导的结果也是非一维tensor构成的variable,并且维度大小和 y 一样,所以采用y.backward(t.ones(y.size())):这是把 y 当作被偏导数,t.ones(y.size())是f 对 y 求偏导的结果,y.size()是表示结果的维度和 y 保持相同设定。
import torch as t from torch.autograd import Variable as V x = V(t.ones(2),requires_grad=True) # 叶子节点x为非一维的tensor构成的variable y = x**2 # f = y.sum() # 对y采用sum()方法实现非一维tensor变成标量 y.backward(t.ones(y.size())) # f 对 y 求偏导 print(x.grad) # 输出叶子节点x的梯度