Back-propagation Explained

反向传导和链式法则:一个统一的数学解释

其实网上关于反向传到算法(back propagation)算法的文章和博客挺多的, 比如我觉得Stanford的这个tutorial 就写得非常详尽了。那为什么我还要重新写一遍呢?原因大概是我觉得网上的很多资料都写得太复杂太麻烦了,不方便理解。 很多 tutorials 把激活函数$f(\cdot)$和层之间耦合起来,导致在推导的时候出现很多符号(比如激活函数的输入叫$z$,激活函数的输出叫$a$)。 现在的深度学习框架 (包括最早期的caffe) 都把激活函数和具体的层解耦开了,因此只需要把激活函数当做一个普通的操作 (operator) 就可以了。 然后在back-prop中我们只用针对任意的operator进行推导就可。

反向传导的数学基础就是复合函数的链式法则求导:对于一个函数 $j(x) = f(g(x))$,利用链式法则求导数就是 $\frac{\partial j}{\partial x} = f'(g(x))\cdot g'(x)$。 神经网络就是一个复合函数,网络的每一层就是一个函数,整个网络就是这么多函数的复合。 链式法则其实很简洁,只要求各函数可导,并没有针对具体每一个函数的具体形式有任何特例。 之前的很多介绍反向传导算法的文章里单独把“激活函数”、“损失函数”挑出来作为特例,使得整个数学表达显得特别复杂。

在这篇文章里,我们试图根据链式法则,对网络所有类型的层(operator)用统一的形式解释清楚链式法则和反向传导算法。 相比之前的一些解释,这一套解释数学上非常清晰,涉及的数学符号非常少: 在前向传导时,每层只有输入 $x^l$ 和输出 $y^l$;在反向传导时,各层只有梯度 $\delta^l$。 并且所有的操作(卷积,全连接,激活函数,损失函数)都有相同的数学形式。 为了解释的方便,我们仅仅把操作分为线性操作非线性操作。 在后面我们也可以看到,就连这个区分也可以统一起来。

1. 前向传导 Forward Pass

这部分没什么好讲的,为了引出数学符号的定义,简单介绍一下。
一个简单的线性操作(卷积) 和非线性操作 (sigmoid函数)。 左: $y^1_1 = \sum_i W^1_i\cdot x^l_i$; 右: $y = \text{sigmoid}(x) = 1/(1+e^{-x})$。

对于一个神经网络,$x^l_i$ 表示“第 $l$ 层的操作符的第 $i$ 个输入”,$y^l_i$ 表示“第 $l$ 层的操作符的第 $i$ 个输出”。 对于一个操作而言,只有输入 $x$ 和输出 $y$,请忘记之前那一套输入,输出,激活值等复杂的数学符号。 再次强调,别问,问就是只有输入和输出。 在一次前向传导中,多个操作序贯进行:前一层的输出是下一层的输入: $y^l = x^{l+1}$。

一个简单的三层网络,每一个箭头的连接都对应一个权重(为了简便,每层的权重只用一个数学符号标注)。 注意最后一层接受两个输入(一个是第二层的输出 $y^2$,另一个是 label),并输出损失 $y^3_1$ 作为网络损失(loss), 并且。

上图是一个简单的多层神经网络。为了下面描述的方便,我们暂时将所有的 operators 分为两类:

  1. 以全连接、卷积层为代表的线性operators,在上图中用绿色圆圈表示;
  2. 以激活函数、损失函数为代表的非线性operators,在上图中用红色圆圈表示。

2. 反向传导 Back Propagation

假设神经网络第 $l$ 层的前向传播表示为 $Y = f(X)$,为了不失一般性,这里的输入输出都用大写符号,表示是一个向量。 这里我们对 $f(\cdot)$ 没有任何假设, 唯一的要求是 $f(\cdot)$ 必须是数值可导的 (不一定是符号可导的,比如 ReLU 激活函数在0点不可导,但只需要数值上倒数有定义即可)。

把网络的一层当做一个黑盒子,前向传导的时候输入 $X$,输出 $Y$;反向传导的时候输入 $\Delta^{l+1}$,输出 $\Delta^l = f'(X^l) \cdot \Delta^{l+1}$。 其中 $f'(x^l)$ 是一个 雅克比矩阵 (jacobian matrix)。 对于激活函数,$f'(x^l)$ 是个对角阵;对于全连接层和卷积层,$f'(x^l)_{i,j}$ 就是第 $i$ 个输出和第 $j$ 个输入之间连接的权重。

我们把 $f(\cdot)$ 当成一个黑盒子,那么在反向传导的时候,输入的是 $l+1$ 层传过来的梯度 $\Delta^{l+1}$,那么 $l$ 层向前传递的 梯度满足以下规则: $$ \begin{equation} \Delta^l = f'(X^l) \cdot \Delta^{l+1} \label{eq:backprop-1} \end{equation} $$

