神经网络硬核入门-反向传播(BP)算法

语言: CN / TW / HK

1 发展历史

1943年,Warren McCulloch和Walter Pitts发表题为《A Logical Calculus of the Ideas Immanent in Nervous Activity》的论文,首次提出神经元的M-P模型。

1958年,就职于Cornell航空实验室的Frank Rosenblatt发明了的一种称为感知器(Perceptron)的人工神经网络。它可以被视为一种最简单形式的前馈神经网络。

1969年,Marvin Minsky和 Seymour Papert发表《Perceptrons: an introduction to computational geometry》一书,从数学的角度证明了单层神经网络的局限性,指出神经网络甚至在面对简单的“异或”逻辑问题时也显得无能为力。因为这篇文章,神经网络的研究陷入了很长一段时间的低迷期,史称“Minsky造成的神经网络冰河事件”。

1974 年,Paul Werbos在哈佛大学攻读博士学位期间,就在其博士论文中发明了影响深远的著名BP神经网络学习算法,但没有引起重视。

1986年,David E. Rumelhart, Geoffrey E. Hinton和 Ronald J. Williams发表文章《Learning representations by back-propagating errors》,重新报道这一方法,BP神经网络学习算法才受到重视。

自此,反向传播算法正式成型,历史的车轮开始缓缓转动,一场对神经网络和深度学习的轰轰烈烈的研究和应用正在徐徐拉开帷幕。

(反向传播算法有多努力,有多牛逼,你们知道吗?)

2 基本定义

图2.1 神经网络

图2.1是一个2层神经网络的示例。

第0层又被称为输入层,最后一层又被称为输出层,中间的若干层被称为隐藏层。

神经网络的层数是从隐藏层开始计数的,输入层不计入总层数。

z表示每个神经元的输入信号,上标(l)表示该神经元在神经网络的第l层,下标i表示该层的第i个神经元:

z向量化后为(假设第l层神经网络有k个神经元):

a表示每个神经元的输出信号,上标(l)表示该神经元在神经网络的第l层,下标i表示该层的第i个神经元:

a向量化后为(假设第l层神经网络有k个神经元):

w表示上一层神经元到下一层神经元的权重,上标(l)表示第l-1层神经元到第l层神经元的权重,下标i,j表示前一层的第i个神经元到后一层的第j个神经元的权重:

w向量化后为(假设第l-1层神经网络有k个神经元,第l层神经网络有q个神经元):

b表示每个神经元输入信号的偏置值,有时又被称为偏置项或偏置单元,上标(l)和下标i表示该偏置属于神经网络的第l层第i个神经元:

b向量化后为(假设第l层神经网络有k个神经元):

注:第一次看本文的时候,可以先跳过“向量化”的内容不看。

3 前向传播

前向传播,就是将样本数据输入到神经网络模型,经由神经网络计算,最终输出预测值的过程。

每个神经元的输入信号z,由参数w、b,和前一层的输出信号a 决定(假设第l层神经网络有k个神经元):

向量化后为:

每个神经元的输出信号z,由激活函数σ和同一个神经元的输入信号a决定(为方便起见,本文中整个神经网络都采用同一种激活函数。但是,实际应用中,隐藏层和输出层的激活函数常常是不一样的):

向量化后为:

下面以图3.1为例进行说明。

图3.1 前向传播模型

样本数据为 (x,y)。其中:x=(x1,x2, x3),x的维度为3维;y=(y1,y2),y的维度为2维。

神经网络的输入层的数据z0为:

神经网络的输出层的预测值y^为:

某个神经元节点的输入信号:

某个神经元节点的输出信号:

神经网络的预测值的损失函数/代价函数为:

注1:损失函数LossFunction、误差函数ErrorFunction、代价函数CostFunction,及其缩写L、E、C表达的是同一个函数。

注2:损失函数里的系数1/2,仅仅是为了令函数求导后的系数为1,为了方便计算而设定的,没有实际意义。

4 反向传播

4.1 算法思路

为了能够得到一个优质的神经网络模型,我们需要求模型的两类参数w和b的最优值,以达到令损失函数值最小的目标。

