Pytorch Note6 自动求导Autograd
Posted Real&Love
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Pytorch Note6 自动求导Autograd相关的知识,希望对你有一定的参考价值。
全部笔记的汇总贴:Pytorch Note 快乐星球
自动求导
用Tensor训练网络很方便,但有时候反向传播过程需要手动实现。这对于像线性回归等较为简单的模型来说,还可以应付,但实际使用中经常出现非常复杂的网络结构,此时如果手动实现反向传播,不仅费时费力,而且容易出错,难以检查。torch.autograd就是为方便用户使用,而专门开发的一套自动求导引擎,它能够根据输入和前向传播过程自动构建计算图,并执行反向传播。
计算图(Computation Graph)是现代深度学习框架如PyTorch和TensorFlow等的核心,其为高效自动求导算法——反向传播(Back Propogation)提供了理论支持,了解计算图在实际写程序过程中会有极大的帮助。
import torch
from torch.autograd import Variable
简单情况的自动求导
下面我们显示一些简单情况的自动求导,"简单"体现在计算的结果都是标量,也就是一个数,我们对这个标量进行自动求导。
x = Variable(torch.Tensor([2]), requires_grad=True)
y = x + 2
z = y ** 2 + 3
print(z)
tensor([19.], grad_fn=)
通过上面的一些列操作,我们从 x 得到了最后的结果out,我们可以将其表示为数学公式
z = ( x + 2 ) 2 + 3 z = (x + 2)^2 + 3 z=(x+2)2+3
那么我们从 z 对 x 求导的结果就是
∂
z
∂
x
=
2
(
x
+
2
)
=
2
(
2
+
2
)
=
8
\\frac{\\partial z}{\\partial x} = 2 (x + 2) = 2 (2 + 2) = 8
∂x∂z=2(x+2)=2(2+2)=8
如果你对求导不熟悉,可以查看以下网址进行复习
# 使用自动求导
z.backward()
print(x.grad)
tensor([8.])
对于上面这样一个简单的例子,我们验证了自动求导,同时可以发现发现使用自动求导非常方便。如果是一个更加复杂的例子,那么手动求导就会显得非常的麻烦,所以自动求导的机制能够帮助我们省去麻烦的数学计算,下面我们可以看一个更加复杂的例子。
x = Variable(torch.randn(10, 20), requires_grad=True)
y = Variable(torch.randn(10, 5), requires_grad=True)
w = Variable(torch.randn(20, 5), requires_grad=True)
out = torch.mean(y - torch.matmul(x, w)) # torch.matmul 是做矩阵乘法
out.backward()
如果你对矩阵乘法不熟悉,可以查看下面的网址进行复习
# 得到 x 的梯度
print(x.grad)
# 得到 y 的的梯度
print(y.grad)
# 得到 w 的梯度
print(w.grad)
上面数学公式就更加复杂,矩阵乘法之后对两个矩阵对应元素相乘,然后所有元素求平均,有兴趣的同学可以手动去计算一下梯度,使用 PyTorch 的自动求导,我们能够非常容易得到 x, y 和 w 的导数,因为深度学习中充满大量的矩阵运算,所以我们没有办法手动去求这些导数,有了自动求导能够非常方便地解决网络更新的问题。
复杂情况的自动求导
上面我们展示了简单情况下的自动求导,都是对标量进行自动求导,可能你会有一个疑问,如何对一个向量或者矩阵自动求导了呢?感兴趣的同学可以自己先去尝试一下,下面我们会介绍对多维数组的自动求导机制。
m = Variable(torch.FloatTensor([[2, 3]]), requires_grad=True) # 构建一个 1 x 2 的矩阵
n = Variable(torch.zeros(1, 2)) # 构建一个相同大小的 0 矩阵
print(m)
print(n)
Variable containing:
2 3
[torch.FloatTensor of size 1x2]
Variable containing:
0 0
[torch.FloatTensor of size 1x2]
# 通过 m 中的值计算新的 n 中的值
n[0, 0] = m[0, 0] ** 2
n[0, 1] = m[0, 1] ** 3
print(n)
Variable containing:
4 27
[torch.FloatTensor of size 1x2]
将上面的式子写成数学公式,可以得到
n
=
(
n
0
,
n
1
)
=
(
m
0
2
,
m
1
3
)
=
(
2
2
,
3
3
)
n = (n_0,\\ n_1) = (m_0^2,\\ m_1^3) = (2^2,\\ 3^3)
n=(n0, n1)=(m02, m13)=(22, 33)
下面我们直接对 n 进行反向传播,也就是求 n 对 m 的导数。
这时我们需要明确这个导数的定义,即如何定义
∂
n
∂
m
=
∂
(
n
0
,
n
1
)
∂
(
m
0
,
m
1
)
\\frac{\\partial n}{\\partial m} = \\frac{\\partial (n_0,\\ n_1)}{\\partial (m_0,\\ m_1)}
∂m∂n=∂(m0, m1)∂(n0, n1)
在 PyTorch 中,如果要调用自动求导,需要往backward()
中传入一个参数,这个参数的形状和 n 一样大,比如是
(
w
0
,
w
1
)
(w_0,\\ w_1)
(w0, w1),那么自动求导的结果就是:
∂
n
∂
m
0
=
w
0
∂
n
0
∂
m
0
+
w
1
∂
n
1
∂
m
0
\\frac{\\partial n}{\\partial m_0} = w_0 \\frac{\\partial n_0}{\\partial m_0} + w_1 \\frac{\\partial n_1}{\\partial m_0}
∂m0∂n=w0∂m0∂n0+w1∂m0∂n1
∂
n
∂
m
1
=
w
0
∂
n
0
∂
m
1
+
w
1
∂
n
1
∂
m
1
\\frac{\\partial n}{\\partial m_1} = w_0 \\frac{\\partial n_0}{\\partial m_1} + w_1 \\frac{\\partial n_1}{\\partial m_1}
∂m1∂n=w0∂m1∂n0+w1∂m1∂n1
n.backward(torch.ones_like(n)) # 将 (w0, w1) 取成 (1, 1)
print(m.grad)
Variable containing:
4 27
[torch.FloatTensor of size 1x2]
通过自动求导我们得到了梯度是 4 和 27,我们可以验算一下
∂
n
∂
m
0
=
w
0
∂
n
0
∂
m
0
+
w
1
∂
n
1
∂
m
0
=
2
m
0
+
0
=
2
×
2
=
4
\\frac{\\partial n}{\\partial m_0} = w_0 \\frac{\\partial n_0}{\\partial m_0} + w_1 \\frac{\\partial n_1}{\\partial m_0} = 2 m_0 + 0 = 2 \\times 2 = 4
∂m0∂n=w0∂m0∂n0+w1∂m0∂n1=2m0+0=2×2=4
∂
n
∂
m
1
=
w
0
∂
n
0
∂
m
1
+
w
1
∂
n
1
∂
m
1
=
0
+
3
m
1
2
=
3
×
3
2
=
27
\\frac{\\partial n}{\\partial m_1} = w_0 \\frac{\\partial n_0}{\\partial m_1} + w_1 \\frac{\\partial n_1}{\\partial m_1} = 0 + 3 m_1^2 = 3 \\times 3^2 = 27
∂m1以上是关于Pytorch Note6 自动求导Autograd的主要内容,如果未能解决你的问题,请参考以下文章
[Pytorch系列-20]:Pytorch基础 - Varialbe变量的手工求导和自动链式求导
Pytorch中的自动求导函数backward()所需参数含义