在PyTorch中计算图的特点可总结如下:

  • autograd根据用户对variable的操作构建其计算图。对变量的操作抽象为Function
  • 对于那些不是任何函数(Function)的输出,由用户创建的节点称为叶子节点,叶子节点的grad_fn为None。叶子节点中需要求导的variable,具有AccumulateGrad标识,因其梯度是累加的。
  • variable默认是不需要求导的,即requires_grad属性默认为False,如果某一个节点requires_grad被设置为True,那么所有依赖它的节点requires_grad都为True。
  • variable的volatile属性默认为False,如果某一个variable的volatile属性被设为True,那么所有依赖它的节点volatile属性都为True。volatile属性为True的节点不会求导,volatile的优先级比requires_grad高。
  • 多次反向传播时,梯度是累加的。反向传播的中间缓存会被清空,为进行多次反向传播需指定retain_graph=True来保存这些缓存。
  • 非叶子节点的梯度计算完之后即被清空,可以使用autograd.gradhook技术获取非叶子节点的值。
  • variable的grad与data形状一致,应避免直接修改variable.data,因为对data的直接操作无法利用autograd进行反向传播
  • 反向传播函数backward的参数grad_variables可以看成链式求导的中间结果,如果是标量,可以省略,默认为1
  • PyTorch采用动态图设计,可以很方便地查看中间层的输出,动态的设计计算图结构。

Variable类和计算图

简单的建立一个计算图,便于理解几个相关知识点:

  • requires_grad  是否要求导数,默认False,叶节点指定True后,依赖节点都被置为True

  • .backward()  根Variable的方法会反向求解叶Variable的梯度

  • .backward()方法grad_variable参数  形状与根Variable一致,非标量Variable反向传播方向指定

  • 叶节点  由用户创建的计算图Variable对象,反向传播后会保留梯度grad数值,其他Variable会清空为None

  • grad_fn  指向创建Tensor的Function,如果某一个对象由用户创建,则指向None

  • .is_leaf  是否是叶节点

  • .grad_fn.next_functions  本节点接收的上级节点的grad_fn

  • .volatile  是否处于推理模式

import torch as t
from torch.autograd import Variable as V a = V(t.ones(3,4),requires_grad=True)
b = V(t.zeros(3,4))
c = a.add(b)
d = c.sum()
d.backward() # 虽然没有要求cd的梯度,但是cd依赖于a,所以a要求求导则cd梯度属性会被默认置为True
print(a.requires_grad, b.requires_grad, c.requires_grad,d.requires_grad)
# 叶节点(由用户创建)的grad_fn指向None
print(a.is_leaf, b.is_leaf, c.is_leaf,d.is_leaf)
# 中间节点虽然要求求梯度,但是由于不是叶节点,其梯度不会保留,所以仍然是None
print(a.grad,b.grad,c.grad,d.grad)
True False True True
True True False False
Variable containing:
1 1 1 1
1 1 1 1
1 1 1 1
[torch.FloatTensor of size 3x4]
None None None
print('\n',a.grad_fn,'\n',b.grad_fn,'\n',c.grad_fn,'\n',d.grad_fn)
None
None
<AddBackward1 object at 0x000002A2F3D2EBA8>
<SumBackward0 object at 0x000002A2F3D2ECC0>

模拟一个简单的反向传播:

def f(x):
"""x^2 * e^x"""
y = x**2 * t.exp(x)
return y def gradf(x):
"""2*x*e^x + x^2*e^x"""
dx = 2*x*t.exp(x) + x**2*t.exp(x)
return dx x = V(t.randn(3,4), requires_grad=True)
y = f(x)
y.backward(t.ones(y.size()))
print(x.grad)
print(gradf(x))
Variable containing:
-0.3315 3.5068 -0.1079 -0.4308
-0.1202 -0.4529 -0.1873 0.6514
0.2343 0.1050 0.1223 15.9192
[torch.FloatTensor of size 3x4] Variable containing:
-0.3315 3.5068 -0.1079 -0.4308
-0.1202 -0.4529 -0.1873 0.6514
0.2343 0.1050 0.1223 15.9192
[torch.FloatTensor of size 3x4]

