Pytorch中的导数与梯度
代码流程
首先运行以下代码:
import torch
该代码将Pytorch库导入,其次运行下面代码:
x = torch.ones(2, 2, requires_grad=True)
该代码表示我们需要生成一个全一矩阵,并且该矩阵的任何计算应当被记录,从而能够得到其导数。
注意:若想追踪一个矩阵的梯度,该矩阵不能为整数类型,因为整数不可以求导。
接下来运行以下代码:
y = x + 2
z = y * y * 3
out = z.mean()
该代码表示计算过程,.mean()函数表示将矩阵z的所有元素求和后做平均。
接下来运行反向传播函数:
out.backward()
只有运行了该函数后,我们才能通过代码print("grad of x: ", x.grad)显示出标量out关于矩阵x的梯度。
注意,
.backward()函数的调用者必须是一个标量,这样才能计算其梯度。
求导过程
首先,标量out的得来如下:
由此可得,$\frac{\partial out}{\partial z_{i}}=\frac{1}{4}$。而$\frac{\partial z_{i}}{\partial x_{i}}=6x+12$,故
\[\frac{\partial out}{\partial x_{i}} = \frac{\partial out}{\partial z_{i}} \times \frac{\partial z_{i}}{\partial x_{i}} = \frac{6x_{i}+12}{4} = 4.5\]因此,print(x.grad)的输出为:
Jacobian矩阵
数学上,如果有一个向量值函数$\vec{y}=f(\vec{x})$,那么向量$\vec{y}$对于向量$\vec{x}$的导数为一个矩阵,我们称其为Jacobian矩阵:
\[J=\left( \begin{array}{ccc}{\frac{\partial y_{1}}{\partial x_{1}}} & {\cdots} & {\frac{\partial y_{1}}{\partial x_{n}}} \\ {\vdots} & {\ddots} & {\vdots} \\ {\frac{\partial y_{m}}{\partial x_{1}}} & {\cdots} & {\frac{\partial y_{m}}{\partial x_{n}}}\end{array}\right)\]通俗来说,torch.autograd是一个用来计算Jacobian的矩阵,此时,若存在一个向量$v=\left( \begin{array}{llll}{v_{1}} & {v_{2}} & {\cdots} & {v_{m}}\end{array}\right)^{T}$,这个向量正好是一个标量函数$l=g(\vec{y})$,即
那么对于链式法则:
\[\left( \begin{array}{c}{\frac{\partial l}{\partial x_{1}}} \\ {\frac{\partial l}{\partial x_{n}}}\end{array}\right)=\left( \begin{array}{ccc}{\frac{\partial y_{1}}{\partial x_{1}}} & {\cdots} & {\frac{\partial y_{m}}{\partial x_{1}}} \\ {\vdots} & {\ddots} & {\vdots} \\ {\frac{\partial y_{1}}{\partial x_{n}}} & {\cdots} & {\frac{\partial y_{m}}{\partial x_{n}}}\end{array}\right) \left( \begin{array}{c}{\frac{\partial l}{\partial y_{1}}} \\ {\vdots} \\ {\frac{\partial l}{\partial y_{m}}}\end{array}\right)=J^{T} \cdot v\]Pytorch中的实现
x = torch.ones(3, requires_grad=True)
y = x * 2
v = torch.tensor([0.1, 1.0, 10], dtype=torch.float)
y.backward(v)
虽然y是也是一个向量,不能直接通过backward来进行求导,但是我们可以利用向量v计算另一个潜在的变量l对于变量x的导数,在编程中将v作为参数传入即可。