在深度学习出现之前,经典机器学习方法的工作重心都在特征提取上。好的特征往往能让特征提取事半功倍。不过,特征提取依赖人的业务经验和对场景的理解,并且特征不具备通用性,往往是“特事特办”。
深度神经网络开创了机器学习的新时代,在很多领域给出了相对通用的解决方案,其应用可以说是遍地开花。图像领域的CNN,NLP领域的Word2vec、LSTM,包括近几年出现的Attention、Transformer等技术,都在各自擅长的领域大放异彩。深度学习出现后,工程师们不用再“处心积虑”地去提取特征以追求一点点的进步了。深度学习把业务领域特征交给神经网络的参数,通过神经网络逐层自动提取特征,在效率上和效果上远超经典模型。
在强大算力的支持下,为了进一步提升效果,神经网络越来越深。深度学习近些年的长足进展,不仅受益于机器学习理论自身的进步,还受益于互联网领域的整体发展。
当神经网络非常深时,相应地会有大量的参数。不过,正如本书前面所分析的,过多的参数容易导致模型发生过拟合,而解决过拟合最直接、最有效的办法就是扩充数据量。目前,各类互联网应用及大数据技术平台提供的海量数据,为深度学习的广泛应用提供了客观条件。
基础硬件的发展,特别是GPU的发展,带来了基础算力的提升。深度神经网络的参数都在百万级,再配以大量数据,其运算量是惊人的。例如,Google在训练BERT模型时,使用了16个TPU集群(一共64块TPU),花了约4天时间,如果没有强大算力的支持,那么这几乎是一个只能停留在思想层面而无法落地的项目。
海量的数据和强大的算力为深度学习的发展提供了客观条件,但仅有这些还不够,网络过深也会带来一些新问题。在学习阶段,神经网络的参数通过梯度下降法进行学习,但网络深度的增加会导致梯度消失和梯度爆炸。在深度学习中,损失函数所对应的曲线非常复杂,有大量局部极小点和鞍点,如果学习方法不合适,那么损失函数的值可能无法降低,从而导致模型效果不佳。此外,过多的参数带来的过拟合问题也亟待解决。
为了解决这些问题,需要对神经网络的细节进行改造。本章将会讲解深度学习中一些至关重要的技术细节。
9.1 激活函数选型
为了解决复杂场景中的问题,神经网络往往会设计得很深,以期获得更好的效果。不过,如果网络过深,第 l 层参数的导数就需要通过链式法则反向传播求解,这涉及 l+1、l+2 等层的激活函数的导数连乘(具体公式可参考第8章)。可见,激活函数选择不当会导致梯度消失。
常见的激活函数,例如Tanh、Sigmoid,公式如下。它们的函数和导数,如图9-1所示。
Tanh(d)=(e^d-e^(-d))/(e^d+e^(-d) )=Sigmoid(2d-1)
Sigmoid(d)=1/(1+e^(-d) )
图9-1
可以看出,Sigmoid函数、Tanh函数的导数都小于1,如果多次相乘,就会趋近于0。而且,越是网络的前层(l 的值越小),导数就越接近0(连乘的次数多)。这会导致损失函数对前层参数求导的结果过小,使前层参数因更新幅度过小而得不到更新。这就是所谓“梯度消失”。在梯度消失问题上,Tanh函数的导数值比Sigmoid函数大一些,因此梯度消失问题的影响相对小一些。
Tanh函数和Sigmoid函数都有饱和区,如图9-2所示。在饱和区内,输入 d 的变化几乎不会引起输出 a 的变化,也就是说,输入的变化对输出的影响极小,并且 ∂a/∂d≈0,参数将得不到学习。
图9-2
梯度消失和激活函数饱和问题困扰了业界多年,导致神经网络的发展长期停滞不前。直到将ReLU函数作为激活函数,这些问题才得以解决,神经网络的发展迎来了新高潮。ReLU函数及其导数的图像,如图9-3所示。
图9-3
ReLU函数的公式如下。
a={█(0,如果 d<0@d,如果 d≥0)┤
当 d ≥ 0 时,∂a/∂d=1,反向传播不会造成梯度消失和梯度爆炸,且没有饱和区。当 d<0 时,梯度为0,本次传播中该神经元“死亡”——这是彻底的消失,也是饱和的形式之一。
在实际应用中,ReLU函数会出现“真死”和“假死”两种情况。
ReLU函数“真死”是指对任何输入恒有 d_j^((l))=w_j^((l)) a^((l-1)) ≤ 0,此时神经网络将无法得
到训练。w_j^((l)) 为参数矩阵 W^((l)) 的第 j 行,d_j^((l)) 为第 l 层神经网络的第 j 个节点,a^((l-1)) 为第
l-1 层的输出(即第 l 层的输入)。在神经网络的中间层,a^((l-1)) 为上一层ReLU函数的输
出,所以有 a^((l-1)) ≥ 0。若 w_j^((l)) 各元素均为非常大的负数,则恒有 w_j^((l)) a^((l-1))<0。这种情况
一般是参数初始化错误或过大的学习因子导致更新权重 w_j^((l)) 过大造成的。因此,在将ReLU
函数作为激活函数时,学习因子不宜过大,w_j^((l)) 的初始化也应遵循一定的准则。
ReLU函数“假死”是指恰巧造成 w_j^((l)) a^((l-1))<0,但下次输入不同的 a 时仍会激活神经
元。ReLU激活函数的神经元死亡问题,在绝大多数情况下都属于假死。适量的假死其实不是坏事,甚至能够提升神经网络的效果,原因如下。
若神经元都不会死亡,即对所有的输入恒有 〖a_j^((l))=d〗_j^((l))=w_j^((l)) a^((l-1)) ≥ 0,ReLU函数就
达不到非线性化,所有层之间的变换均为线性变换,深层网络将退化成一层网络。
假死的神经元可以理解成对某些输入不予输出,起到了特征选择的作用。
为了避免神经元大量死亡,同时具备非线性处理能力,ReLU函数的诸多变体出现了。其中,LReLU(Leaky-ReLU)函数的应用相对广泛,它的函数图像如图9-4所示。
图9-4
LReLU函数的公式如下。
a={█(kd,如果 d<0 @d,如果 d≥0)┤
其中,k 为超参数,取值一般在0和1之间。如果LReLU函数中的 k 是可学习的,那么它就进化成了PReLU函数。
上述激活函数的输入都是 d_j^((l))=w_j^((l)) a^((l-1)),输出都是 f(d)。各激活函数的不同之处在于
f 的形式不同。
不同于上述激活函数,Maxout函数的每个神经元都有一批对应的参数 w_j^((l) ) (1)~w_j^((l) ) (n),
因此,该激活函数的输入是多个值,具体如下。
d_j^((l) ) (1)=w_j^((l) ) (1) a^((l-1) )
⋯
d_j^((l) ) (n)=w_j^((l) ) (n)a^((l-1))
其最终的输出为
a_j^((l+1) )=max(d_j^((l) ) (1),⋯,d_j^((l) ) (n))
即每个可学习参数(w_j^((l) ) (1)~w_j^((l) ) (n))都与当前神经元的输入 a^((l-1) ) 相乘,然后取最大值。以
n=3 为例,如图9-5所示。
图9-5
Maxout函数的强大之处在于,在进行网络训练时,不仅会学习参数,还会自适应地改变激活函数的形状,起到探索激活函数的作用。
在实际的工程中,PReLU函数和Maxout函数的计算量都偏大,且都有超参数,因此使用最多的还是ReLU函数。
理论上,任何非线性函数都有可能成为激活函数,但在实际应用中,经常使用的也就是上述几种。一个非线性函数能够在实践中胜出,必然有其优点。笔者总结了常用激活函数的优点,列举如下。需要注意的是,并不是所有的激活函数都具备以下所有优点(大部分只具备其中几条),这些优点之间也存在相互冲突的情况。但是,只要优点足够突出,激活函数就有机会“胜出”。
零均值输出(激活函数为奇函数)。例如,Tanh函数在学习阶段,输出的均值是0(输出有正有负),W 的各个维度的更新方向可以不同(有正有负),从而避免了单向更新(各个维度只能同增或同减)给学习带来的难度(例如,将 W=〖[2,3]〗^T 更新至 W=〖[1,4]〗^T,只能走Z字形路线)。
适当的非线性。正如前面所分析的,如果只进行线性变换,那么网络的层数将毫无意义。但是,激活函数的变化不宜过于激烈。如果激活函数的变化过于激烈,那么输入的轻微变化就会导致输出的剧烈变化(稳定性不好)。理想的情况是 f(x)≈x。常用激活函数Sigmoid、Tanh、ReLU都有一个近似线性的区域能满足此性质。
导数不宜太小或太大,以免发生梯度消失和梯度爆炸。由于ReLU函数在这方面表现优异,所以它成为深度神经网络激活函数的首选。Sigmoid函数和Tanh函数都不可避免地存在梯度消失问题,在深层神经网络中使用它们时要慎重一些。
单调性。避免因导数忽正忽负而在学习过程中产生震荡。例如,f(x)=sin(x) 不适合作为激活函数使用。
输出范围受到限制。如果输出范围没有限制,那么经过层层激活,各层的输出就有可能被放大至一个很大的值,从而导致最后一层的Softmax函数饱和。例如,f(x)=x^2不适合作为激活函数使用。
没有超参数。Sigmoid函数、Tanh函数、ReLU函数没有超参数,Maxout函数、PReLU函数有超参数。引入超参数会提高使用难度,而设置超参数本身依赖经验。
运算简单,速度快。在大型神经网络中,神经元动辄百万个,因此,运算速度是一个非常重要的指标。ReLU函数的运算速度具有碾压性的优势。
9.2 权重初始化
在训练神经网络前,需要对各层待训练的权重进行初始化