1.2 深度学习神经网络
深度学习是从海量数据中学习的方法,它是从不同层数的神经网络提取数据特征,层数越高,这些被提取的特征可能越抽象。深度学习中的“深度”就是在这些神经网络结构中的连续的表示层,模型中包含多少层就是网络结构的深度。现在的深度学习网络一般都包含了几十个甚至上百个表示层,这些表示层组成的神经网络都会从我们设定的训练数据集中进行学习。深度学习最终的表现受到的最大制约是输入神经网络模型中的数据集,算法的不断迭代和更新促使我们更好地学习数据集。
深度学习神经网络是一种计算学习系统,它使用功能网络来理解并将一种形式的数据输入转换为所需的输出(通常为另一种形式)。人工神经网络的概念受到了生物学和人类大脑神经元共同发挥作用来理解人类感官输入方式的启发。神经网络只是深度学习算法中使用的许多工具和方法之一。神经网络本身可以在许多不同的深度学习算法中作为一部分,以便将复杂的数据输入处理到计算机可以理解的空间中。
使用神经网络的深度学习算法,通常不需要通过定义对输入的期望的特定规则进行编程。相反,神经网络学习算法通过处理训练期间提供的许多带标签的示例(即带有“答案”的数据)来学习,并使用此答案来学习输入的哪些特征以构造正确的输出。一旦处理了足够多的示例,神经网络就可以开始处理看不见的新输入并成功返回准确的结果。程序看到的示例和输入的种类越多,结果通常就越准确,因为程序是根据经验学习的。
深度学习的发展受到了神经生物学的启发,一些核心概念都是从神经生物学中的既有概念得到的。深度学习机制与神经传导机制的对比如图1-2所示,但是深度学习只是借鉴了神经生物学的一些灵感,两者的学习机制并不是相同的,此处需要向读者进行声明。目前,神经网络已应用于许多现实生活中的问题,其中包括语音和图像识别、垃圾邮件过滤、财务和医疗诊断等。
●图1-2 深度学习机制与神经传导机制的对比图
1.2.1 神经网络
通过一个示例可以更好地理解深度学习神经网络这个概念。想象一下尝试在文本中如何将“我爱你中国”合理地分词。尽管这对于人类来说很容易理解,但是使用经典方法训练计算机来进行分辨要困难得多。考虑到“我爱你中国”在文本上的分词可能存在多种可能性,因此当文本变长时几乎不可能编写用于说明每种情况的代码。但是,使用深度学习,尤其是神经网络,该程序可以更好地理解文本中的内容。通过使用几层函数将文本分解为计算机可以使用的数据点和信息,神经网络可以判断许多示例中存在的趋势,并根据文本的相似性对其进行处理和分词。
图1-3中,最左侧的第一列为输入层(Input Layer),输入神经网络的所有数据都是从这一层输入的,此处标记为第0层。中间第二列为中间层,但是大部分情况下我们称之为隐藏层(Hidden Layer)。因为这一层是对输入的数据进行处理的层,我们只能看到输入的数据和输出的数据,看不到数据在这些层中是如何被加工、提取特征并且返回的,类比于肉眼看不到的神经元,所以在这里的所有层都被变为隐藏层,这一层被标记为第1层(层数多时,会有多个隐藏层)。右边第三列,我们称之为输出层(Output Layer),这层将输出我们需要得到的结果,这层被标记为第2层。
●图1-3 神经网络结构图
一般情况下,神经网络是从隐藏层开始计算层数的,第0层的输入层不计入神经网络层数。这个神经网络是一个最简单的两层神经网络,后续所有需要应用的深度学习神经网络都是在此基础上形成的。
神经网络可以应用于各种各样的问题,并且可以评估许多不同类型的输入,包括图像、视频、文件、数据库等。它们也不需要显式编程即可解释这些输入的内容。由于神经网络提供了通用的解决问题的方法,因此该技术可以应用的领域几乎没有限制。当今神经网络的一些常见应用包括图像/模式识别、自动驾驶车辆轨迹预测、面部识别、数据挖掘、电子邮件中的垃圾邮件过滤、医学诊断和癌症研究。如今,神经网络的用途还有很多,并且使用率正在迅速提高。
1.2.2 隐藏层
在神经网络中,隐藏层位于输入层和输出层之间,它的功能是将权重应用于输入层输入的数据,并通过激活函数将其转化为输出层的数据。简而言之,隐藏层对输入到网络中的输入执行非线性变换。隐藏的层取决于神经网络的功能,并且类似地,这些层可以根据其相关的权重而变化。
隐藏层在神经网络中很常见,但是用法和体系结构经常因情况而异。它是几乎任何神经网络的典型部分,可以在其中模拟人脑中所进行的活动的类型。隐藏层中的人工神经元都像大脑中的生物神经元一样工作——它吸收概率输入信号,对其进行处理,并将其转换为与生物神经元轴突相对应的输出。深度学习模型的许多分析都集中在神经网络中隐藏层的构建上,设置这些隐藏层以生成各种结果的方法有多种。例如,专注于图像处理的卷积神经网络、常用的循环神经网络以及可直接用于训练数据的简单前馈神经网络。
1.2.3 梯度下降
在使用深度学习神经网络模型进行训练时,输入层输入的数据经过模型的转化,会从输出层输出一个结果,因此,需要判断这个结果和真实值之间的差异,一般是差异值越小越好。梯度下降(Gradient Descent)方法可以很好地评估这个差异。
我们以经典的房价预测模型来帮助理解梯度下降算法。现在,给定历史房屋数据,需要创建一个模型,该模型在给定房屋面积(1 ft2=0.093 m2)的情况下预测新房屋的价格。
让我们从绘制历史住房数据开始,如图1-4所示。
●图1-4 房屋面积与对应的房屋价格
现在,使用简单的线性模型(在历史数据上拟合一条线)来预测给定面积(x)的新房子(ypred)的价格,如图1-5所示。
●图1-5 新房子价格预测图
如图1-6所示,点画线给出了给定房屋面积(x)的预计房价(ypred)。
●图1-6 给定房屋面积的预计房价
实线提供了历史数据中的实际房价,yactual和ypred之间的差异(由虚线给出)是预测误差(E)。
因此,我们需要找到一条具有最佳a、b值(称为权重)的线,以减小预测误差并提高预测精度,使其最适合历史数据。因此,我们的目标是找到最佳的a、b,以最小化房屋实际价格和预测价格之间的误差。
平方和误差(Sum of Squared Errors,SSE)=1/2∑(yactual-ypred)2
这是梯度下降出现的地方。梯度下降是一种优化算法,可找到减小预测误差的最佳权重(W,b)。
现在,让我们逐步了解梯度下降算法:
(1)使用随机值初始化权重(W和b),然后计算误差(SSE);
(2)计算梯度,即当权重(W和b)从其随机初始化值改变很小的值时,SSE的变化。这有助于我们在使SSE最小化的方向上改变W和b的值;
(3)使用梯度调整权重以达到最佳值,即SSE最小化;
(4)使用新的权重进行预测并计算新的SSE;
(5)重复第(2)步和第(3)步,直到SSE不再显著减少。
如果线性函数能够很好地拟合到这个模型的数据,那么,就可以通过这个方法最后得到接近实际房价的预测值。但是在实际操作中,我们发现线性函数所拟合的数据结果往往并不能很好地将预测的结果与实际的结果贴合,因此,我们就需要增加一些权重使得线性函数变为非线性函数 [一般标记为F(x)] 来进行拟合:
ypred=F(x)=w1x1+w2x2+…+wnxn+b=Wx+b
这个非线性的函数可以被看作两个一维向量W和x的乘积加上一个一维向量B。但是在大多数情况下,由于偏置B的维度远低于权重W,将其省略后对函数的影响微乎其微,因此,一般情况下可以标记为F(x)=Wx。
梯度下降的函数又称成本函数(Cost Function),标记为如下的函数:J(W,b)= y-F(x)。成本函数如图1-7所示。
●图1-7 成本函数示意图
1.2.4 激活函数
有一种说法认为:“没有激活函数的神经网络实质上只是线性回归模型”,激活函数在神经网络中占据着重要的位置。激活函数通过计算加权和并进一步增加偏差来决定是否要激活神经元。激活函数的目的是将非线性引入神经元的输出中。它可以对输入进行非线性转换,使其能够学习和执行更复杂的任务。
我们在1.2.3节中对神经元进行数据运算时用的是F(x)=Wx,现在以此来进行计算。
第一层的计算结果为ypred(1)=W(1)x+b(1),激活值为A(1)=ypred(1)。
第二层的计算结果为ypred(2)=W(2)(A(1))+b(2),激活值为A(2)=ypred(2)。
将第二层的计算结果展开,可得如下公式:
ypred(2)=W(2)(W(1)x+b(1))+b(2)
ypred(2)=(W(2)W(1))x+(W(2)b(1)+b(2))
令W(2)W(1)=W,W(2)b(1)+b(2)=b,最终输出
ypred(2)=Wx+b
由上述推演过程可以看出,即使应用了隐藏层,输出的结果仍会是线性函数。因此可以得出结论:无论神经网络中连接多少个隐藏层,所有层的行为方式都相同,因为两个线性函数的组合仍是线性函数。神经元仅凭其线性无法学习。非线性激活函数将使其根据差异误差进行学习。因此,我们需要激活函数。
以下为目前常用的几种激活函数,可以根据实际的业务场景选择合适的激活函数进行使用。
(1)线性函数
公式:y=ax。
无论有多少层,如果使用线性激活函数,模型本质上都是线性的。
取值范围:(-inf,+inf)。
用途:线性激活函数仅在一个地方使用,即输出层。
(2)Sigmoid函数
公式:y=1/(1+e-x)。
性质:非线性。请注意,x值介于-2与2之间时,y值非常陡峭。这意味着在该区间x值的一点变化也会导致y值的剧大变化。
取值范围:(0,1)。
用途:通常在二分类的输出层中使用,结果为0或1,因为Sigmoid函数的值仅介于0和1之间,如果值大于0.5,则可以将结果预测为1,否则,结果为0。Sigmoid函数如图1-8所示。
●图1-8 Sigmoid函数
(3)tanh函数
比Sigmoid函数更好的激活是tanh函数,也称为双曲正切函数。它实际上是Sigmoid函数的数学变体版本。两者是相似的,可以相互推导。
公式:f(x)=tanh(x)=2/(1+e-2x)-1
或者tanh(x)=2sigmoid(2x)-1
取值范围:(-1,1)。
性质:非线性。
用途:通常用于神经网络的隐藏层,它的值介于-1与1之间,隐藏层的均值为0或非常接近,因此有助于通过使均值接近0来使数据居中,这使学习下一层变得容易得多。Tanh函数如图1-9所示。
(4)ReLU函数
ReLU(Rectified Linear Unit,ReLU)函数也称修正线性单元,是使用最广泛的激活函数,主要在神经网络的隐藏层中使用。
公式:A(x)=max(0,x)。如果x为正,则给出输出x;否则,为0。
取值范围:[0,inf)。
性质:非线性。这意味着我们可以轻松地向后传播偏差,并且ReLU函数可以激活多层神经元。
●图1-9 tanh函数
用途:ReLU函数比tanh函数和Sigmoid函数更加节省资源,因为它涉及更简单的数学运算。一次只有少数神经元被激活,使得网络稀疏,从而使其高效且易于计算。ReLU函数如图1-10所示。
●图1-10 ReLU函数
(5)Softmax函数
Softmax函数是Sigmoid函数的一种变体,适合处理多分类问题。
性质:非线性。
用途:通常在尝试处理多个类时使用。Softmax函数会将每个类别的输出压缩在0和1之间,并且除以输出的总和。Softmax函数更适用于分类器的输出层。
一般情况下,可以根据自己或者其他人的经验选择合适的激活函数,如果真的不确定应该使用哪种激活函数,应优先选择使用ReLU激活函数,因为它具有普适性。如果输出用于二分类,那么Sigmoid激活函数是很自然的选择。
1.2.5 权重初始化
权重初始化对深度学习神经网络最终的收敛有重要影响,合理的权重参数往往可以加速模型的训练。
(1)权重初始化的原因
假设我们的神经网络是一个包含9层隐藏层的神经网络,每个隐藏层包含了2个隐藏单元。网络的权重初始化设置为Wi(i=1,2,…,9),偏置初始化设置为0,激活函数设置为ReLU激活函数。结构如图1-11所示。
●图1-11 9层神经网络
在网络进行前向传播的过程中,如果在第1层输入的是,那么通过第1层隐藏层后,两个隐藏单元输出的值分别是和。如果,那么这两个隐藏单元的输出值是一样的,也就是说这两个隐藏单元会导致相同的梯度。在训练过程中,这会产生对称性的训练,从而阻止不同神经元学习不同的事物。当和太大或者太小时,虽然打破了对称性,但是将会导致神经元的学习缓慢或者发散。因此,选择合适的初始化值非常重要。
在这个9层神经网络中,假设所有的激活函数是线性的(恒等函数),那么输出的预测值为
其中,L=10,由于从第1层到第9层的神经元都是2个并且接收了2个输入,所以W1,W2,…,WL-1都是2×2的矩阵。
假设W都被初始化为1.5,如下所示:
对应到式(1.1)中,可以看到这个输出的激活值a[L]是随着L呈现指数级增长的,当这些激活值被用于反向传播时,会导致出现梯度爆炸问题。换言之,受限于参数的损失函数的梯度过大,导致损失函数在其最小值附近振荡。
假设W都被初始化为0.5,如下所示:
对应到式(1.1)中,可以看到这个输出的激活值a[L]是随着L而呈现指数级下降的,当这些激活值被用于反向传播时,会导致出现梯度消失问题。换言之,受限于参数的损失函数的梯度过小,导致损失函数在达到最小值之前已经收敛。
由此可见,进行合理的参数初始化是非常重要的。
(2)如何找到合适的初始化权重值
为了防止梯度消失或者梯度爆炸,需要坚持以下原则:一是激活值的均值应该是0;二是每一层的激活值的方差应该保持一致。
在这两个原则下,反向传播的梯度信号就不会在任意层中被过小或过大的值相乘,从而不会出现梯度消失或者梯度爆炸的情况。
更加具体地,对于L层,它的前向传播如下所示:
a[L -1]=g[L-1](z[L-1])
z[L ]=W[L]a[L-1]+b[L]
a[L ]=g[L](z[L])
此外,需要遵循以下的公式:
E(a[L-1])=E(a[L])
Var(a[L-1])=Var(a[L])
参数初始化时是为了让神经网络在训练过程中学习到有用的信息,这意味着参数梯度不应该为0。
(3)Kaiming初始化的概念与PyTorch实现
在计算机视觉的深度学习网络中,大部分情况下使用的激活函数都是ReLU激活函数,在这种情况下一般选择Kaiming初始化(又称为He初始化、Msra初始化)。Kaiming初始化是何凯明在推导Resnet网络的论文中提出的。文中提到了“正向传播时,状态值的方差保持不变;反向传播时,关于激活值的梯度的方差也保持不变。”
当单层使用ReLU激活函数时,其平均标准差将非常接近输入连接层数的平方根。代码如下所示:
输出的结果如下:
按输入连接层数的平方根缩放权重矩阵的值将导致每个单独的ReLU层平均具有1的标准差,如下所示:
输出结果如下:
由上述所示,将层激活的标准差保持在1附近将使我们能够在深度神经网络中堆叠更多的层,而不会出现梯度爆炸或消失的情况。
何凯明团队据此提出了根据输入权重初始化的策略,如下所示:
1)在给定层上创建具有适合权重矩阵的维度的张量,并用服从标准正态分布的随机数字填充它。
2)将每个随机选择的数字乘以sqrt(2/n),其中n是从上一层的输出进入给定层的传入连接数。
3)偏置张量初始化设置为0。
定义Kaiming初始化函数的代码如下所示:
Kaiming初始化的均值和标准差:
Kaiming正态分布初始化器:它从以0为中心、标准差为std = sqrt(2/n)的截断正态分布中抽取样本,其中n是权值张量中的输入单位的数量。
Kaiming均匀分布初始化器:它从区间[-sqrt(6/n),sqrt(6/n)]的均匀分布中抽取样本,其中n是权值张量中的输入单位的数量。
当训练超过30层的卷积神经网络时,Kaiming团队发现,如果采取Kaiming初始化的策略,这个网络会有较好的收敛。对于计算机视觉的任务,大部分情况下都是包含了ReLU激活函数和多个深层的层,因此,采取Kaiming初始化是一种比较好的策略。
1.2.6 正则化
正则化一词的意思是使事情变得规则或可以接受,这就是我们在深度学习中使用它的原因。正则化是一种通过在给定的训练集上适当拟合函数并避免过度拟合来减少错误的技术。正则化可以通过在误差函数中添加附加惩罚项来调整函数,附加项控制过度波动的函数,使系数无法取极值。在神经网络中,这种保持检查或减小误差系数值的技术称为权重衰减或权重增加。
我们知道,高斯噪声是目标变量与实际获得的输出值(遵循高斯分布)之间的偏差。这样做是为了表示任何现实世界数据集的情况,因为不存在没有任何噪声的完美数据。那么假设在数据集上的初始条件是目标变量ypred由实际值组成,并加上一些高斯噪声(Gaussian noise):
ypred=F(x,W)+Gaussian noise
在许多情况下,使用此误差函数通常会导致过度拟合。因此,引入了正则化项。引入正则化系数后,整体成本函数变为
J(x,W,b)=J(x,W,b)+λφ(W)
其中,λ为控制权重W的系数,可以通过改变λ来达到控制权重大小的目的。
1.2.7 归一化
当网络层数比较深时,模型可能对初始随机权重和学习算法的配置敏感。因为在每次进行小批量处理(mini-batch)之后,当权重更新时,输入到网络深层的分布可能会发生变化。这可能导致学习算法永远追逐运动目标。网络中各层输入分配的这种变化称为内部协变量偏移(Internal Covariate Shift,ICS)。
(1)批量归一化
批量归一化(Batch Normalization)是用于训练深度学习模型的流行归一化方法之一。通过在训练阶段稳定层输入的分布,它可以实现对深度神经网络的更快、更稳定的训练。为了改善模型中的训练,重要的是减少内部协变量偏移。批量归一化在这里通过添加控制层输入的均值和方差的网络层来减少内部协变量偏移。
批量归一化的优点如下:
1)批量归一化可减少内部协变量偏移(ICS)并加速深度神经网络的训练。
2)这种方法减少了梯度对参数或参数初始值的比例的依赖,从而提高了学习率,而没有发散的风险。
3)批量归一化可以通过防止网络陷入饱和模式来使用饱和非线性。
(2)权重归一化
权重归一化(Weight Normalization)是在深度神经网络中对权重向量进行重新参数化的过程,该过程通过将权重向量的长度与其方向解耦来进行。简单来说,可以将权重归一化定义为一种改善神经网络模型权重可优化性的方法。
权重归一化的优点如下:
1)权重归一化改善了优化问题的条件,并加快了随机梯度下降的收敛速度。
2)它可以成功应用于长短时记忆(Long Short-Term Memory,LSTM)等递归模型以及深度强化学习或生成模型。
(3)层归一化
层归一化(Layer Normalization)是提高各种神经网络模型训练速度的一种方法。与批量归一化不同,此方法直接从隐藏层内神经元的总输入中直接估算归一化统计量。层规范化基本上是为了克服批量规范化的缺点而设计的,例如依赖于小批量等。
层归一化的优点如下:
1)通过在每个时间步分别计算归一化统计量,可以轻松地将层归一化应用于递归神经网络。
2)这种方法有效地稳定了循环网络中的隐藏状态。
(4)组归一化
组归一化(Group Normalization)可以说是批量归一化的替代方法。该方法通过将通道划分为组并在每个组内计算均值和方差进行归一化(即对每个组内的特征进行归一化)来工作。与批量归一化不同,组归一化与批次大小无关,并且其准确性在各种批次大小中都很稳定。
组归一化的优点如下:
1)它具有取代许多深度学习任务中的批量归一化的能力。
2)只需几行代码,就可以轻松实现。