Pytorch自动求导机制

Pytorch自动求导机制对pytorch中的自动求导机制自学理解,自动求导机制是pytorch中的核心功能,其原理是利用的链式法则。

pytorch学习记录

一、自动求导机制

参考:Pytorch的自动求导机制与使用方法(一) – 知乎 (zhihu.com)

​ 自动求导机制是pytorch核心功能之一。

1.1 求导法则

​ 限制:pytorch中默认只能是标量标量/向量/矩阵求导。

​ 即只能对
f ( x ) = z , f : R n R f(x)=z, f:R^n\rightarrow R
求导,其中
x x
可以通过多次复合函数得到,对
f f
求导利用链式法则:


z x = z y y x \frac{\partial z}{\partial x}=\frac{\partial z}{\partial y}\frac{\partial y}{\partial x}

​ 其中
x x
为叶节点,
z z
为根节点,
y y
是过程操作,并不会被收集,只是作一个传播作用。

​ pytorch中的求导法则,实则是反向传播。

1.2 Tensor张量操作规则

​ PyTorch中数据以张量(n维数组)的形式流动torch.Tensor可以用来创建张量。

​ 当Tensor的属性中requires_grad=True时,则系统就可以开始跟踪对此Tensor的所有操作。其中记录的每个操作求得的梯度不会保存,只有在最后的一个操作才会保存梯度。

​ 如上述的两次链式法则,只保存
z x \frac{\partial z}{\partial x}
,而
z y \frac{\partial z}{\partial y}
只是被当作中间值,不会保存

​ 例:Tensor.backward()方法默认计算对计算图叶子节点的导数,中间过程的导数是不计算的

x = torch.tensor(3.0, requires_grad=True)
y = 2*x
z = y**2
f = z+2
f.backward()
print(x.grad)
print(y.grad)
print(z.grad)

​ 输出结果:

tensor(24.)
None
None

​ pytorch在求导时候会自动构建计算图。

preview

​ 从上图可以看出,一个Tensor中:

  • data中保存着所存有的数据
  • grad中保存梯度
  • requires_grad表示是否开始追踪所有的操作历史

​ 想要计算梯度的时候,需要调用Tensor.backward()。在调用backward()时,只有当requires_grad和is_leaf同时为真时,才会计算节点的梯度值。

​ 例:pytorch只能标量对其他进行求导。

input:
import torch
x = torch.ones(2,2,requires_grad=True)
y = x+2# y 为非标量
print(x.is_leaf, y.is_leaf)
print(y.requires_grad)
y.backward()
ouput:
<<<True False
<<<True
<<<RuntimeError: grad can be implicitly created only for scalar outputs
    
# y为非标量,所以不符合y.requires_grad=True,所以无法求导

z = y * y * 3
out = z.mean()
print(out)
print(out.requires_grad)
out.backward()
print(x.grad)

output:
<<<tensor(27., grad_fn=<MeanBackward0>)
<<<True
<<<tensor([[4.5000, 4.5000],
        [4.5000, 4.5000]])

​ 求导过程如下:

​ 令
o u t = o out=o
,由于


o = 1 4 z i = 1 4 3 ( x i ) + 2 o=\frac{1}{4}\sum z_i=\frac{1}{4}\sum 3(x_i)+2

​ 所以


o x i x i = 1 = 9 2 = 4.5 \frac{\partial o}{\partial x_i}|_{x_i=1}=\frac{9}{2}=4.5

​ 例:对
y = x 3 y=x^3
,对
x = 2 x=2
进行求导

import torch
import torch.autograd

x = torch.tensor([2,0],requires_grad=True)
print("x= ",x)
print("x.requires_grad= ", x.requires_grad)
y = x ** 3
print("y= ",y)
print("y.requires_grad = ", y.grad_fn)

y.backward() #反向传播,求解导数
print("x.grad = ", x.grad)

​ 输出结果为:

x =  tensor([2.], requires_grad=True)
x.requires_grad =  True
y =  tensor([8.], grad_fn=<PowBackward0>)
y.requires_grad =  <PowBackward0 object at 0x7f3a1dac6320>
x.grad =  tensor([12.])

1.3 autograd类原理

​ autograd类的原理其实是利用雅可比矩阵进行计算。

​ 设函数
f : R n R m f:R^n\rightarrow R^m
,其中
f = ( f 1 , f 2 , , f m ) f=(f_1,f_2,\dots,f_m)
,则雅克比矩阵为:


J = [ f x 1     f x n ] = [ f 1 x 1 f 1 x n f m x 1 f m x n ] J=\left[ \frac{\partial f}{\partial x_1}\ \cdots \ \frac{\partial f}{\partial x_n} \right] =\left[ \begin{matrix} \frac{\partial f_1}{\partial x_1}& \cdots& \frac{\partial f_1}{\partial x_n}\\ \vdots& \ddots& \vdots\\ \frac{\partial f_m}{\partial x_1}& \cdots& \frac{\partial f_m}{\partial x_n}\\ \end{matrix} \right]

​ 上面矩阵是
f f
关于
x = ( x 1 , , x m ) x=(x_1,\dots,x_m)
求导。

​ 令
l l
是一个标量函数,对
f f
进行求导有:


v = [ l f 1 , l f 2 , , l f m ] v=[\frac{\partial l}{\partial f_1},\frac{\partial l}{\partial f_2},\cdots,\frac{\partial l}{\partial f_m} ]

​ 则
l l

x x
进行求导有:


d l d x = J v T \frac{dl}{dx}=J*v^T

​ 其中对
x i x_i
求偏导有:


l x i = v J i \frac{\partial l}{\partial x_i}=v*J_i

​ 其中
J i J_i
表示
J J
矩阵的第
i i
列。

​ 可以看到,
l : R m R l:R^m\rightarrow R
,从神经网络的例子来理解,
f f
是隐藏层为
m m
个神经元的个数,
l l
为输出层。

​ 从损失函数理解:标量
l l
类似于MSE函数将minibatch平均为一个平均loss上.

1.4 具体例子

标量对向量求导:

​ 令
x = [ x 1 , x 2 , x 3 ] T x=[x_1,x_2,x_3]^T

w = [ w 1 , w 2 , w 3 ] T w=[w_1,w_2,w_3]^T

y = w x + b y=w*x+b

​ 则偏导数为:


y x = [ y x 1 , y x 2 , y x 3 ] = [ w 1 , w 2 , w 3 ] \frac{\partial y}{\partial x}=[\frac{\partial y}{\partial x_1},\frac{\partial y}{\partial x_2},\frac{\partial y}{\partial x_3}]=[w_1,w_2,w_3]
x = torch.tensor([1.0,2.0,3.0], requires_grad=True)
w = torch.tensor([4.0,5.0,6.0], requires_grad=True)
b = 10
y = torch.dot(x,w)+b
y.backward()
print(x.grad)
print(w.grad)

output:
<<<tensor([4., 5., 6.])
<<<tensor([1., 2., 3.])

标量对矩阵求导:

​ 令
X = [ x 11 x 12 x 13 x 21 x 22 x 23 ] X=\left[ \begin{matrix}{} ​ x_{11}& x_{12}& x_{13}\\ ​ x_{21}& x_{22}& x_{23}\\ \end{matrix} \right]

​ 第一次操作:


Y = X + 1 = [ x 11 + 1 x 12 + 1 x 13 + 1 x 21 + 1 x 22 + 1 x 23 + 1 ] Y=X+1=\left[ \begin{matrix}{} x_{11}+1& x_{12}+1& x_{13}+1\\ x_{21}+1& x_{22}+1& x_{23}+1\\ \end{matrix} \right]

​ 第二次操作:


Z = [ z 11 z 12 z 13 z 21 z 22 z 23 ] = [ ( y 11 ) 2 ( y 12 ) 2 ( y 13 ) 2 ( y 21 ) 2 ( y 22 ) 2 ( y 23 ) 2 ] Z=\left[ \begin{matrix}{} z_{11}& z_{12}& z_{13}\\ z_{21}& z_{22}& z_{23}\\ \end{matrix} \right] =\left[ \begin{matrix}{} \left( y_{11} \right) ^2& \left( y_{12} \right) ^2& \left( y_{13} \right) ^2\\ \left( y_{21} \right) ^2& \left( y_{22} \right) ^2& \left( y_{23} \right) ^2\\ \end{matrix} \right]

​ 第三次操作:


f = 1 6 s u m ( Z ) f=\frac{1}{6}sum(Z)

​ 偏导数为:


f x i j = 1 6 2 ( ( x i j + 1 ) 2 x i j ) = 1 3 ( x i j + 1 ) \frac{\partial f}{\partial x_{ij}}=\frac{1}{6}*2(\frac{\partial(x_{ij}+1)^2}{\partial x_{ij}})=\frac{1}{3}(x_{ij}+1)
import torch
import torch.autograd

x = torch.tensor([[1.0,2.0,3.0],[4.0,5.0,6.0]], requires_grad=True)
y = x+1
z = y**2
f = torch.mean(z)

f.backward()
print(x.grad)

ouput:
<<<tensor([[0.6667, 1.0000, 1.3333],
        [1.6667, 2.0000, 2.3333]])

向量/矩阵对向量/矩阵求导:

​ 在pytorch中一般标量对向量或矩阵用的比较多,因为深度学习中最后的loss是输出一个值为标量,因此向量对矩阵求导需要传入一个梯度,即传入一个
l l

f f
的求导得到的梯度。

​ 令
x = [ x 1 , x 2 , x 3 ] x=[x_1,x_2,x_3]

y = x 2 = [ 2 x 1 , 2 x 2 , 2 x 3 ] y=x*2=[2*x_1,2*x_2,2*x_3]
,此时对应上面1.3节有,
y = ( f 1 , f 2 , f 3 ) y=(f_1,f_2,f_3)
,其中
f i = 2 x i f_i=2*x_i
,所以有:


d y d x = [ f 1 x 1 f 1 x 2 f 1 x 3 f 2 x 1 f 2 x 2 f 2 x 3 f 3 x 1 f 3 x 2 f 3 x 3 ] = [ 2 0 0 0 2 0 0 0 2 ] \frac{dy}{dx}=\left[ \begin{matrix} \frac{\partial f_1}{\partial x_1}&\frac{\partial f_1}{\partial x_2} & \frac{\partial f_1}{\partial x_3}\\ \frac{\partial f_2}{\partial x_1} & \frac{\partial f_2}{\partial x_2} &\frac{\partial f_2}{\partial x_3} \\ \frac{\partial f_3}{\partial x_1}& \frac{\partial f_3}{\partial x_2} & \frac{\partial f_3}{\partial x_3} \\ \end{matrix} \right] =\left[ \begin{matrix} 2&0 & 0\\ 0 & 2 &0 \\ 0& 0 &2\\ \end{matrix} \right]

​ 传入:
v v

l l

f f
的梯度


v = [ 0.1 , 1.0 , 0.0001 ] v=[0.1,1.0,0.0001]

​ 例:

x = torch.randn(3, requires_grad=True)
y = x * 2
print(y)
v = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
y.backward(v)
print(x.grad)

output:
<<<tensor([-0.1656,  1.2321, -3.1254], grad_fn=<MulBackward0>)
<<<tensor([2.0000e-01, 2.0000e+00, 2.0000e-04])

注意此处获得的梯度是
l l

x x
的梯度,而
l l
是没有显式的,因为只是传入了一个
l l

f f
的梯度。

今天的文章Pytorch自动求导机制分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/16480.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注