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 xz=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)} mn=(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} m0n=w0m0n0+w1m0n1
∂ 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} m1n=w0m1n0+w1m1n1

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 m0n=w0m0n0+w1m0n1=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学习Aurograd自动求导机制总结

Pytorch中的自动求导函数backward()所需参数含义

Pytorch使用pytorch进行张量计算自动求导和神经网络构建

『PyTorch』第三弹_自动求导

自动求导 动手学深度学习 pytorch