前言:最近越看代码越觉得自己要补的东西很多很多,我觉得了解pytorch框架中的梯度计算过程其实还是很重要的,于是打算码一篇关于动态计算图的文章。
首先明确一点:pytorch使用的是动态计算图,也就是计算图的创建过程是随着张量的运算式子而创建的,并且不断更新,直到张量的运算结束。这和tensorflow框架所使用的静态图产生了对比,静态图的创建过程是在张量的运算全部走完,然后直接创建的一张图。
一.Tensor
张量就是n维向量,数据类型就是tensor类型,tensor类型中的有8个很重要的属性:
data:张量的数据
dtype:张量的数据类型
shape:张量的形状
device:张量所在的设备位置,一般是CPU/GPU,是加速的关键
grad:梯度值
grad_fn:梯度反向传播过程中,告诉你某中间变量是通过哪种运算得来的
requires_grad:是否可微,一般默认为False
is_leaf:是否为也叶子节点
这八个属性前四个表示了该张量数据层面的属性,后四个表示了该张量的微分属性。
二.动态计算图
动态计算图的特点主要通过下面的例子来说明,可以通过例子仔细体会:
首先创建两个可微分的张量,这两个张量都是叶子节点:
x = torch.tensor([3.], requires_grad=True)
w = torch.tensor([2.], requires_grad=True)
然后创建两个中间变量:
y = x + w
z = y ** 2
注意,由于叶子节点张量x和w是可微分的,所以y和z都是可微的,我们可以通过以下打印结果看出:
print(y.requires_grad)
print(z.requires_grad)
'''
True
True
'''
接下来,通过对z的反向传播,我们可以求出此时x和w的梯度:
z.backward()
print(w.grad)
print(x.grad)
'''
tensor([10.])
tensor([10.])
'''
值得注意的是:
1.动态计算图在执行完backward()操作之后,计算图就会被销毁,如果再次使用z.backward(),会直接报错,可以在执行第一次反向传播操作时使用z.backward(retain_graph=True)来保存计算图。
2.叶子节点的梯度值会根据反向转播次数的增加不断地累计,这个小细节要注意!并且在反向传播之后,中间节点张量的梯度也会跟着销毁(省内存),所以遇到一些要保留中间节点梯度的情况,我们需要使用retain_grad()函数。
3.如果我们想在变量y之后的计算不加入计算图(说白了就是让z的requires_grad的值为False),有以下两种方法:
方法一:
with torch.no_grad():
z = y ** 2
方法二(detach函数只复制了tensor的数值,而requires_grad=False):
y1 = y.detach()
z = y1 ** 2
这个时候z已经不是可微的了,并且也不能执行反向传播操作了,但我们仍然可以将y进行反向传播。