梯度

将一个多元函数对其所有变量的偏导数连结一起,我们能够得到该函数的梯度(gradient)向量。

设函数f:RnRf:\mathbb{R}^n \rightarrow \mathbb{R} ,其输入为nn 维向量x=[x1,x2,...,xn]T\mathbf{x}=[x_1, x_2, ..., x_n]^T,输出是一个标量。

则函数y=f(x)y=f(\mathbf{x}) 对于x\mathbf{x} 的梯度,是一个包含nn 个偏导数的向量

yx=[yx1,yx2,,yxn]T\frac{\partial y}{\partial \mathbf{x}}=\left[\frac{\partial y}{\partial x_{1}}, \frac{\partial y}{\partial x_{2}}, \cdots, \frac{\partial y}{\partial x_{n}}\right]^T

标量对向量的求导

假设x\mathbf{x}nn 维向量,yyaa均为标量,则在多元微分函数常使用以下规则:

yyaaauausum(x)(\mathbf{x})x2|x|^2(十分常用!)
yx\frac{\partial y}{\partial \mathbf{x}}0T\mathbf{0}^Tayxa\frac{\partial y}{\partial \mathbf{x}}1T\mathbf{1}^T2xT2\mathbf{x}^T
yyu+vu+vuvuv<u,v><\mathbf{u},\mathbf{v}>
yx\frac{\partial y}{\partial \mathbf{x}}ux+vx\frac{\partial u}{\partial \mathbf{x}}+\frac{\partial v}{\partial \mathbf{x}}uxv+vxu\frac{\partial u}{\partial \mathbf{x}}v+\frac{\partial v}{\partial \mathbf{x}}uuTvx+vTux\mathbf{u}^T\frac{\partial \mathbf{v}}{\partial \mathbf{x}}+\mathbf{v}^T\frac{\partial \mathbf{u}}{\partial \mathbf{x}}

<u,v><\mathbf{u},\mathbf{v}> 的含义,即为两个向量u\mathbf{u}v\mathbf{v} 进行内积运算

向量对向量的求导

假设xxnn 维向量,yymm 维向量函数,则yx\frac{\partial \mathbf{y}}{\partial \mathbf{x}}m×nm\times n 的矩阵

其具体的表达式为:

yx=[y1xy0xymx]=[y1x1,y1x2,,y1xny2x1,y2x2,,y2xnymx1,ymx3,,ymxn]\frac{\partial \mathbf{y}}{\partial \mathbf{x}} =\left[\begin{array}{c} \frac{\partial y_{1}}{\partial \mathbf{x}} \\ \frac{\partial y_{0}}{\partial \mathbf{x}} \\ \vdots \\ \frac{\partial y_{m}}{\partial \mathbf{x}} \end{array}\right] =\left[\begin{array}{ccc} \frac{\partial y_{1}}{\partial x_{1}}, \frac{\partial y_{1}}{\partial x_{2}}, \cdots, \frac{\partial y_{1}}{\partial x_{n}} \\ \frac{\partial y_{2}}{\partial x_{1}}, \frac{\partial y_{2}}{\partial x_{2}}, \cdots, \frac{\partial y_{2}}{\partial x_{n}} \\ \vdots & \\ \frac{\partial y_{m}}{\partial x_{1}}, \frac{\partial y_{m}}{\partial x_{3}}, \cdots, \frac{\partial y_{m}}{\partial x_{n}}\end{array}\right]

下列表格中,aa 是与xx 无关的标量,a\mathbf{a} 是与x\mathbf{x} 无关的向量,A\mathbf{A} 是与xx 无关的矩阵,

y\mathbf{y}a\mathbf{a}x\mathbf{x}Ax\mathbf{Ax} (十分常用)xTA\mathbf{x^T} \mathbf{A} (十分常用!)
yx\frac{\partial \mathbf{y}}{\partial \mathbf{x}}0\mathbf{0} (全0矩阵)I\mathbf{I}(单位矩阵)A\mathbf{A}AT\mathbf{A}^T
y\mathbf{y}aua\mathbf{u}Au\mathbf{Au}u+v\mathbf{u}+\mathbf{v}
yx\frac{\partial \mathbf{y}}{\partial \mathbf{x}}auxa\frac{\partial \mathbf{u}}{\partial \mathbf{x}}Aux\mathbf{A}\frac{\partial \mathbf{u}}{\partial \mathbf{x}}ux+vx\frac{\partial \mathbf{u}}{\partial \mathbf{x}}+\frac{\partial \mathbf{v}}{\partial \mathbf{x}}

<x,y>y=xT\frac{\partial <\mathbf{x}, \mathbf{y}>}{\partial \mathbf{y}}=\partial{\mathbf{x}^T} 十分常用!

自动求导

PyTorch 通过自动计算导数,即自动求导(automatic differentiation)来加快求导,即计算一个函数在指定值上的导数,不同于符号求导、数值求导。

在实际中,根据设计的模型,系统会构建一个计算图(computational graph,将代码分解成一个个操作子,将计算表示成一个无环图),来跟踪计算是哪些数据通过哪些操作组合起来产生输出。自动求导使系统能够随后,反向传播梯度。

深度学习耗GPU资源的最大原因——由于自动求导的反向累积的内存复杂度为O(n)O(n)(需要存储正向累计的所有中间结果)

其中反向传播(backpropagation)意味着跟踪整个计算图,填充关于每个参数的偏导数,它分为两个主要过程:Forward Pass 以及 Backward Pass。

实例

假设y^=xω\hat{y}=x * \omegaloss=(y^y)2=(xωy)2loss = (\hat{y} - y)^2=(x \cdot \omega - y)^2

则 Forward Pass 如下过程:(计算各点的值,局部的梯度)

通过PyTorch实现:

1
2
3
w = torch.Tensor([1.0])
w.requires_grad = True # 默认的Tensor不计算梯度,需要指定其存放梯度
l = loss(x, y) # 前馈过程,但注意,此时 w.grad 仍为 None

当子节点要求计算梯度时(requires_gread = True),则父节点也会进行自动求导,求出相应的梯度(但并未存放)

Backward Pass 如下过程:(取出之前计算的中间结果,进行链式法则运算,得到损失函数关于其他变量的梯度)

1
2
l.backward() 	# 此时,便将得到loss关于w的梯度
w.grad # 注意,只有叶子节点才有.grad属性

只有当损失函数调用 backward(),梯度才会存放到被显式要求计算梯度的参数中,计算图也相应释放

在默认情况下,PyTorch 会累积梯度,因此在使用反向传播前需要清除之前的梯度:

1
w.grad.zero_()