一般的标量导数表示切线的斜率
将导数拓展到不可微的函数。在函数的不可导点,将导数(斜率)取为一个范围内的任意值。
如,对于函数:
y = ∣ x ∣ y=|x| y=∣x∣
其导数可以记为:
∂ ∣ x ∣ ∂ x = { 1 i f x > 0 − 1 i f x < 0 a i f x = 0 , a ∈ [ − 1 , 1 ] \frac{\partial |x|}{\partial x}= \begin{cases} 1 & if\ x>0 \\ -1 & if\ x<0 \\ a & if\ x=0,a\in [-1,1] \end{cases} ∂x∂∣x∣=⎩⎪⎨⎪⎧1−1aif x>0if x<0if x=0,a∈[−1,1]
函数 max ( x , 0 ) \max{(x,0)} max(x,0) 的导数可以记为:
∂ max ( x , 0 ) ∂ x = { 1 i f x > 0 0 i f x < 0 a i f x = 0 , a ∈ [ − 1 , 1 ] \frac{\partial \max{(x,0)}}{\partial x}= \begin{cases} 1 & if\ x>0 \\ 0 & if\ x<0 \\ a & if\ x=0,a\in [-1,1] \end{cases} ∂x∂max(x,0)=⎩⎪⎨⎪⎧10aif x>0if x<0if x=0,a∈[−1,1]
导数在向量的拓展。
\quad | 标量 x x x | 向量 x \boldsymbol{x} x |
---|---|---|
标量 y y y | ∂ y ∂ x ( 标 量 ) \frac{\partial y}{\partial x}(标量) ∂x∂y(标量) | ∂ y ∂ x ( 行 向 量 ) \frac{\partial y}{\partial \boldsymbol{x}}(行向量) ∂x∂y(行向量) |
向量 y \boldsymbol{y} y | ∂ y ∂ x ( 列 向 量 ) \frac{\partial \boldsymbol{y}}{\partial x}(列向量) ∂x∂y(列向量) | ∂ y ∂ x ( 矩 阵 ) \frac{\partial \boldsymbol{y}}{\partial \boldsymbol{x}}(矩阵) ∂x∂y(矩阵) |
y y y 是标量, x = [ x 1 x 2 ⋮ x n ] \boldsymbol{x}=\left[\begin{matrix} x_1 \\ x_2 \\\vdots \\x_n \end{matrix}\right] x=⎣⎢⎢⎢⎡x1x2⋮xn⎦⎥⎥⎥⎤ 是向量,梯度按照如下方法计算,标量相对于一个列向量的导数是一个行向量(分子布局法),所以下面的计算都要转置。
∂ y ∂ x = [ ∂ y ∂ x 1 , ∂ y ∂ x 2 , ⋯ , ∂ y ∂ x n ] \frac{\partial y}{\partial \boldsymbol{x}} = \left[\begin{matrix} \dfrac{\partial y}{\partial x_1}, & \dfrac{\partial y}{\partial x_2}, & \cdots, & \dfrac{\partial y}{\partial x_n} \end{matrix}\right] ∂x∂y=[∂x1∂y,∂x2∂y,⋯,∂xn∂y]
如,对于 y = x 1 2 + 2 x 2 2 y=x_1^2 + 2x_2^2 y=x12+2x22,梯度 ∂ y ∂ x = [ 2 x 1 , 4 x 2 ] \frac{\partial y}{\partial{\boldsymbol{x}}}=\left[\begin{matrix}2x_1,4x_2\end{matrix}\right] ∂x∂y=[2x1,4x2]。
一些梯度的计算:
y | s u m ( x ) sum(\boldsymbol{x}) sum(x) | ∥ x ∥ 2 \|x\|^2 ∥x∥2 | ⟨ u , v ⟩ \langle \boldsymbol{u},\boldsymbol{v}\rangle ⟨u,v⟩ |
---|---|---|---|
∂ y ∂ x \dfrac{\partial y}{\partial{\boldsymbol{x}}} ∂x∂y | 1 T \boldsymbol{1}^T 1T | 2 x T 2\boldsymbol{x}^T 2xT | u T ∂ v ∂ x + v T ∂ u ∂ x \boldsymbol{u}^T\frac{\partial \boldsymbol{v}}{\partial \boldsymbol{x}}+\boldsymbol{v}^T\frac{\partial \boldsymbol{u}}{\partial \boldsymbol{x}} uT∂x∂v+vT∂x∂u |
y = [ y 1 y 2 ⋮ y m ] \boldsymbol{y}=\left[\begin{matrix} y_1 \\ y_2 \\\vdots \\y_m \end{matrix}\right] y=⎣⎢⎢⎢⎡y1y2⋮ym⎦⎥⎥⎥⎤ 是矩阵, x x x 是一个标量,计算梯度的方式如下,得到的是一个列向量(分子布局法)。
∂ y ∂ x = [ ∂ y 1 ∂ x ∂ y 2 ∂ x ⋮ ∂ y m ∂ x ] \frac{\partial \boldsymbol{y}}{\partial x}= \left[\begin{matrix} \dfrac{\partial y_1}{\partial x} \\ \dfrac{\partial y_2}{\partial x} \\ \vdots \\ \dfrac{\partial y_m}{\partial x} \end{matrix}\right] ∂x∂y=⎣⎢⎢⎢⎢⎢⎢⎢⎡∂x∂y1∂x∂y2⋮∂x∂ym⎦⎥⎥⎥⎥⎥⎥⎥⎤
对于向量 x = [ x 1 x 2 ⋮ x n ] \boldsymbol{x}=\left[\begin{matrix} x_1 \\ x_2 \\\vdots \\x_n \end{matrix}\right] x=⎣⎢⎢⎢⎡x1x2⋮xn⎦⎥⎥⎥⎤ 与向量 y = [ y 1 y 2 ⋮ y m ] \boldsymbol{y}=\left[\begin{matrix} y_1 \\ y_2 \\\vdots \\y_m \end{matrix}\right] y=⎣⎢⎢⎢⎡y1y2⋮ym⎦⎥⎥⎥⎤,其求梯度的方式如下,最终得到一个矩阵。
∂ y ∂ x = [ ∂ y 1 ∂ x ∂ y 2 ∂ x ⋮ ∂ y m ∂ x ] = [ ∂ y 1 ∂ x 1 , ∂ y 1 ∂ x 2 , ⋯ , ∂ y 1 ∂ x n ∂ y 2 ∂ x 1 , ∂ y 2 ∂ x 2 , ⋯ , ∂ y 2 ∂ x n ⋮ ∂ y m ∂ x 1 , ∂ y m ∂ x 2 , ⋯ , ∂ y m ∂ x n ] \frac{\partial \boldsymbol{y}}{\partial \boldsymbol{x}}= \left[\begin{matrix} \dfrac{\partial y_1}{\partial \boldsymbol{x}} \\ \dfrac{\partial y_2}{\partial \boldsymbol{x}} \\ \vdots \\ \dfrac{\partial y_m}{\partial \boldsymbol{x}} \end{matrix}\right]= \left[\begin{matrix} \dfrac{\partial y_1}{\partial x_1}, & \dfrac{\partial y_1}{\partial x_2}, & \cdots, & \dfrac{\partial y_1}{\partial x_n} \\ \dfrac{\partial y_2}{\partial x_1}, & \dfrac{\partial y_2}{\partial x_2}, & \cdots, & \dfrac{\partial y_2}{\partial x_n} \\ &\vdots && \\ \dfrac{\partial y_m}{\partial x_1}, & \dfrac{\partial y_m}{\partial x_2}, & \cdots, & \dfrac{\partial y_m}{\partial x_n} \end{matrix}\right] ∂x∂y=⎣⎢⎢⎢⎢⎢⎢⎢⎡∂x∂y1∂x∂y2⋮∂x∂ym⎦⎥⎥⎥⎥⎥⎥⎥⎤=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡∂x1∂y1,∂x1∂y2,∂x1∂ym,∂x2∂y1,∂x2∂y2,⋮∂x2∂ym,⋯,⋯,⋯,∂xn∂y1∂xn∂y2∂xn∂ym⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤
y \boldsymbol{y} y | x \boldsymbol{x} x | A x \boldsymbol{Ax} Ax | x T A \boldsymbol{x}^T\boldsymbol{A} xTA | a u a\boldsymbol{u} au | A u \boldsymbol{Au} Au |
---|---|---|---|---|---|
∂ y ∂ x \dfrac{\partial \boldsymbol{y}}{\partial{\boldsymbol{x}}} ∂x∂y | I \boldsymbol{I} I | A \boldsymbol{A} A | A T \boldsymbol{A}^T AT | a ∂ u ∂ x a\dfrac{\partial \boldsymbol{u}}{\partial \boldsymbol{x}} a∂x∂u | A ∂ u ∂ x \boldsymbol{A}\dfrac{\partial \boldsymbol{u}}{\partial \boldsymbol{x}} A∂x∂u |
相关链接:
标量的链式法则:
若 y = f ( u ) , u = g ( x ) y=f(u),u=g(x) y=f(u),u=g(x),则 y y y 相对于 x x x 的导数可以表示为:
∂ y ∂ x = ∂ y ∂ u ∂ u ∂ x \frac{\partial y}{\partial x}=\frac{\partial y}{\partial u}\frac{\partial u}{\partial x} ∂x∂y=∂u∂y∂x∂u
向量的链式求导法则:
类型 | 求导 |
---|---|
y y y 是标量, u u u 是标量, x \boldsymbol{x} x 是向量 | ∂ y ∂ x ( 1 , n ) = ∂ y ∂ u ( 1 , ) ∂ u ∂ x ( 1 , n ) \underset{(1,n)}{\dfrac{\partial y}{\partial \boldsymbol{x}}}= \underset{(1,)}{\dfrac{\partial y}{\partial u}} \underset{(1,n)}{\dfrac{\partial u}{\partial \boldsymbol{x}}} (1,n)∂x∂y=(1,)∂u∂y(1,n)∂x∂u |
y y y 是标量, u \boldsymbol{u} u 是向量, x \boldsymbol{x} x 是向量 | ∂ y ∂ x ( 1 , n ) = ∂ y ∂ u ( 1 , k ) ∂ u ∂ x ( k , n ) \underset{(1,n)}{\dfrac{\partial y}{\partial \boldsymbol{x}}}= \underset{(1,k)}{\dfrac{\partial y}{\partial \boldsymbol{u}}} \underset{(k,n)}{\dfrac{\partial \boldsymbol{u}}{\partial \boldsymbol{x}}} (1,n)∂x∂y=(1,k)∂u∂y(k,n)∂x∂u |
y \boldsymbol{y} y 是向量, u \boldsymbol{u} u 是向量, x \boldsymbol{x} x 是向量 | ∂ y ∂ x ( m , n ) = ∂ y ∂ u ( 1 , k ) ∂ u ∂ x ( k , n ) \underset{(m,n)}{\dfrac{\partial \boldsymbol{y}}{\partial \boldsymbol{x}}}= \underset{(1,k)}{\dfrac{\partial \boldsymbol{y}}{\partial \boldsymbol{u}}} \underset{(k,n)}{\dfrac{\partial \boldsymbol{u}}{\partial \boldsymbol{x}}} (m,n)∂x∂y=(1,k)∂u∂y(k,n)∂x∂u |
例子:
对于一个表达式 z = ( ⟨ x , w ⟩ − y ) 2 z=(\langle \boldsymbol{x},\boldsymbol{w}\rangle - y)^2 z=(⟨x,w⟩−y)2,其中 x , w ∈ R n , y ∈ R \boldsymbol{x},\boldsymbol{w}\in \mathbb{R}^n,y\in\mathbb{R} x,w∈Rn,y∈R,计算 ∂ z ∂ w \dfrac{\partial z}{\partial \boldsymbol{w}} ∂w∂z 的过程如下:
首先将其中的变量求导,记 a = ⟨ x , w ⟩ , b = a − y , z = b 2 a=\langle \boldsymbol{x},\boldsymbol{w}\rangle,\quad b=a-y,\quad z=b^2 a=⟨x,w⟩,b=a−y,z=b2
使用链式求导对每个中间变量求导
∂ z ∂ w = ∂ z ∂ b ∂ b ∂ a ∂ a ∂ w = ∂ b 2 ∂ b ∂ ( a − y ) ∂ a ∂ ⟨ x , w ⟩ ∂ w = 2 b ⋅ 1 ⋅ x T = 2 ( ⟨ x , w ⟩ − y ) x T \begin{aligned} \dfrac{\partial z}{\partial \boldsymbol{w}} & = \dfrac{\partial z}{\partial b}\dfrac{\partial b}{\partial a}\dfrac{\partial a}{\partial \boldsymbol{w}} \\ & = \dfrac{\partial b^2}{\partial b}\dfrac{\partial (a-y)}{\partial a}\dfrac{\partial \langle \boldsymbol{x},\boldsymbol{w}\rangle}{\partial \boldsymbol{w}} \\ & = 2b \cdot 1 \cdot \boldsymbol{x}^T \\ & = 2(\langle \boldsymbol{x},\boldsymbol{w}\rangle -y)\boldsymbol{x}^T \end{aligned} ∂w∂z=∂b∂z∂a∂b∂w∂a=∂b∂b2∂a∂(a−y)∂w∂⟨x,w⟩=2b⋅1⋅xT=2(⟨x,w⟩−y)xT
自动求导是计算一个函数在指定值上的导数,即对于一个 y = f ( x ) y=f(x) y=f(x) 的表达式,对于某一个 x = c x=c x=c, f ′ ( c ) f'(c) f′(c) 的值是什么。
自动求导是根据计算图进行详细计算的,也就是给定一个表达式:
如,对于上面例子的表达式, z = ( ⟨ x , w ⟩ − y ) 2 z=(\langle \boldsymbol{x},\boldsymbol{w}\rangle - y)^2 z=(⟨x,w⟩−y)2, a , b , z a,b,z a,b,z 就算单个的计算子,它们可以构成如下的计算图,其中每个圈表示一个表达式或输入。
构造出计算图后,根据链式求导法则:
∂ y ∂ x = ∂ y ∂ u n ∂ u n ∂ u n − 1 ⋯ ∂ u 2 ∂ u 1 ∂ u 1 ∂ x \dfrac{\partial y}{\partial x}=\dfrac{\partial y}{\partial u_n}\dfrac{\partial u_n}{\partial u_{n-1}}\cdots\dfrac{\partial u_2}{\partial u_{1}}\dfrac{\partial u_1}{\partial x} ∂x∂y=∂un∂y∂un−1∂un⋯∂u1∂u2∂x∂u1
可以通过如下两种方式实现自动求导:
正向累积。先计算 ∂ u 1 ∂ x \dfrac{\partial u_1}{\partial x} ∂x∂u1 ,再计算 ∂ u 2 ∂ u 1 \dfrac{\partial u_2}{\partial u_1} ∂u1∂u2,之后依次向前计算
∂ y ∂ x = ∂ y ∂ u n ( ∂ u n ∂ u n − 1 ( ⋯ ( ∂ u 2 ∂ u 1 ∂ u 1 ∂ x ) ) ) \dfrac{\partial y}{\partial x}= \dfrac{\partial y}{\partial u_n} \left(\dfrac{\partial u_n}{\partial u_{n-1}} \left(\cdots \left(\dfrac{\partial u_2}{\partial u_{1}} \dfrac{\partial u_1}{\partial x} \right)\right)\right) ∂x∂y=∂un∂y(∂un−1∂un(⋯(∂u1∂u2∂x∂u1)))
正向累计可以根据计算图,从下向上依次计算每个结点相对于下一个参数的导数。
反向累积(反向传播)。先计算 ∂ y ∂ u n \dfrac{\partial y}{\partial u_n} ∂un∂y ,再计算 ∂ u n ∂ u n − 1 \dfrac{\partial u_n}{\partial u_{n-1}} ∂un−1∂un,之后依次向后计算。
∂ y ∂ x = ( ( ( ∂ y ∂ u n ∂ u n ∂ u n − 1 ) ⋯ ) ∂ u 2 ∂ u 1 ) ∂ u 1 ∂ x \dfrac{\partial y}{\partial x}=\left(\left( \left(\dfrac{\partial y}{\partial u_n} \dfrac{\partial u_n}{\partial u_{n-1}}\right) \cdots\right) \dfrac{\partial u_2}{\partial u_{1}}\right) \dfrac{\partial u_1}{\partial x} ∂x∂y=(((∂un∂y∂un−1∂un)⋯)∂u1∂u2)∂x∂u1
反向累积需要根据计算图从上向下依次求导,求导过程中需要将正向计算中的结果拿来构成每个结点的求导结果并保存为中间结果。如下所示:
若要计算 y = 2 x T x y=2\mathbf{x}^T\mathbf{x} y=2xTx 关于行向量 x \mathbf{x} x 的导数,可以通过如下步骤。
构造 x \mathbf{x} x 向量,并指定在 x.grad
中保存计算出来的梯度
# 1. 先创建 x ,再指定 x 可以求导
>>> x = torch.arange(4.0)
>>> x
tensor([0., 1., 2., 3.])
>>> x.requires_grad_(True)
tensor([0., 1., 2., 3.], requires_grad=True)
>>> x.grad #默认为 None
# 2. 在创建 x 同时指定 x 可以求导
>>> x = torch.arange(4.0, requires_grad=True)
>>> x
tensor([0., 1., 2., 3.], requires_grad=True)
>>> x.grad #默认为 None
利用 x \mathbf{x} x 计算 y y y
>>> y = 2 * torch.dot(x,x) #计算内积
>>> y
tensor(28., grad_fn=<MulBackward0>) #grad_fn 保存 y 的运算信息,表明 y 是由 x 构建的
>>> y.grad_fn
<MulBackward0 object at 0x00000211287EEFD0>
调用反向传播函数 y.backward()
自动计算 y y y 关于 x \mathbf{x} x 每个分量的梯度
>>> y.backward()
>>> x.grad
tensor([ 0., 4., 8., 12.])
>>> x.grad == 4 * x
tensor([True, True, True, True])
其计算的本质就是构造出 y = f ( x ) y=f(x) y=f(x) 的表达式,上式为 y = 2 ( x 1 2 + x 2 2 + x 3 2 + x 4 2 ) y=2(x_1^2 + x_2^2 + x_3^2 + x_4^2) y=2(x12+x22+x32+x42) ,之后将向量 x \boldsymbol{x} x 中的每个值都带入计算,得到 x = [ x 1 , x 2 , ⋯ , x n ] \boldsymbol{x}=\left[\begin{matrix}x_1,x_2,\cdots,x_n\end{matrix}\right] x=[x1,x2,⋯,xn] 中每个位置的变量对应的求导公式 y ′ = f ′ ( x ) = [ f ′ ( x 1 ) , f ′ ( x 2 ) , ⋯ , f ′ ( x n ) ] = [ 4 x 1 2 , 4 x 2 2 , 4 x 3 2 , 4 x 4 2 ] y'=f'(\boldsymbol{x})=\left[\begin{matrix}f'(x_1),f'(x_2),\cdots,f'(x_n)\end{matrix}\right]=\left[\begin{matrix} 4x_1^2,4x_2^2,4x_3^2,4x_4^2\end{matrix}\right] y′=f′(x)=[f′(x1),f′(x2),⋯,f′(xn)]=[4x12,4x22,4x32,4x42],再将值带入对应位置。
下面展示了计算 x x x 的累加和的梯度
在默认情况下,PyTorch 会 累加梯度,使用 x.grad.zero_()
可以清除之前的值
>>> x.grad.zero_()
tensor([0., 0., 0., 0.])
>>> x.grad
tensor([0., 0., 0., 0.])
>>> y = x.sum()
>>> y.backward()
>>> x.grad
tensor([1., 1., 1., 1.])
使用 y.backward()
计算梯度时必须保证 y
是一个标量,在数学上是不允许对非标量求导的,如下所示,一般情况下我们首先对 y
求和,求和之后是一个标量,再进行求导。
>>> x.grad.zero_()
>>> y = x *x
>>> y.backward()
Traceback (most recent call last):
File "" , line 1, in <module>
File "D:\Applications\Anaconda\lib\site-packages\torch\tensor.py", line 221, in backward
torch.autograd.backward(self, gradient, retain_graph, create_graph)
File "D:\Applications\Anaconda\lib\site-packages\torch\autograd\__init__.py", line 126, in backward
grad_tensors_ = _make_grads(tensors, grad_tensors_)
File "D:\Applications\Anaconda\lib\site-packages\torch\autograd\__init__.py", line 50, in _make_grads
raise RuntimeError("grad can be implicitly created only for scalar outputs")
RuntimeError: grad can be implicitly created only for scalar outputs
>>> y.sum().backward()
>>> x.grad
tensor([0., 2., 4., 6.])
将某些计算移动到记录的计算图之外,即通过 y.detach()
命令使得中间变量 y y y 成为一个常数,对于包含 y y y 的表达式,当计算梯度时候会将 y y y 视为常数
#1. 对 y 执行 detach()
>>> x.grad.zero_()
tensor([0., 0., 0., 0.])
>>> y = x * x
>>> u = y.detach()
>>> z = u * x
>>> z.sum().backward()
>>> x.grad == u
tensor([True, True, True, True])
>>> x.grad
tensor([0., 1., 4., 9.])
#2. 不对 y 执行 detach()
>>> x.grad.zero_()
tensor([0., 0., 0., 0.])
>>> y = x * x
>>> z = y * x
>>> z.sum().backward()
>>> x.grad
tensor([ 0., 3., 12., 27.])
#3. 因为 y 是 x 的函数,所以直接对 y 进行梯度求解没有影响,但是不能对 u 求导
>>> x.grad.zero_()
tensor([0., 0., 0., 0.])
>>> y.sum().backward()
>>> x.grad
tensor([0., 2., 4., 6.])
>>> u.sum().backward()
RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn