一.张量.requires_grad和张量.is_leaf
- 张量中的requires_grad属性表明的是该张量是否需要求导
- 只要在反向传播(求导)过程中涉及到的张量,起requires_grad均应该是True
- requires_grad属性默认为False,需要该属性为True时,需要显示设置;当一个节点由requires_grad=True的节点计算得到时,该节点的requires_grad也为True,此时不需要在显示设置
- 张量中的is_leaf属性表明的是该张量是否是叶子节点,叶子节点即用户自己给定的节点,非叶子节点即计算图中由其他节点计算得到的节点
- 实际使用过程中,定义节点时候is_leaf属性给定,程序会自动根据该节点的来源确定该节点是否为叶子节点,但要注意,随着计算图的构建,一个节点是否为叶子节点会发生变化
a = torch.tensor([1.],requires_grad=True) #此时a是叶子节点
a = b + c #a由其他节点表示,a变成非叶子节点
a = a + 1 #这也会使a变成非叶子节点
- 叶子节点决定了经过反向传播backward()后该节点的梯度是否保留.只要是反向传播过程中涉及到的张量,其梯度在反向传播时均进行了计算,但只有叶子节点的梯度得到了记录和保留,可以在之后的权值更新时候调用
- 一般来说,叶子节点的requires_grad属性均需要设置为True
二.in-place操作(原地操作)和普通操作
- pytorch中,操作后带有_的操作,+=,-=,*=,/=操作为in-place操作(原地操作)
- in-place操作不改变变量的地址,仅值改变变量的值
- in-place操作在某些情况下可以减小内存使用,但多数情况效果不明显,因此如果不是内存严重不足,不建议用in-place操作
- 以加法为例辨析
a = torch.tensor([1.],requires_grad=True)
b = torch.tensor([2.],requires_grad=True)
c = a + b
#以下操作均为a+b
a = torch.add(a,b) #使用add()函数进行a加b操作,将相加结果返回,非in-place操作,相加前后a的地址发生了变化
a = a.add(b) #使用类内方法.add()进行a加b操作,将相加结果返回,非in-place操作,,相加前后a的地址发生了变化
#使用类内方法.add_()进行c加b操作,将相加结果返回,并赋值给c,in-place操作,相加前后c的地址保持不变
c.add_(b)
- 反向传播过程中涉及的张量和叶子张量不可以使用in-place操作
- 自动求导对in-place操作的控制:实际上,每个张量都有一个参数._version,初始值为0,张量每进行依次in-place操作操作,该值+1,当反向求导时,会对反向传播中涉及到的张量._version进行检查,如果不为0,则会报错.
- 当对叶子节点进行in-place操作操作时候,会报错
三.张量和张量.data
- 张量是一个tensor类的对象,张量.data是张量的一个属性
- 对张量进行加减乘除等运算实际上是在对计算图进行构建和修改,如a本来是叶子节点,a = b + c就会使得a变成非叶子节点,这相当于在添加了计算图a = b + c,从而是的a是由b节点和c节点计算得到的,因此变成了非叶子节点
- 对张量.data进行更改可以理解为对张量中的数据进行更改,但没有更改计算图
- 因此,在构建计算图时候,使用张量进行计算,在只想更新数据的时候,使用张量.data进行数据修改
- 任何节点的张量.data均可以使用in-place操作
四.权值更新
如对于权值张量a,反向传播后其梯度为a.grad,学习率为lr,权值更新数学公式为a = a + lr*a.grad
错误的更新方法
a = a + lr*a.grad #更新后a变成了非叶子节点,下次反向传播时其梯度不保存,无法进行再次更新
a = a.add(lr*a.grad) #更新后a变成了非叶子节点,下次反向传播时其梯度不保存,无法进行再次更新
a.add_(lr*a.grad) #叶子节点不能进行in-place操作
正确的更新方法
即权值更新用张量.data进行计算,网络构建采用张量直接计算
a.data.add_(lr*a.grad)
a.data = a.data + lr*a.grad