结果一致。

.grad_fn.next_functions

x = V(t.ones(1))
w = V(t.rand(1),requires_grad=True)
b = V(t.rand(1),requires_grad=True) y = w.mul(x)
z = y.add(b) print(x.is_leaf,w.is_leaf,b.is_leaf,y.is_leaf,z.is_leaf)
print(x.requires_grad,w.requires_grad,b.requires_grad,y.requires_grad,z.requires_grad)
print(x.grad_fn,w.grad_fn,b.grad_fn,y.grad_fn,z.grad_fn) # grad_fn.next_functions
# grad_fn.next_functions代表了本节点的输入节点信息,grad_fn表示了本节点的输出信息
# 叶子结点grad_fn为None,没有next_functions,但是间接查询到AccumulateGrad object表示该叶子节点
# 接受梯度更新,查询到None表示不接受更新
print(y.grad_fn.next_functions,z.grad_fn.next_functions)
print(z.grad_fn.next_functions[0][0]==y.grad_fn)
print(z.grad_fn.next_functions[0][0],y.grad_fn)
.is_leaf
True True True False False .requires_grad
False True True True True .grad_fn
None None None <MulBackward1 object at 0x000002A2F57F5710> <AddBackward1 object at 0x000002A2F57F5630> .grad_fn.next_functions
((<AccumulateGrad object at 0x000002A2F57F5630>, 0), (None, 0))
((<MulBackward1 object at 0x000002A2F57F57B8>, 0),
(<AccumulateGrad object at 0x000002A2F57F57F0>, 0))
z.grad_fn.next_functions[0][0]==y.grad_fn
True
z.grad_fn.next_functions[0][0],y.grad_fn
<MulBackward1 object at 0x000002A2F57F57F0> <MulBackward1 object at 0x000002A2F57F57F0>

.volatile

# volatile
# 节省显存提高效用的参数volatile,也会作用于依赖路径全部的Variable上,且优先级高于requires_grad,
# 这样我们在实际设计网络时不必修改其他叶子结点的requires_grad属性,只要将输入叶子volatile=True即可 x = V(t.ones(1),volatile=True)
w = V(t.rand(1),requires_grad=True)
y = w.mul(x)
print(x.requires_grad,w.requires_grad,y.requires_grad)
print(x.volatile,w.volatile,y.volatile)
False True False
True False True

附录、Variable类源码简介

class Variable(_C._VariableBase):

    """
Attributes:
data: 任意类型的封装好的张量。
grad: 保存与data类型和位置相匹配的梯度,此属性难以分配并且不能重新分配。
requires_grad: 标记变量是否已经由一个需要调用到此变量的子图创建的bool值。只能在叶子变量上进行修改。
volatile: 标记变量是否能在推理模式下应用(如不保存历史记录)的bool值。只能在叶变量上更改。
is_leaf: 标记变量是否是图叶子(如由用户创建的变量)的bool值.
grad_fn: Gradient function graph trace. Parameters:
data (any tensor class): 要包装的张量.
requires_grad (bool): bool型的标记值. **Keyword only.**
volatile (bool): bool型的标记值. **Keyword only.**
""" def backward(self, gradient=None, retain_graph=None, create_graph=None, retain_variables=None):
"""计算关于当前图叶子变量的梯度,图使用链式法则导致分化
如果Variable是一个标量(例如它包含一个单元素数据),你无需对backward()指定任何参数
如果变量不是标量(包含多个元素数据的矢量)且需要梯度,函数需要额外的梯度;
需要指定一个和tensor的形状匹配的grad_output参数(y在指定方向投影对x的导数);
可以是一个类型和位置相匹配且包含与自身相关的不同函数梯度的张量。
函数在叶子上累积梯度,调用前需要对该叶子进行清零。 Arguments:
grad_variables (Tensor, Variable or None):
变量的梯度,如果是一个张量,除非“create_graph”是True,否则会自动转换成volatile型的变量。
可以为标量变量或不需要grad的值指定None值。如果None值可接受,则此参数可选。
retain_graph (bool, optional): 如果为False,用来计算梯度的图将被释放。
在几乎所有情况下,将此选项设置为True不是必需的,通常可以以更有效的方式解决。
默认值为create_graph的值。
create_graph (bool, optional): 为True时,会构造一个导数的图,用来计算出更高阶导数结果。
默认为False,除非``gradient``是一个volatile变量。
"""
torch.autograd.backward(self, gradient, retain_graph, create_graph, retain_variables) def register_hook(self, hook):
"""Registers a backward hook. 每当与variable相关的梯度被计算时调用hook,hook的申明:hook(grad)->Variable or None
不能对hook的参数进行修改,但可以选择性地返回一个新的梯度以用在`grad`的相应位置。 函数返回一个handle,其``handle.remove()``方法用于将hook从模块中移除。 Example:
>>> v = Variable(torch.Tensor([0, 0, 0]), requires_grad=True)
>>> h = v.register_hook(lambda grad: grad * 2) # double the gradient
>>> v.backward(torch.Tensor([1, 1, 1]))
>>> v.grad.data
2
2
2
[torch.FloatTensor of size 3]
>>> h.remove() # removes the hook
"""
if self.volatile:
raise RuntimeError("cannot register a hook on a volatile variable")
if not self.requires_grad:
raise RuntimeError("cannot register a hook on a variable that "
"doesn't require gradient")
if self._backward_hooks is None:
self._backward_hooks = OrderedDict()
if self.grad_fn is not None:
self.grad_fn._register_hook_dict(self)
handle = hooks.RemovableHandle(self._backward_hooks)
self._backward_hooks[handle.id] = hook
return handle def reinforce(self, reward):
"""Registers a reward obtained as a result of a stochastic process.
区分随机节点需要为他们提供reward值。如果图表中包含任何的随机操作,都应该在其输出上调用此函数,否则会出现错误。
Parameters:
reward(Tensor): 带有每个元素奖赏的张量,必须与Variable数据的设备位置和形状相匹配。
"""
if not isinstance(self.grad_fn, StochasticFunction):
raise RuntimeError("reinforce() can be only called on outputs "
"of stochastic functions")
self.grad_fn._reinforce(reward) def detach(self):
"""返回一个从当前图分离出来的心变量。
结果不需要梯度,如果输入是volatile,则输出也是volatile。 .. 注意::
返回变量使用与原始变量相同的数据张量,并且可以看到其中任何一个的就地修改,并且可能会触发正确性检查中的错误。
"""
result = NoGrad()(self) # this is needed, because it merges version counters
result._grad_fn = None
return result def detach_(self):
"""从创建它的图中分离出变量并作为该图的一个叶子"""
self._grad_fn = None
self.requires_grad = False def retain_grad(self):
"""Enables .grad attribute for non-leaf Variables."""
if self.grad_fn is None: # no-op for leaves
return
if not self.requires_grad:
raise RuntimeError("can't retain_grad on Variable that has requires_grad=False")
if hasattr(self, 'retains_grad'):
return
weak_self = weakref.ref(self) def retain_grad_hook(grad):
var = weak_self()
if var is None:
return
if var._grad is None:
var._grad = grad.clone()
else:
var._grad = var._grad + grad self.register_hook(retain_grad_hook)
self.retains_grad = True

