输入“/”快速插入内容

是的!你应该了解反向传播

作者:Andrej Karpathy (OpneAI联合创始人)
发表时间:2016年12月
翻译者:Dorothy,翻译过程中参考了这个翻译
当我们在斯坦福大学提供CS231n时(CS231n: Convolutional Neural Networks for Visual Recognition,是顶级院校斯坦福出品的深度学习与计算机视觉方向专业课程),我们在最基础的反向传播课程中特意设计了包含显示计算的编程作业。学生们必须用原始numpy(用于科学计算的Python库)实现每一层的正向和反向传播。难免有学生在课堂留言板上抱怨: “为什么我们要手动编写反向传播,而在现实世界中的框架(如TensorFlow)会自动计算它们?”
这似乎是一个非常合理的呼吁——一旦课程结束后你再也不会编写反向传播,为什么还需要练习呢?我们只是为了自娱自乐而折磨学生吗?一些简单的回答或许可以勉强解释:“出于对知识的好奇,你值得知道”或者“万一以后你想改进核心算法”,但是以下是更有力更实际的论据,我想专门写一篇文章来阐述一下。
向传播的问题在于它是一个抽象漏洞。The Law of Leaky Abstractions 是一个有关程式的定律。最早是由Joel Spolsky在其blog中提出,其定义为“所有非不证自明的抽象概念,都有某种程度的疏漏”)
换句话说,很容易陷入抽象化学习过程的陷阱—觉得自己可以简单地将任意层堆叠在一起,然后反向传播会在你的数据上“神奇地发挥作用”。那么让我们来看一些明显的例子,而事实并非如此,以非常不直观的方式。
一些养眼的东西:带有前向传递(黑色)和反向传递(红色)的 Batch Norm 层的计算图。(引用自这篇文章
sigmoid 函数中的梯度消失
让我们从这里轻松开始。从前有段时间,很流行在全连接层中使用sigmoid(或者tanh)等非线性函数。直到人们想到反向传播才意识到其中的一个棘手的问题,就是如果你很草率地设定初始值或者做数据预处理,这些非线性函数可能会“饱和”或者完全停止学习——你的训练损失值将会变得很平缓或者不再下降。如下例子,一个全连接层中的sigmoid计算(使用原始numpy):
代码块
z = 1/(1 + np.exp(-np.dot(W, x))) # forward pass
dx = np.dot(W.T, z*(1-z)) # backward pass: local gradient for x
dW = np.outer(z*(1-z), x) # backward pass: local gradient for W
如果你的权重矩阵W初始化时太大,矩阵乘法的输出范围可能非常大(例如-400到400之间的数字),这将使向量z中的所有输出几乎都是二进制的:要么1要么0。但是如果这样的话,z(1-z)(sigmoid函数非线性的局部梯度)在两种情况下都将变为零*(“梯度消失”),从而使xW的梯度都为零。由于“乘法”在链式法则中的作用,从此时起,其余的反向传播值将全部为零。(由于梯度是通过将每一层的局部梯度相乘而计算出来的,所以如果某一层的局部梯度为零,那么在这一层之前的所有层的梯度都将变为零。这就是所谓的“乘法”在链式法则中的作用。)
关于 sigmoid 的另一个不明显的有趣事实是,当 z = 0.5 时,其局部梯度 (z*(1-z)) 的最大值是 0.25 。这意味着每次梯度信号流过 sigmoid gate时,其幅度总是会减小四分之一(或更多)。如果你使用的是基础的SGD,那么网络较低层的训练速度将比较高层慢得多。
总而言之如果你在网络中使用的是sigmoid或tanh非线性函数,并了解反向传播的话,你应时刻警惕你的初始化不会导致它们完全饱和。请参阅此CS231n 讲座视频中的详细说明。
垂死的 ReLU
另一个有趣的非线性是 ReLU,它将神经元阈值限制为零。使用 ReLU 的全连接层的前向和后向传播的核心包括:
代码块
z = np.maximum(0, np.dot(W, x)) # forward pass
dW = np.outer(z > 0, x) # backward pass: local gradient for W
观察一会你会发现如果一个神经元在前向传播中被限制到零(即z = 0,它不会“触发”),那么它的权重将得到零梯度。这会导致所谓的“dead ReLU”问题,即如果 ReLU 神经元不幸被如此初始化,它将永远不会激发,或者如果在训练过程中该神经元的权重大幅更新至此区间,则该神经元将永远死亡。这就像永久性的、不可恢复的脑损伤。有时你可以向前传播整个训练集,发现你训练网络中的很大一部分(例如 40%)神经元始终为零。
总的来说:如果你理解了反向传播,并且你的网络用的是ReLU,你会经常担忧Dead ReLU的问题。这些神经元在整个训练集中永远不会对任何样本开启,并且将永久死亡。神经元也可能在训练过程中死亡,通常因为激进的学习率。请参阅CS231n 讲座视频中的详细说明。
RNN 中的梯度爆炸
Vanilla RNN (RNN即Recurrent Neuron Network,Vanilla RNN是最基础的RNN)是反向传播的不直观的另一个很好的例子。我将复制粘贴 CS231n 中的一张幻灯片,该幻灯片是简化的 RNN,不接受任何输入x,并且仅计算隐藏状态的递归(等效地,输入x可以始终为零):