显而易见的,对于这个L的最优化问题,我们可以考虑使用求偏导数的方法,并利用梯度下降法逐步逼近参数w和b的最优解。

但是由于神经网络结构复杂、参数多,导致传统的偏导数和梯度下降法无法直接被使用。直至反向传播算法应运而生,它大大提高了求解神经网络模型的最优化参数的效率,使得神经网络实现了从理论到工业应用的跨越,也掀起了学习和使用神经网络的热潮。

反向传播算法:首先利用损失函数求得模型的最终误差。接着再将误差自后向前层层传递,获取每个神经元的误差。最后将每层每个神经元的误差对w和b求偏导,迭代获取的w和b的最优解,从而构建损失函数最小的最优神经网络模型。当然,和梯度下降法类似,神经网络也需要经过多次迭代,才能够逼近并获得最优模型。

注:反向传播算法能够提高计算效率的两个因素:一是运算过程的向量化,二是能够实现分布式计算。

4.2 求误差δ

我们对任意神经元上的误差δ作出如下定义:

注1:这个误差δ完全是一个人为的定义,求误差δ的目的,是以此为中介,求损失函数对参数w和b的偏导数。我们也可以定义误差为损失函数对神经元输出信号的偏导。

注2:虽然这个误差是一个人为的定义,但是这个思路在机器学习领域一直都有可靠且广泛的应用,例如XGB和LGB的残差也是用了类似的将损失函数对超参数求偏导的方法。

那么,对于神经网络的最后一层(第L层),可以求得整个神经网络的第一组误差:

进行向量化,并记为 公式BP1

其中:右式用到了哈达玛积,表示将两个同样结构的矩阵的相同位置上的数值相乘。

这样,我们就求得了最后一层神经网络的误差。

接着,为了能够把误差层层向前传导,我们需要找到两个相邻层误差之间的关系,对于任意两层l层和l-1层的神经网络:

进行向量化,并记为 公式BP2

其中:右式的第二项和第三项之间用到了哈达玛积,表示将两个同样结构的矩阵的相同位置上的数值相乘。

现在,根据公式BP1我们能够获得网络最后一层的输出误差,接着根据公式BP2我们能够从后往前获得网络所有层的误差。

4.3 求参数w和b的偏导数

获取了各层各神经元的误差δ后,我们就能够利用误差δ作为中介,对模型参数w和b进行迭代求解。

先求解w的偏导数:

进行向量化,并记为 公式BP3

再求解b的偏导数:

进行向量化,并记为 公式BP4

4.4 迭代

和梯度下降法类似,反向传播算法要进行多次迭代,以使得参数w和b趋向能够使损失函数最小的值。

每轮迭代时,参数w和b的变化公式如下:

这里的α是超参数:学习效率(LearningRate)。用于控制参数w和b变化的速度。

注:本文的推导只涉及一个输入样本的数据,但是在实际应用中,一般会使用批量样本,这时只需要将各个样本求得的偏导数取均值,再进行迭代即可,具体内容这里就不作展开了。

4.5 算法流程总结

简要叙述下反向传播算法的流程:

启动一个循环,事先定义好循环终止的条件,循环内容如下:

步骤1:根据公式BP1,求得神经网络模型最后一层的误差δ(L)。

步骤2:根据公式BP2,自后向前,求得神经网络模型所有层的误差δ(l)。

步骤3:根据公式BP3和BP4,求得参数w和b的偏导数∂L/∂w和∂L/∂b。

步骤4:利用偏导数∂L/∂w和∂L/∂b,更新参数w和b。

步骤5:判断当前模型的损失函数L是否符合预期,如果“是”则跳出循环终止训练,否则继续训练。

5 实操技巧

只会推公式对解决问题没有任何帮助!!!

推导数学公式的目的在于了解算法,以达到建立和优化模型的目的。所以谷歌最喜欢用的算法就是“博士生下降”(grad student descent)算法,也就是靠那些数学很好的博士去调参。。。。。。

本节介绍几个和反向传播算法相关的实操技巧。

5.1 梯度消失

先回顾一下公式BP2:

我们重点关注下激活函数的导数:

在模型刚开始学习的时候,激活函数σ的导数σ’变化得较快,这时候不会出现问题。但是,随着模型越来越逼近最优解,或者在训练的过程中不小心的一次扰动,σ’可能就等于0了。

这样,无论后一层的误差δ(l)是多少,乘上一个接近0的数,那么前一层的误差δ(l-1)自然也会近似于0。δ(l-1)一旦等于0后,那么误差就不会继续向前传播了。后续的w和b也就都等于0了。

所以,随着网络层数的增加,远离输出层(即接近输入层)的层不能够得到有效的学习,层数更深的神经网络有时反而会具有更大的训练误差。这就是梯度消失。

有时候为了预防梯度消失,我们会采用 梯度检验 的方法,以确认反向传播算法是否工作在正常状态。

5.2 激活函数的选择

我们先看看传统的sigmoid函数:

图5.1 Sigmoid函数

可以看出,Sigmoid函数只有一小段接近线性的区域,在输出趋近于0或1的区间,函数的值是保持不变或者变化很小的,反映到导数上,就是导数接近于0。

所以,我们为了避免梯度消失, 隐藏层不使用sigmoid函数作为激活函数

为了保证激活函数不至于那么容易就饱和,导致导数等于0,我们考虑将ReLU函数作为激活函数:

图5.2 ReLU函数和PReLU函数

在ReLU中,在自变量大于0的区间,能保证梯度不会消失,在自变量小于0的区间,函数的值被截断。在PReLU中,当x<0时,它不输出0,而是具有一个较小的斜率。

所以, 卷积神经网络钟爱的激活函数是ReLU ,它有利于反向传播阶段的计算,也能缓解过拟合。 在ReLU函数效果不好时,建议尝试PReLU函数 。此外, ReLu和PReLU一般只用于隐藏层

既然提到了激活函数,那么顺便列举一下其它几个Tip,虽然它们和反向传播算法无关:

激活函数最大的贡献就是将非线性引入神经网络模型,以使得算法能够学习更复杂的模型,否则即便增加网络的深度也依旧还是线性映射,起不到多层的效果。

如果是分类问题,输出层的激活函数一般会选择sigmoid函数。

在输入数据不是均值为0的正态分布时,Sigmoid函数输出的均值一般不为0,这是Sigmoid函数除了容易导致梯度消失外,另一个缺点。

5.3 参数w和b的初始化

5.3.1 参数w

在神经网络中,将权重值w初始化为0是不合适的。考虑全连接的深度神经网络,同一层中的任意神经元都是同构的,它们拥有相同的输入和输出,如果再将参数全部初始化为同样的值,那么无论前向传播还是反向传播的各个神经元的取值都将是完全相同的。

所以,我们采用 打破对称性 的方式,进行w初始值的随机化。

在使用ReLu作为激活函数时,为了避免梯度爆炸,建议w采用接近单位矩阵的初始化值。资料表明(我没验证过),初始化w为单位矩阵并使用ReLU作为激活函数,在一些应用中取得了与长短期记忆模型LSTM相似的结果。

两个比较流行的参数 w 的初始化方法:

1 HE 初始化 (He Initialization) 。将 w 初始化为均值为 0 ,方差为 2/dim_input 的正态分布随机数。其中 dim_input 为当前层输入数据的维度。

2 Xavier 初始化 。将 w 初始化为均值为 0 ,方差为 6/(dim_input+ dim_output) 的正态分布随机数。其中 dim_input 为当前层输入数据的维度, dim_output 为当前层输出数据的维度。

5.3.2 参数b

根据公式BP3:

在使用ReLU函数时,一个比较好的做法是用一个较小的正数来初始化参数b(参数b常常被初始化为1/0.1/0.01),以避免神经元节点输出为0的问题。

6 参考资料

[1]    Michael Nielsen Neural Networks and Deep Learning https://neuralnetworksanddeeplearning.com

[2]    Andrew Ng(吴恩达) Machine Learning https://www.coursera.org/learn/machine-learning

[3]    StoneNote 神经网络简史 https://www.jianshu.com/p/8aacc7f201f2

本文转载自公众号: 数论遗珠,作者:阮智

分享到: