代码流程

首先运行以下代码:

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的得来如下:

\[out=\frac{1}{4} \sum_{i}z_{i},\\ z_{i}=3(x_{i}+2)^{2}\]

由此可得,$\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)的输出为:

\[tensor([[4.5000, 4.5000], [4.5000, 4.5000]])\]

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})$,即

\[v=\left(\frac{\partial l}{\partial y_{1}} \quad \cdots \quad \frac{\partial l}{\partial y_{m}}\right)^{T}\]

那么对于链式法则:

\[\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作为参数传入即可。