前言
一个普通人尝试理解反向传播…从上至下阅读吧,看看有什么收获。
先说结论:反向传播用于快速计算神经网络中节点的梯度
导数(Derivative)
对函数$f: \mathbb{R} \rightarrow \mathbb{R}$,其输入和输出都是标量。 如果$f$的导数存在,这个极限被定义为:
如果$f’(a)$存在,则称$f$在$a$处是可微(differentiable)的。 如果$f$在一个区间的每一个点上都是可微的,那么该函数在此区间上可微。导数$f’(x)$可以解释为$f(x)$相对于$x$的瞬时(instantaneous)变化率,该变化率基于变化$h$,$h$趋近于$0$。
微分(Differential)
给定$y = f(x)$,其中$x$和$y$分别是函数$f$的自变量和因变量,则以下表达式等价:
其中符号$\frac{d}{dx}$和$D$是微分运算符。可以使用以下规则来对常见函数求微分:
- $DC = 0$($C$为常数)
- $Dx^n = nx^{n-1}$(n为任意实数)
- $De^x = e^x$
- $D\ln(x) = 1/x$
假设函数$f$和$g$可微,$C$为常数,则有如下法则:
1、 $\frac{d}{dx} [Cf(x)] = C \frac{d}{dx} f(x)$
2、 $\frac{d}{dx} [f(x) + g(x)] = \frac{d}{dx} f(x) + \frac{d}{dx} g(x)$
3、 $\frac{d}{dx} [f(x)g(x)] = f(x) \frac{d}{dx} [g(x)] + g(x) \frac{d}{dx} [f(x)]$
4、 $\frac{d}{dx} \left[\frac{f(x)}{g(x)}\right] = \frac{g(x) \frac{d}{dx} [f(x)] - f(x) \frac{d}{dx} [g(x)]}{[g(x)]^2}$
这些法则可以便于计算由常见函数组成的函数的微分,注意,复合函数无法通过这些法则微分。
偏导数(Partial derivative)
就是将微分推广到多元函数,这个应该懂得都懂。
设$y = f(x_1, x_2, \ldots, x_n)$为具有$n$个变量的函数,$y$关于$x_i$的偏导数为:
计算时,将$x_1, \ldots, x_{i-1}, x_{i+1}, \ldots, x_n$看作常数,直接计算$y$关于$x_i$的导数即可。
同时也有以下等价表示:
梯度(Gradient)
梯度向量是连结一个多元函数对其所有变量的偏导数后得到的。
设函数$f:\mathbb{R}^n\rightarrow\mathbb{R}$的输入是一个$n$维向量$\mathbf{x}=[x_1,x_2,\ldots,x_n]^\top$,且输出是一个标量。则函数$f(\mathbf{x})$相对于$x$的梯度为:
$\nabla_{\mathbf{x}} f(\mathbf{x})$在没有歧义的时候可以被$\nabla f(\mathbf{x})$替换。
继续假设$x$为$n$维向量,在微分多元函数时有如下法则(注意n和m的位置):
1、对所有$\mathbf{A} \in \mathbb{R}^{m \times n}$,有$\nabla_{\mathbf{x}} \mathbf{A} \mathbf{x} = \mathbf{A}^\top$ (用于转置矩阵)
2、 对所有$\mathbf{A} \in \mathbb{R}^{n \times m}$,有$\nabla_{\mathbf{x}} \mathbf{x}^\top \mathbf{A} = \mathbf{A}$ (抵消)
3、对所有$\mathbf{A} \in \mathbb{R}^{n \times n}$,有$\nabla_{\mathbf{x}} \mathbf{x}^\top \mathbf{A} \mathbf{x} = (\mathbf{A} + \mathbf{A}^\top)\mathbf{x}$ (方阵)
4、$\nabla_{\mathbf{x}} |\mathbf{x} |^2 = \nabla_{\mathbf{x}} \mathbf{x}^\top \mathbf{x} = 2\mathbf{x}$ (函数相对于向量$x$的梯度乘以向量$x$范数的平方等于$2x$)
接第四条,对于任何矩阵$\mathbf{X}$,都有$\nabla_{\mathbf{X}} |\mathbf{X} |_F^2 = 2\mathbf{X}$。
链式法则(Chain rule)
计算梯度的关键是对多元函数求导,但深度学习中多元函数通常是复合(composite)的,即无法通过常见函数微分法则求导。
链式法则可以帮助我们对复合多元函数求导。
设可微分函数$y$有变量$u_1, u_2, \ldots, u_m$,其中每个可微分函数$u_i$都有变量$x_1, x_2, \ldots, x_n$,$y$是$x_1, x_2, \ldots, x_n$的函数。对于任意$i = 1, 2, \ldots, n$,链式法则给出:
反向传播(Back Propagation)
现在,依据上方的基础知识,我们已经知道深度学习优化算法的关键在于梯度,梯度的关键在于求导,而这个导数通常不好求,需要链式法则的参与。
反向传播提供了一种基于链式法则快速计算任一偏导数的方法。在深度学习框架中,框架根据我们的模型构建计算图(computational graph),计算图用于跟踪数据、操作和组合顺序,正向传播(即由输入到输出进行计算)后框架通过自动微分(automatic differentiation)反向传播梯度,跟踪整个计算图,填充关于每个参数的偏导数。
在此我不准备罗列一堆复杂的数学公式来推导反向传播的计算细节(我也不会),而是想基于应用的角度,简单的理解一下反向传播。
设有一函数$y=2\mathbf{u} + 5$,其中$u = 3\mathbf{x}^{\top}\mathbf{x}$,$y$是关于$x$的函数。
下面我们来求$y$对$x$的梯度,求梯度本质上就是求偏导。
1 | import torch |
到这里$x$还没有梯度,因为还没有进行反向传播,下面来计算梯度。 原式$y$对$u$的导数为$2$,$u$对$x$的导数为$6x$,根据链式法则,$y$对$x$的导数为$12x$。
1 | y.backward() |
验证得,梯度计算正确。 这里存在一个问题,为什么不去展示中间变量$u$的梯度呢?因为出于节省内存(显存)的考虑,pytorch在反向传播的过程中只保留了计算图中的叶子结点的梯度值,而未保留中间节点的梯度。但的确可以通过一些手段获取中间变量的梯度,只是没必要,这里也不做演示了。
参考文献:
[1]https://zh-v2.d2l.ai/chapter_preliminaries/calculus.html
[2]https://blog.csdn.net/Weary_PJ/article/details/105706318
[3]https://blog.csdn.net/weixin_41799019/article/details/117353078
[4]https://blog.csdn.net/xierhacker/article/details/53431207