这里我们不像其他的各种文章中分别把激活函数、损失函数当做特殊条件。在反向传导中,梯度传播的规则就只有 Eq.$\ref{eq:backprop-1}$ 这一种规则。 其中导函数 $f'(X^l)$ 是一个雅克比矩阵(Jacob), 其第 $i$ 行第 $j$ 列的元素 $f'(X^l)_{i,j}$ 表示第 $i$ 个输出对第 $j$ 个输入的导数。 具体地讲:

下面我分别用激活函数、卷积层、全连接层和损失函数作为示例,说明这几种 operator 的反向传播规则看似各不一样, 其实都遵循 Eq.$\ref{eq:backprop-1}$ 的规则。

2.1 激活函数的反向传导

激活函数的前向传导和反向传导。在反向传导时,输入后一层的梯度$\delta^{l+1}$,输出$l$层的梯度$\delta^l = \delta^{l+1}\cdot f'(x^l)$。 如果 $f(x) = \text{sigmoid}(x)$,那么当输入 $x^l$ 进入 sigmoid 函数的饱和区时,$f'(x^l)$将趋近于0,导致前面的层得不到足够的梯度,出现“梯度消失现象”。

由于激活函数都是单输入单输出的,我们可以用数值符号(而非向量)描述激活函数的反向传导过程。 假设激活函数是 $y = f(x)$,对于任意的输入$x^l_i$,前向传导的输出都是$y^l_i = f(x^l_i)$。 在反向传导时,假设 $l+1$ 层传回的梯度是 $\delta^{l+1}_i$,根据 Eq.$\ref{eq:backprop-1}$, $l$ 层的梯度为 $\delta^l_i = f'(x^l_i)\cdot \delta^{l+1}_i$。

Sigmoid 和 ReLU 反向传导,以及为什么 ReLU 可以进行 inplace 前向传导

这里我们列举化两个比较典型的激活函数:Sigmoid 和 ReLU。 首先写出这两个激活函数的函数式: $$ \begin{equation} \begin{split} \text{sigmoid}: y &= \frac{1}{1+e^{-x}}\\ \text{ReLU}: y &= \begin{cases} x, & \text{if } x\geq 0\\ 0, & \text{otherwise} \end{cases} \end{split} \end{equation} $$ 和导数: $$ \begin{equation} \begin{split} \text{sigmoid}: \frac{\partial y}{\partial x} &= \frac{1}{(1+e^{-x})^2e^x} \\ \text{ReLU}: \frac{\partial y}{\partial x} &= \begin{cases} 1, & \text{if } y \geq 0\\ 0, & \text{otherwise} \end{cases} \end{split}\label{eq:d-sigmoid-relu} \end{equation} $$

结合 Eq.$\ref{eq:backprop-1}$,在 backward 的时候向前传播的梯度分别是: Sigmoid: $$ \begin{equation} \begin{split} \text{sigmoid}: \delta^l &= \frac{1}{(1+e^{-x})^2e^x}\cdot \delta^{l+1} \\ \text{ReLU}: \delta^l &= \begin{cases} \delta^{l+1}, & \text{if } y \geq 0\\ 0, & \text{otherwise} \end{cases} \end{split}\label{eq:back-sigmoid-relu} \end{equation} $$

观察 Eq.$\ref{eq:d-sigmoid-relu}$ 和 Eq.$\ref{eq:back-sigmoid-relu}$,不难发现 ReLU 和 Sigmoid 的区别: 计算 Sigmoid 的导数值必须要知道输入 $x$,而 ReLU 函数的导数值可以通过函数输出$y$ 来可以确定。 也就是说,对 ReLU 而言,输入 $x$ 对于计算反传的梯度值是不必要的。 既然输入 $x$ 在反向传播中没有用到,,那么就可以被覆盖掉(在 forward 中可以进行 inplace 操作)。

实际上 Sigmoid 函数的输入 $x$ 也可以通过输出 $y=\text{sigmoid}(x)$ 推断出来,因此输入 $x$ 也可以被覆盖掉。 只是这样做增加了额外的计算量,相比而言 ReLU 函数 inplace 操作的话额外的代价几乎为0。 只要函数 $f(x)$ 是单调的,输如就可以用输出推断出来。

2.2 卷积层/全连接层的反向传导

卷积层和全连接层的反向传导法则:后一层的梯度 $\delta^{l+1}$ 沿着连接向前传递:$\delta^l$ 等于 $\delta^{l+1}$ 乘以对应的连接权重。 例如:$\delta^{l}_2 = \delta^{l+1}_1\cdot W^l_2 + \delta^{l+1}_2 \cdot W^l_3$。

在全连接层和卷积层中,前层的梯度等于后层梯度沿连接的权重回传。 也即在上图中, $\delta^l$ 等于 $\delta^{l+1}$ 乘以对应的连接权重。 如果将卷积/全连接层看做函数,其实这个反传规则也是符合 Eq.$\ref{eq:backprop-1}$ 的。因为连接的权重本身可以视作该函数输出对输入的导数: $W = \frac{\partial y^l}{x^l}$。

