PyTorch 自动求导机制【附示例代码】

Pytorch 自动求导机制

文章目录

  • Pytorch 自动求导机制
    • 1. 简单的自动求导(输出是1维)
    • 2. 多维数组的自动求导(输出是多维)
    • 3. 多次自动求导

自动求导是 PyTorch 中非常重要的特性,能够让我们避免手动去计算非常复杂的导数,这能够极大地减少了我们构建模型的时间。

每个Tensor都有个标志:requires_grad,它都允许从梯度计算中精细地排除子图,并可以提高效率。

requires_grad=True 要求梯度

requires_grad=False 不要求梯度

什么是排除子图?
答: 排除没必要的梯度计算。

requires_grad

如果有一个单一的输入操作需要梯度,它的输出也需要梯度。相反,只有所有输入都不需要梯度,输出才不需要。如果其中所有的变量都不需要梯度进行,后向计算不会在子图中执行。

步骤:

  • 导入Variable并指定requires_grad=True,
  • y.backward()
  • x.grad

1. 简单的自动求导(输出是1维)

import torch
from torch.autograd import Variable

x = torch.Tensor([2])
# tensor 变成Variable
x = Variable(x,requires_grad=True)
y=x**3
y.backward()
print(x.grad)
tensor([12.])

例2: z = ( x + 2 ) 2 + 3 z = (x + 2)^2 + 3 z=(x+2)2+3 z z z x x x进行求导。

x = Variable(torch.Tensor([2]), requires_grad=True)
y = x + 2
z = y ** 2 + 3
print(z)
tensor([19.], grad_fn=)
# 使用自动求导
z.backward()
print(x.grad)
tensor([8.])

例3:更复杂的例子

x = Variable(torch.randn(5, 10), requires_grad=True)
y = Variable(torch.randn(5, 5), requires_grad=True)
w = Variable(torch.randn(10, 5), requires_grad=True)

out = torch.mean(y - torch.mm(x, w)) # torch.matmul 是做矩阵乘法或者的使用torch.mm()
print(out)
out.backward()
tensor(-0.8683, grad_fn=)
# 得到 x 的梯度
print(x.grad)
tensor([[ 0.0479,  0.0470,  0.0192,  0.1219,  0.2274, -0.1751,  0.0755,  0.0062,
         -0.1455,  0.0992],
        [ 0.0479,  0.0470,  0.0192,  0.1219,  0.2274, -0.1751,  0.0755,  0.0062,
         -0.1455,  0.0992],
        [ 0.0479,  0.0470,  0.0192,  0.1219,  0.2274, -0.1751,  0.0755,  0.0062,
         -0.1455,  0.0992],
        [ 0.0479,  0.0470,  0.0192,  0.1219,  0.2274, -0.1751,  0.0755,  0.0062,
         -0.1455,  0.0992],
        [ 0.0479,  0.0470,  0.0192,  0.1219,  0.2274, -0.1751,  0.0755,  0.0062,
         -0.1455,  0.0992]])
# 得到 y 的的梯度
print(y.grad)
tensor([[0.0400, 0.0400, 0.0400, 0.0400, 0.0400],
        [0.0400, 0.0400, 0.0400, 0.0400, 0.0400],
        [0.0400, 0.0400, 0.0400, 0.0400, 0.0400],
        [0.0400, 0.0400, 0.0400, 0.0400, 0.0400],
        [0.0400, 0.0400, 0.0400, 0.0400, 0.0400]])
# 得到 w 的梯度
print(w.grad)
tensor([[0.0219, 0.0219, 0.0219, 0.0219, 0.0219],
        [0.0160, 0.0160, 0.0160, 0.0160, 0.0160],
        [0.0847, 0.0847, 0.0847, 0.0847, 0.0847],
        [0.1473, 0.1473, 0.1473, 0.1473, 0.1473],
        [0.0931, 0.0931, 0.0931, 0.0931, 0.0931],
        [0.0924, 0.0924, 0.0924, 0.0924, 0.0924],
        [0.1073, 0.1073, 0.1073, 0.1073, 0.1073],
        [0.0393, 0.0393, 0.0393, 0.0393, 0.0393],
        [0.0226, 0.0226, 0.0226, 0.0226, 0.0226],
        [0.0419, 0.0419, 0.0419, 0.0419, 0.0419]])

2. 多维数组的自动求导(输出是多维)

例4:多维数组的自动求导机制

m = Variable(torch.FloatTensor([[2, 3]]), requires_grad=True) # 构建一个 1 x 2 的矩阵
n = Variable(torch.zeros(1, 2)) # 构建一个相同大小的 0 矩阵
print(m)
print(n)
tensor([[2., 3.]], requires_grad=True)
tensor([[0., 0.]])
# 通过 m 中的值计算新的 n 中的值
n[0, 0] = m[0, 0] ** 2
n[0, 1] = m[0, 1] ** 3
print(n)
tensor([[ 4., 27.]], grad_fn=)
import numpy as np
a =np.array([[1,2,3],[4,5,6]]) 

a[1,1]==a[1][1] #True
# 只有二维数组或矩阵,tensor才能这样取数
True

$ n = (n_0,\ n_1) = (m_02, m_13) = (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)
tensor([[ 4., 27.]])

关键:为什么要torch.ones_like(n)?【向量求导】
n不是一个标量,是一个向量,需要传入一个维数一样的1向量,使得梯度是每个分量的求梯度的和。

验证:
∂ 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 m1n=w0m1n0+w1m1n1=0+3m12=3×32=27

3. 多次自动求导

通过调用 backward 我们可以进行一次自动求导,如果我们再调用一次 backward,会发现程序报错,没有办法再做一次。这是因为 PyTorch 默认做完一次自动求导之后,计算图就被丢弃了,所以两次自动求导需要手动设置一个东西,我们通过下面的小例子来说明。

x = Variable(torch.FloatTensor([3]), requires_grad=True)
y = x * 2 + x ** 2 + 3
print(y)
tensor([18.], grad_fn=)

设置retain_graphTrue来保留计算图

y.backward(retain_graph=True) 
print(x.grad)
tensor([8.])
y.backward() # 再做一次自动求导,这次不保留计算图
print(x.grad)
y.backward() # 第三次做自动求导,这次不保留计算图
print(x.grad)# 报错
tensor([16.])

练习
x = [ x 0   x 1 ] = [ 2   3 ] x = \left[ \begin{matrix} x_0 \ x_1 \end{matrix} \right] = \left[ \begin{matrix} 2 \ 3 \end{matrix} \right] x=[x0 x1]=[2 3]

k = ( k 0 ,   k 1 ) = ( x 0 2 + 3 x 1 ,   2 x 0 + x 1 2 ) k = (k_0,\ k_1) = (x_0^2 + 3 x_1,\ 2 x_0 + x_1^2) k=(k0, k1)=(x02+3x1, 2x0+x12)

求:
j = [ ∂ k 0 ∂ x 0 ∂ k 0 ∂ x 1   ∂ k 1 ∂ x 0 ∂ k 1 ∂ x 1 ] j = \left[ \begin{matrix} \frac{\partial k_0}{\partial x_0} & \frac{\partial k_0}{\partial x_1} \ \frac{\partial k_1}{\partial x_0} & \frac{\partial k_1}{\partial x_1} \end{matrix} \right] j=[x0k0x1k0 x0k1x1k1]

x = Variable(torch.Tensor([2,3]),requires_grad=True)
k=Variable(torch.zeros(2))
k[0]=x[0]**2+3*x[1]
k[1]=2*x[0]+x[1]**2
print(k)
tensor([13., 13.], grad_fn=)
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([[4., 3.],
        [2., 6.]])
k.backward(torch.ones_like(k),retain_graph=True)
print(x.grad)

参考:自动求导机制

你可能感兴趣的:(PyTorch,python)