前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PyTorch3:计算图torch.autograph

PyTorch3:计算图torch.autograph

作者头像
小胡胡说
修改2020-08-06 18:19:13
1.2K0
修改2020-08-06 18:19:13
举报
文章被收录于专栏:小胡学编程

神经网络的训练过程是一个不断更新权重的过程,而权重的更新要使用到反向传播,而反向传播的本质呢就是求导数

1. 计算图


一个深度学习模型是由“计算图”所构成的。

计算图是一个有向无环图。数据是图的节点,运算是图的

计算图
计算图

上图所示的这张计算图的数学表达式为y=(x+w)*(w+1) 。其中,x,w和b 是由用户定义的,称为“叶子节点”,可在Pytorch中加以验证:

代码语言:javascript
复制
a = torch.tensor([1.])
b = torch.tensor([2.])
c = a.add(b)

a.is_leaf() # True
c.is_leaf() # False

计算图可以分为动态图与静态图两种。

1.1 动态图


动态图的搭建过程与执行过程可以同时进行。PyTorch 默认采用动态图机制。

我们看一个例子:

代码语言:javascript
复制
import torch
first_counter = torch.Tensor([0])
second_counter = torch.Tensor([10])
 
while (first_counter[0] < second_counter[0]): #[0] 加不加没有影响
    first_counter += 2
    second_counter += 1
 
print(first_counter)
print(second_counter)

1.2 静态图

静态图先创建计算图,然后执行计算图。计算图一经定义,无法改变。TensorFlow 2.0 以前以静态图为主。

我们看同样的例子在 TensorFlow 2.0 以前是怎么搭建的:

代码语言:javascript
复制
import tensorflow as tf
first_counter = tf.constant(0) # 定义变量
second_counter = tf.constant(10) # 定义变量

def cond(first_counter, second_counter, *args): # 定义条件
    return first_counter < second_counter
def body(first_counter, second_counter): # 定义条件
    first_counter = tf.add(first_counter, 2)
    second_counter = tf.add(second_counter, 1)
    return first_counter, second_counter
    
c1, c2 = tf.while_loop(cond, body, [first_counter, second_counter]) # 定义循环

with tf.Session() as sess: # 建立会话执行计算图
    counter_1_res, counter_2_res = sess.run([c1, c2])

print(first_counter)
print(second_counter)

因为静态图在设计好以后不能改变,调试的过程中 debug 实在太痛苦了。

所以 TensorFlow 2.0 开始默认使用动态图。

1.3 计算图示例


假如我们想计算上面计算图中y=(x+w)*(w+1)x=2,w=1 时的导数:

在 PyTorch 中求导数非常简单,使用 tensor.backward()即可:

代码语言:javascript
复制
import torch

x = torch.tensor([2.], requires_grad=True) # 开启导数追踪
w = torch.tensor([1.], requires_grad=True) # 开启导数追踪

a = w.add(x)
b = w.add(1)
y = a.mul(b)

y.backward() # 求导
print(w.grad)

2. derivative(导数)的概述


如何求导数是中学的数学知识,这里不再过多赘述.

仅仅提一点,对求某某的 “偏导数”,此时仅将这一变量当作变量,其他不相关的变量被看成常量,在求导时消去。

3. chain rule 运算规则


假如我们想对z=f(g(x)) 求导,可以设 y=g(x),z=f(x)则:z对x的导数等于z对y求导乘上y对x求导

4. 张量的反向传播


张量的求导函数为:

代码语言:javascript
复制
tensor.backward(gradient=None, retain_graph=None, create_graph=False)

4.1 运算结果为 0 维张量的反向传播


我们自己创建的 tensor 叫做创建变量,通过运算生成的 tensor 叫做结果变量

tensor 的一个创建方法为

代码语言:javascript
复制
torch.tensor(data, dtype=None, device=None, requires_grad=False, pin_memory=False)

别的不说,单单说 requires_grad。如果想求这个 tensor 的导数,这个变量必须设为 Truerequires_grad 的默认值为 False

代码语言:javascript
复制
>>> a = torch.tensor(2.)
>>> a.requires_grad
False
>>> a
tensor(1.)

而所有基于叶子节点生成的 tenor 的 requires_grad 属性与叶子节点相同。

代码语言:javascript
复制
>>> b = a**2 + 1
>>> b.requires_grad
False
>>> b
tensor(5.)

如果没有在创建的时候显式声明 requires_grad=True,也可以在用之前临时声明:

代码语言:javascript
复制
>>> a.requires_grad_(True)
>>> a.requires_grad = True # 另一种写法
>>> a
tensor(2., requires_grad=True)