『PyTorch』第五弹_深入理解autograd_上:Variable属性方法的更多相关文章

  1. 『PyTorch』第五弹_深入理解autograd_下:函数扩展&高阶导数

    一.封装新的PyTorch函数 继承Function类 forward:输入Variable->中间计算Tensor->输出Variable backward:均使用Variable 线性 ...

  2. 『PyTorch』第五弹_深入理解autograd_中:Variable梯度探究

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

  3. 『PyTorch』第五弹_深入理解Tensor对象_中下:数学计算以及numpy比较_&_广播原理简介

    一.简单数学操作 1.逐元素操作 t.clamp(a,min=2,max=4)近似于tf.clip_by_value(A, min, max),修剪值域. a = t.arange(0,6).view ...

  4. 『PyTorch』第五弹_深入理解Tensor对象_下:从内存看Tensor

    Tensor存储结构如下, 如图所示,实际上很可能多个信息区对应于同一个存储区,也就是上一节我们说到的,初始化或者普通索引时经常会有这种情况. 一.几种共享内存的情况 view a = t.arang ...

  5. 『PyTorch』第五弹_深入理解Tensor对象_中上:索引

    一.普通索引 示例 a = t.Tensor(4,5) print(a) print(a[0:1,:2]) print(a[0,:2]) # 注意和前一种索引出来的值相同,shape不同 print( ...

  6. 『PyTorch』第五弹_深入理解Tensor对象_上:初始化以及尺寸调整

    一.创建Tensor 特殊方法: t.arange(1,6,2)t.linspace(1,10,3)t.randn(2,3) # 标准分布,*size t.randperm(5) # 随机排序,从0到 ...

  7. 『PyTorch』第四弹_通过LeNet初识pytorch神经网络_下

    『PyTorch』第四弹_通过LeNet初识pytorch神经网络_上 # Author : Hellcat # Time : 2018/2/11 import torch as t import t ...

  8. 『PyTorch』第十弹_循环神经网络

    RNN基础: 『cs231n』作业3问题1选讲_通过代码理解RNN&图像标注训练 TensorFlow RNN: 『TensotFlow』基础RNN网络分类问题 『TensotFlow』基础R ...

  9. 『PyTorch』第四弹_通过LeNet初识pytorch神经网络_上

    总结一下相关概念: torch.Tensor - 一个近似多维数组的数据结构 autograd.Variable - 改变Tensor并且记录下来操作的历史记录.和Tensor拥有相同的API,以及b ...

随机推荐

  1. [hgoi#2019/2/24]玄学考试

    感想 对于这次考试,真的不想说什么了,太玄学了!!! t1输出比标准输出长,这是什么操作???难道要关文件???但是交到oj上又A掉了.这是什么操作. t2还好,没有出什么意外...但是要吐槽一下出题 ...

  2. yiming

      关于双城 专家团队 总裁专栏 双城荣誉 诚聘英才 双城著作移民服务 移民专题 移民专刊 移民百科 成功案例 联系我们   澳大利亚 匈牙利 美国 加拿大 欧洲 瓦努阿图 双城集团 双城地产 君益诚 ...

  3. java NIO 直接与非直接缓冲区

    ByteBuffer有两个创建缓冲区的方法:static ByteBuffer allocate(int capacity)static ByteBuffer allocateDirect(int c ...

  4. 工具函数判断data为整型字符串

    /** * 如果data是整型字符串,则转为整型,否则原样返回 * @param {*} data 整型字符串 */ export const stringToInt = (data) => { ...

  5. CentOS6.7下Ansible部署

    Ansible是一种集成IT系统的配置管理, 应用部署, 执行特定任务的开源平台. 它基于Python语言实现, 部署只需在主控端部署Ansible环境, 被控端无需安装代理工具, 只需打开SSH, ...

  6. UTF-8 GBK GB2312

    至于UTF-8编码则是用以解决国际上字符的一种多字节编码,它对英文使用8位(即一个字节),中文使用24位(三个字节)来编码.对于英文字符较多的论坛则用UTF-8节省空间. GBK包含全部中文字符:UT ...

  7. AtomicInteger和count++的比较

    J2SE 5.0提供了一组atomic class来帮助我们简化同步处理.基本工作原理是使用了同步synchronized的方法实现了对一个long, integer, 对象的增.减.赋值(更新)操作 ...

  8. 强大的svg操作库——Raphael

    先常规先引入Raphael库: <script src="raphael.js" type="text/javascript"></scrip ...

  9. 条理清晰的搭建SSH环境之添加所需jar包

    一.首先介绍要添加框架环境: JUnit Struts2 Hibernate Spring (1)配置JUnit /**-------------------------添加JUnit-------- ...

  10. Spark记录-SparkSql官方文档中文翻译(部分转载)

    1 概述(Overview) Spark SQL是Spark的一个组件,用于结构化数据的计算.Spark SQL提供了一个称为DataFrames的编程抽象,DataFrames可以充当分布式SQL查 ...