激活函数可以为神经网络提供非线性特征,对神经网络的功能影响很大。20世纪70年代,神经网络研究一度陷入低谷的主要原因是,M.Minsky证明了当时的神经网络由于没有sigmoid这类非线性的激活函数,无法解决非线性可分问题,例如异或问题。因此,从某种意义上讲,非线性激活函数拯救了神经网络。
实际选择激活函数时,通常要求激活函数是可微的、输出值的范围是有限的。由于基于反向传播的神经网络训练算法使用梯度下降法来做优化训练,所以激活函数必须是可微的。激活函数的输出决定了下一层神经网络的输入。如果激活函数的输出范围是有限的,特征表示受到有限权重的影响会更显著,基于梯度的优化方法就会更稳定;如果激活函数的输出范围是无限的,例如一个激活函数的输出域是[0,+∞),神经网络的训练速度可能会很快,但必须选择合适的学习率(learning rate)。
如果设计的神经网络达不到预期目标,可以尝试不同的激活函数。常见的激活函数包括sigmoid函数、tanh函数、ReLU函数、PReLU/Leaky ReLU函数、ELU函数等。
sigmoid函数是过去最常用的激活函数。它的数学表示为:
sigmoid函数的几何图像如图2.10所示。当 x 非常小时,sigmoid的值接近0;当 x 非常大时,sigmoid的值接近1。sigmoid函数将输入的连续实值变换到(0,1)范围内,从而可以使神经网络中的每一层权重对应的输入都是一个固定范围内的值,所以权重的取值也会更加稳定。
sigmoid函数也有一些缺点:
(1)输出的均值不是0。sigmoid的均值不是0,会导致下一层的输入的均值产生偏移,可能会影响神经网络的收敛性。
(2)计算复杂度高。sigmoid函数中有指数运算,通用CPU需要用数百条加减乘除指令才能支持e -x 运算,计算效率很低。
(3)饱和性问题。sigmoid函数的左右两边是趋近平缓的。当输入值 x 是比较大的正数或者比较小的负数时,sigmoid函数提供的梯度会接近0,导致参数更新变得非常缓慢,这一现象被称为sigmoid的饱和性问题。此外,sigmoid函数的导数的取值范围是(0,0.25]。当深度学习网络层数较多时,通过链式法则计算偏导,相当于很多小于0.25的值相乘,由于初始化的权重的绝对值通常小于1,就会导致梯度趋于0,进而导致梯度消失现象。
图2.10 sigmoid函数
为了避免sigmoid函数的缺陷,研究者设计了很多种激活函数。tanh函数就是其中一种,它曾经短暂地流行过一段时间。tanh函数的定义为:
tanh函数的几何图像如图2.11所示。相对于sigmoid函数,tanh函数是中心对称的。sigmoid函数把输入变换到(0,1)范围内,而tanh函数把输入变换到(-1,1)的对称范围内,所以该函数是零均值的。因此tanh解决了sigmoid函数的非零均值问题。但是当输入很大或很小时,tanh函数的输出是非常平滑的,梯度很小,不利于权重更新,因此tanh函数仍然没有解决梯度消失的问题。
ReLU(Rectified Linear Unit,修正线性单元)函数首次用于受限玻尔兹曼机 [47] ,是现在比较常用的激活函数。当输入是负数时,ReLU函数的输出为0;否则输出等于输入。其形式化定义为:
ReLU函数的计算特别简单,没有tanh函数和sigmoid函数中的指数运算,只需要对0和 x 取最大值,可以用一条计算机指令实现。而且,当 x >0时,ReLU函数可以保持梯度不衰减,如图2.12所示,从而缓解梯度消失问题。因此,现在深度学习里,尤其是ResNet等上百层的神经网络里,常用类似于ReLU的激活函数。
图2.11 tanh函数
图2.12 ReLU函数
但是ReLU函数也存在一些问题:
(1)ReLU函数的输出不是零均值的。
(2)对于有些样本,会出现ReLU“死掉”的现象。在反向传播过程中,如果学习率比较大,一个很大的梯度值经过ReLU函数,可能会导致ReLU函数更新后的偏置和权重是很小的负数,进而导致下一轮正向传播过程中ReLU函数的输入是负数,输出为0。由于ReLU函数的输出为0,在后续迭代的反向传播过程中,该处的梯度一直为0,相关参数的值不再变化,从而导致ReLU函数的输入始终是负数,输出始终为0,即ReLU“死掉”。
(3)ReLU函数的输出范围是无限的。这可能导致神经网络的输出的幅值随着网络层数的增加不断变大。
由于ReLU函数在 x <0时可能会死掉,后来又出现了很多ReLU的改进版本,包括Leaky ReLU [48] 和PReLU(Parametric ReLU) [49] 。
Leaky ReLU函数的定义为:
其中,参数 α 是一个很小的常量,其取值区间为(0,1) [50] 。当 x <0时,Leaky ReLU函数有一个非常小的斜率 α ,如图2.13所示,可以避免ReLU死掉。
图2.13 Leaky ReLU函数
PReLU函数的定义与Leaky ReLU类似,唯一的区别是 α 是可调参数。每个通道有一个参数 α ,该参数通过反向传播训练得到。
ELU(Exponential Linear Unit,指数线性单元)函数 [51] 融合了sigmoid和ReLU函数,其定义为
其中 α 为可调参数,可以控制ELU在负值区间的饱和位置。
ELU的输出均值接近0,可以加快收敛速度。当 x > 0时,ELU取值为 y = x ,从而避免梯度消失。当 x ⩽0时,ELU为左软饱和,如图2.14所示,可以避免神经元死掉。ELU的缺点是涉及指数运算,计算复杂度比较高。
图2.14 ELU函数
高斯误差线性单元 (Gaussian Error Linear Units,GELU)是一种在BERT和GPT-2等模型中广泛使用的激活函数。数学上,它可以表示为:
其中, x 是输入值, Φ ( x )是标准正态分布 N (0,1)的累积分布函数,可以用误差函数erf(·)进行表示。GELU函数在实际使用中的近似表示为:
如图2.15所示,GELU函数在输入接近零时保持近似线性,在远离零时则表现出非线性的饱和特性。GELU函数的优点之一是它的光滑性质,在保持非线性特性的同时,允许模型输出的概率分布更加平滑。这种光滑性与ReLU等激活函数相比,在处理语言模型中预测下一个词的概率时非常有用。因此,虽然GELU函数相比ReLU函数的计算复杂度高,但它可以提供更好的梯度传播和模型收敛性能,因而被广泛应用在基于Transformer的大语言模型中。还有很多其他的激活函数,本节不再一一介绍。