结合 Eq.$\ref{eq:backprop-1}$,我们可以写出 卷积层/全连接层 反向传播的梯度。 以 $\delta^{l}_2$ 为例(请对照上图): $$ \begin{equation} \begin{split} \delta^{l}_2 &= \delta^{l+1}_1\cdot W^l_2 + \delta^{l+1}_2 \cdot W^l_3 \\ &= \delta^{l+1}_1\cdot \frac{\partial y^l_1}{\partial x^l_2} + \delta^{l+1}_2 \cdot \frac{\partial y^l_2}{\partial x^l_2} \end{split}\label{eq:conv-fc-bp} \end{equation} $$ 其中 $\frac{\partial y^{l}_1}{\partial x^l_2}$ 就等于 $y^{l}_1$ 与 $x^l_2$ 之间连接的权重 $W^l_2$。 因此卷积层/全连接层的反向传播规则也遵循 Eq.$\ref{eq:backprop-1}$。 Eq $\ref{eq:conv-fc-bp}$ 写成向量形式为: $$ \Delta^{l} = \Delta^{l+1} \cdot \frac{\partial Y^{l+1}}{\partial X^l} $$ 形式上与 Eq.$\ref{eq:backprop-1}$ 一致。

2.3 损失函数的反向传导

平方损失函数 $\mathcal{L}=(x-y)^2$ 的反向传导。损失函数直接产生梯度 $\delta = \frac{\partial \mathcal{L}}{\partial x}$, 不用接受后层传回的梯度。

损失函数是神经网络的最后一层,因此不用接收后层传回来的梯度。损失函数在反向传导时直接计算损失 $\mathcal{L}$ 对输入 $x$ 的偏导数作为梯度,并反传到前面各层。 以平方损失 $\mathcal{L}(x,y) = (x-y)^2$ 为例,损失函数产生的梯度为损失对函数输入 $x$ 的导数:$\delta = 2x$。 注意由于 label $y$ 并不需要求导,因此损失函数反向传播时只对 $x$ 这一路径返回梯度。

3. 损失函数对各层参数 $W^l$ 的导数:

经过前面的梯度反传,我们得到了最后的损失 $\mathcal{L}$ 对各层输入 $x^l$ 的导数 $\delta^l = \frac{\partial \mathcal{L}}{\partial x^l}$, 也就是损失函数对CNN中间 feature maps 的导数。 但是我们最重要求的是神经网络对参数 $W$ 的导数,并延梯度方向更新网络参数。

3.1 卷积层中对权重$W^l$的导数:

我们用一个简单的卷积层为例,我们可以用很简单的推导得出损失函数对权重$W$的导数 $\frac{\partial \mathcal{L}}{\partial W}$。 如下图所示,经过反向传导,损失函数对该卷积层输出特征的导数是 $\delta^{l+1}$,即 $$ \begin{equation} \frac{\partial \mathcal{L}}{\partial y^{l}} = \delta^{l+1} \label{eq:d-x} \end{equation} $$ 而 $$ \begin{equation} y^{l} = x^l \circledast W \label{eq:conv} \end{equation} $$ $\circledast$ 表示卷积运算。结合 Eq.$\ref{eq:d-x}$ 和 Eq.$\ref{eq:conv}$,很容易推出上图中的 $\frac{\partial \mathcal{L}}{\partial W}$: $$ \begin{equation} \begin{split} \frac{\partial \mathcal{L}}{\partial W^l_1} = \delta^{l+1}_1 \cdot x^l_1 \\ \frac{\partial \mathcal{L}}{\partial W^l_2} = \delta^{l+1}_1 \cdot x^l_2 \\ \frac{\partial \mathcal{L}}{\partial W^l_2} = \delta^{l+1}_2 \cdot x^l_1 \\ \label{eq:d-w} \end{split} \end{equation} $$

3.2 卷积层中对偏置 $b$ 的导数:

考虑卷积中的偏置项: \begin{equation} y^{l} = x^l \circledast W + b \label{eq:conv-with-bias} \end{equation} 很容易得到 $$ \begin{equation} \frac{\partial \mathcal{L}}{\partial b} = \delta^{l+1}_1 \label{eq:d-b} \end{equation} $$

至此,我们得到了损失函数对权重 $W$ 以及偏置 $b$ 的导数。接下来只需要用梯度下降法更新网络参数即可。

4. 总结 Conclusion remarks

总的来说,反向传导算法从将数据 $x$ 输入到网络,到得到损失对网络参数的导数,大致有以下几个步骤:

  1. 前向传导,得到各层的输出$y^l$;
  2. 从损失函数开始由后向前,根据 Eq.$\ref{eq:backprop-1}$ 进行梯度反传,得到损失函数对各层输出的导数。 再次强调没有什么激活函数损失函数,所有的层都是一个 operator,唯一的准则就是 Eq.$\ref{eq:backprop-1}$;
  3. 根据 Eq.$\ref{eq:d-w}$ 和 Eq.$\ref{eq:d-b}$ 计算损失函数对参数的导数

全文完,感谢阅读。如果你有任何问题,或者发现有任何表述、数学表达的错误,请在下方留言。


(The comment system is provided by Disqus that is blocked by the GFW. For users from mainland China you may need a VPN.)