自动求导是 PyTorch 中非常重要的特性,能够让我们避免手动去计算非常复杂的导数,这能够极大地减少了我们构建模型的时间,这也是其前身 Torch 这个框架所不具备的特性,下面我们通过例子看看 PyTorch 自动求导的独特魅力以及探究自动求导的更多用法。

import torch
from torch.autograd import Variable
# 简单情况的自动求导
# 下面我们显示一些简单情况的自动求导,"简单"体现在计算的结果都是标量,也就是一个数,我们对这个标量进行自动求导。
x = Variable(torch.Tensor([2]), requires_grad=True)
y = x + 2
z = y ** 2 + 3
print(z)
# 使用自动求导
z.backward()
print(x.grad)


tensor([ 19.]) tensor([ 8.]) 通过上面的一些列操作,我们从 x 得到了最后的结果out,我们可以将其表示为数学公式 z=(x+2)2+3z=(x+2)2+3 那么我们从 z 对 x 求导的结果就是 ∂z/∂x=2(x+2)=2(2+2)=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)
tensor(1.00000e-02 *
        [[-3.9407,  3.5175, -1.6757,  7.0566,  4.7556,  4.1101,  3.7159,
          -1.5029, -0.9861, -2.6751,  5.5320,  0.8253, -1.1554,  4.2152,
          -7.8965,  1.3745,  7.6813, -1.8536,  0.2535, -4.9601],
         [-3.9407,  3.5175, -1.6757,  7.0566,  4.7556,  4.1101,  3.7159,
          -1.5029, -0.9861, -2.6751,  5.5320,  0.8253, -1.1554,  4.2152,
          -7.8965,  1.3745,  7.6813, -1.8536,  0.2535, -4.9601],
         [-3.9407,  3.5175, -1.6757,  7.0566,  4.7556,  4.1101,  3.7159,
          -1.5029, -0.9861, -2.6751,  5.5320,  0.8253, -1.1554,  4.2152,
          -7.8965,  1.3745,  7.6813, -1.8536,  0.2535, -4.9601],
         [-3.9407,  3.5175, -1.6757,  7.0566,  4.7556,  4.1101,  3.7159,
          -1.5029, -0.9861, -2.6751,  5.5320,  0.8253, -1.1554,  4.2152,
          -7.8965,  1.3745,  7.6813, -1.8536,  0.2535, -4.9601],
         [-3.9407,  3.5175, -1.6757,  7.0566,  4.7556,  4.1101,  3.7159,
          -1.5029, -0.9861, -2.6751,  5.5320,  0.8253, -1.1554,  4.2152,
          -7.8965,  1.3745,  7.6813, -1.8536,  0.2535, -4.9601],
         [-3.9407,  3.5175, -1.6757,  7.0566,  4.7556,  4.1101,  3.7159,
          -1.5029, -0.9861, -2.6751,  5.5320,  0.8253, -1.1554,  4.2152,
          -7.8965,  1.3745,  7.6813, -1.8536,  0.2535, -4.9601],
         [-3.9407,  3.5175, -1.6757,  7.0566,  4.7556,  4.1101,  3.7159,
          -1.5029, -0.9861, -2.6751,  5.5320,  0.8253, -1.1554,  4.2152,
          -7.8965,  1.3745,  7.6813, -1.8536,  0.2535, -4.9601],
         [-3.9407,  3.5175, -1.6757,  7.0566,  4.7556,  4.1101,  3.7159,
          -1.5029, -0.9861, -2.6751,  5.5320,  0.8253, -1.1554,  4.2152,
          -7.8965,  1.3745,  7.6813, -1.8536,  0.2535, -4.9601],
         [-3.9407,  3.5175, -1.6757,  7.0566,  4.7556,  4.1101,  3.7159,
          -1.5029, -0.9861, -2.6751,  5.5320,  0.8253, -1.1554,  4.2152,
          -7.8965,  1.3745,  7.6813, -1.8536,  0.2535, -4.9601],
         [-3.9407,  3.5175, -1.6757,  7.0566,  4.7556,  4.1101,  3.7159,
          -1.5029, -0.9861, -2.6751,  5.5320,  0.8253, -1.1554,  4.2152,
          -7.8965,  1.3745,  7.6813, -1.8536,  0.2535, -4.9601]])
 tensor(1.00000e-02 *
        [[ 2.0000,  2.0000,  2.0000,  2.0000,  2.0000],
         [ 2.0000,  2.0000,  2.0000,  2.0000,  2.0000],
         [ 2.0000,  2.0000,  2.0000,  2.0000,  2.0000],
         [ 2.0000,  2.0000,  2.0000,  2.0000,  2.0000],
         [ 2.0000,  2.0000,  2.0000,  2.0000,  2.0000],
         [ 2.0000,  2.0000,  2.0000,  2.0000,  2.0000],
         [ 2.0000,  2.0000,  2.0000,  2.0000,  2.0000],
         [ 2.0000,  2.0000,  2.0000,  2.0000,  2.0000],
         [ 2.0000,  2.0000,  2.0000,  2.0000,  2.0000],
         [ 2.0000,  2.0000,  2.0000,  2.0000,  2.0000]])
 tensor([[ 0.1169,  0.1169,  0.1169,  0.1169,  0.1169],
         [ 0.0960,  0.0960,  0.0960,  0.0960,  0.0960],
         [-0.0079, -0.0079, -0.0079, -0.0079, -0.0079],
         [-0.0947, -0.0947, -0.0947, -0.0947, -0.0947],
         [-0.0520, -0.0520, -0.0520, -0.0520, -0.0520],
         [ 0.0512,  0.0512,  0.0512,  0.0512,  0.0512],
         [-0.0107, -0.0107, -0.0107, -0.0107, -0.0107],
         [ 0.0929,  0.0929,  0.0929,  0.0929,  0.0929],
         [ 0.1115,  0.1115,  0.1115,  0.1115,  0.1115],
         [-0.0069, -0.0069, -0.0069, -0.0069, -0.0069],
         [ 0.0536,  0.0536,  0.0536,  0.0536,  0.0536],
         [ 0.0340,  0.0340,  0.0340,  0.0340,  0.0340],
         [-0.0295, -0.0295, -0.0295, -0.0295, -0.0295],
         [ 0.0401,  0.0401,  0.0401,  0.0401,  0.0401],
         [ 0.0659,  0.0659,  0.0659,  0.0659,  0.0659],
         [ 0.0636,  0.0636,  0.0636,  0.0636,  0.0636],
         [ 0.0617,  0.0617,  0.0617,  0.0617,  0.0617],
         [-0.0532, -0.0532, -0.0532, -0.0532, -0.0532],
         [ 0.1524,  0.1524,  0.1524,  0.1524,  0.1524],
         [ 0.0155,  0.0155,  0.0155,  0.0155,  0.0155]])
# 复杂情况的自动求导
# 上面我们展示了简单情况下的自动求导,都是对标量进行自动求导.
# 下面我们会介绍对多维数组的自动求导机制。
m = Variable(torch.FloatTensor([[2, 3]]), requires_grad=True) # 构建一个 1 x 2 的矩阵
n = Variable(torch.zeros(1, 2)) # 构建一个相同大小的 0 矩阵
print(m)
print(n)
# 通过 m 中的值计算新的 n 中的值
n[0, 0] = m[0, 0] ** 2
n[0, 1] = m[0, 1] ** 3
print(n)
n.backward(torch.ones_like(n)) # 将 (w0, w1) 取成 (1, 1)
print(m.grad)
tensor([[ 2.,  3.]])
 tensor([[ 0.,  0.]])
 tensor(2.)
 tensor([[  4.,  27.]])
 tensor([[  4.,  27.]])

将上面的式子写成数学公式,可以得到

pytorch 自动学习率 pytorch自动求导机制_pytorch 自动学习率

下面我们直接对 n 进行反向传播,也就是求 n 对 m 的导数。

这时我们需要明确这个导数的定义,即如何定义

pytorch 自动学习率 pytorch自动求导机制_标量_02

在 PyTorch 中,如果要调用自动求导,需要往backward()中传入一个参数,这个参数的形状和 n 一样大,比如是 (w0, w1)(w0, w1),那么自动求导的结果就是:

pytorch 自动学习率 pytorch自动求导机制_标量_03

通过自动求导我们得到了梯度是 4 和 27,我们可以验算一下

pytorch 自动学习率 pytorch自动求导机制_python  _04

 

通过验算我们可以得到相同的结果

 

# 多次自动求导
# 通过调用 backward 我们可以进行一次自动求导,如果我们再调用一次 backward,会发现程序报错,没有办法再做一次。
# 这是因为 PyTorch 默认做完一次自动求导之后,计算图就被丢弃了,所以两次自动求导需要手动设置一个东西,我们通过下面的小例子来说明。
x = Variable(torch.FloatTensor([3]), requires_grad=True)
y = x * 2 + x ** 2 + 3
print(y)
y.backward(retain_graph=True) # 设置 retain_graph 为 True 来保留计算图
print(x.grad)
y.backward() # 再做一次自动求导,这次不保留计算图
print(x.grad)
tensor([ 18.])
 tensor([ 8.])
 tensor([ 16.])

可以发现 x 的梯度变成了 16,因为这里做了两次自动求导,所以讲第一次的梯度 8 和第二次的梯度 8 加起来得到了 16 的结果。

pytorch 自动学习率 pytorch自动求导机制_标量_05

x = Variable(torch.FloatTensor([2, 3]), requires_grad=True)
k = Variable(torch.zeros(2))
k[0] = x[0] ** 2 + 3 * x[1]
k[1] = x[1] ** 2 + 2 * x[0]
print(k)
j = torch.zeros(2, 2)
k.backward(torch.FloatTensor([1, 0]), retain_graph=True)
j[0] = x.grad.data
x.grad.data.zero_() # 归零之前求得的梯度
k.backward(torch.FloatTensor([0, 1]))
j[1] = x.grad.data
print(j)
tensor([ 13.,  13.])
 tensor([[ 4.,  3.],
         [ 2.,  6.]])