而因为 b = a**2 + 1,此时 b 的属性变成了

代码语言:javascript
复制
tensor(5., grad_fn=<AddBackward0>)

想对 b 求导,使用 b.backward() 即可:

代码语言:javascript
复制
>>> b.backward()

查看 aa = 2 处的导数,使用 a.grad 即可:

代码语言:javascript
复制
>>> a.grad
tensor(4.)

4.2 运算结果为 1 维以上张量的反向传播


如果结果为1 维以上张量,直接求导会出错:

代码语言:javascript
复制
>>> a = torch.tensor([1., 2.], requires_grad=True)
>>> b = a**2 + 1
>>> b
tensor([2., 5.], grad_fn=<AddBackward0>)
>>> b.backward()
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-391-a721975e1357> in <module>
----> 1 b.backward()
...
RuntimeError: grad can be implicitly created only for scalar outputs

这是因为 [2., 3.] 没法求导。这时候就必须指定 backward() 中的 gradient 变量为一个与创建变量维度相同的变量作为权重,这里以 torch.tensor([1., 1.]) 为例:

代码语言:javascript
复制
>>> b.backward(gradient=torch.tensor([1., 1.]))
>>> b.backward(gradient=torch.ones_like([1., 1.])) # 创建一个与 a 维度相同的全 1 张量
>>> a.grad
tensor([2., 4.])

5. 张量的显式求导 torch.augograd.grad


虽然我们可以通过 b.backward() 来计算 a.grad 的值,下面这个函数可以直接求得导数。

代码语言:javascript
复制
torch.autograd.grad(outputs, inputs, grad_outputs=None, retain_graph=None, create_graph=False, only_inputs=True, allow_unused=False)

y=f(x)为例,inputsxoutputsy。如果 是 0 维张量,grad_outputs 可以忽略;否则需要为一个与x维度相同的张量作为权重。

代码语言:javascript
复制
>>> x=torch.tensor([[1.,2.,3.],[4.,5.,6.]],requires_grad=True)
>>> y=x+2
>>> z=y*y*3
>>> dzdx = torch.autograd.grad(inputs=x, outputs=z, grad_outputs=torch.ones_like(x))
>>> print(dzdx)
(tensor([[18., 24., 30.],
        [36., 42., 48.]])

假如我们1⃣以上面的z对x求导 ,结果为6(x+2) 。假如我们想用z对x求二阶偏导呢?会报错:

代码语言:javascript
复制
>>> dzdx = torch.autograd.grad(inputs=x, outputs=z, grad_outputs=torch.ones_like(x))
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-440-7a6333e01d6f> in <module>
----> 1 dzdx = torch.autograd.grad(inputs=x, outputs=z, grad_outputs=torch.ones_like(x))
...
RuntimeError: Trying to backward through the graph a second time, but the buffers have already been freed. Specify retain_graph=True when calling backward the first time.

这是因为动态计算图的特点是使用完毕后会被释放,当我们对 b 求导的话,对 b 求导的计算图在使用完毕后就被释放了。如果我们想求二阶导数,需要设置 retain_graph=Truecreate_graph=Trueretain_graph 为保存计算图,create_graph 为创建计算图,两者的作用是相同的,都可以保存当前计算图。

代码语言:javascript
复制
>>> dzdx = torch.autograd.grad(inputs=x, outputs=z, grad_outputs=torch.ones_like(x),create_graph=True)
>>> dz2dx2 = torch.autograd.grad(inputs=x, outputs=dzdx, grad_outputs=torch.ones_like(x))
>>> print(dz2dx2)
(tensor([[6., 6., 6.],
        [6., 6., 6.]]),)

6. 张量的显式反向传播计算torch.autograd.backward


代码语言:javascript
复制
torch.autograd.backward(tensors, grad_tensors=None, retain_graph=None, create_graph=False)

以上面的 ab 为例,b.backward() = torch.autograd.backward(b)。其中 grad_tensorsb.backward() 中的 gradient 变量作用相同;retain_graphcreate_graphtorch.augograd.grad 中的同名变量相同,不再赘述。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 计算图
    • 1.1 动态图
      • 1.2 静态图
        • 1.3 计算图示例
        • 2. derivative(导数)的概述
        • 3. chain rule 运算规则
        • 4. 张量的反向传播
          • 4.1 运算结果为 0 维张量的反向传播
            • 4.2 运算结果为 1 维以上张量的反向传播
            • 5. 张量的显式求导 torch.augograd.grad
            • 6. 张量的显式反向传播计算torch.autograd.backward
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档