1.5 线性变换和激活函数
1.5.1 全连接线性变换
有了前面的线性代数的基础知识后,我们就可以在这个基础上构建深度学习的基本模块,即线性变换模块。最简单的线性变换就是全连接线性变换。我们可以通过前面介绍的多层感知机(MLP)来举例说明线性变换在神经网络中起到的作用。图1.13画的是一个简单的多层感知机模型,把神经元分成了三类,即输入层(Input Layer)、隐藏层(Hidden Layer)和输出层(Output Layer)。其中,输入层只有1层,这一层有3个神经元(每个神经元由一个浮点数来表示),隐藏层由2层组成,分别有4个神经元和5个神经元,输出层同输入层一样,也只有1层,这一层由2个神经元组成。在实际的多层感知机模型中,一般输入层和输出层各有一层,而中间的隐藏层可以有若干层(这里是2层)。我们可以看到层与层之间的神经元用实线相互连接,而这些相互连接的关系采用线性变换(以及一个非线性函数)的方法来进行描述。具体可以表述为:假如这个神经网络输入的是大小为3的向量,在从输入层到第一层隐藏层的变换过程中,这个大小为3的向量会和一个大小为4×3的矩阵做矩阵乘法,最终得到一个大小为4的向量。对于这个向量,我们需要对每个分量求一个激活函数(后面会提到),得到第一层隐藏层的值。同理,把第一层隐藏层的值(即一个大小为4的向量)乘以5×4的矩阵,然后在得到的结果上作用一个激活函数,最后得到一个大小为5的向量,这就是第二层隐藏层的值。同理,经过一系列的线性变换和激活函数的作用可以得到大小为2的向量,这个向量即为这个神经网络最后的输出。我们可以看到,下一层神经网络的每个值都和前一层神经网络的每个值相关联,我们称这种神经网络连接方式为全连接层(Fully Connected Layer,FC Layer),对应的线性变换为全连接线性变换。对于多层感知机来说,神经网络的连接层中每一层都是全连接层。对于全连接层来说,我们可以用矩阵描述这个连接方式,即如果对一个n大小输入的向量,将其变换为m大小的向量,需要一个m×n大小的矩阵来描述这种线性变换,我们称这种线性变换的矩阵为连接的权重(Weight)。对于神经网络来说,权重是一个经过一定随机初始化(比如权重的每个分量值都初始化为标准正态分布),权重在神经网络的训练过程中会逐渐向着让模型更好地符合数据分布的方向变化,这个过程称之为模型的优化(Optimization)。除权重外,我们一般还会给线性变换之后的输出统一加一个可训练的参数,这个参数是标量,即为一个实数,我们称这个参数为偏置(Bias)。
图1.13 多层感知机连接图例
1.5.2 卷积线性变换
全连接的线性变换由于前一层和后一层所有的神经元之间都有一对一的连接关系,也称为稠密连接层(Dense Layer)。在实际的应用过程中,这种神经元之间关系的描述可能有许多冗余,对于神经网络模型的训练并不是很友好。为此,人们发明了一系列稀疏(Sparse)的连接方式来描述前后两层神经元之间的连接关系,其中最有名的一个就是卷积层(Convolution Layer,Conv Layer),对应的神经网络称为卷积神经网络(Convolution Neural Networks,CNN),如图1.14所示,左边是输入的一张图片(某一通道),右边是一个3×3的卷积核(Convolution Kernel,或称为Filter),卷积核的每个分量都是可训练的实数,也称为权重,在运算过程中,需要从输入图片中取出和卷积核大小相同的一块区域(左边虚线方框的区域),然后把区域里面的数和卷积核的权重按照一一对应的方式相乘,并把所有的乘积求和,作为最后的输出。通过变化虚线框的位置(按照箭头方向移动,得到对应行列的值,其中移动的步长Stride是一个可以调节的参数),可以得到新的输出,这个过程即为卷积的过程。由于卷积核的权重只和输入的局部区域相连接,因此,这里称卷积的连接方式为稀疏连接。在实际的运算中,卷积运算一般是转换为矩阵运算来进行的,首先是把图片按照卷积的顺序转换为矩阵,对应的函数称之为Im2Col(Image to Columns),其中列的大小和卷积核的大小一致,比如3×3的卷积核对应的矩阵的列就是9,行的方向则和卷积的方向一致。然后把卷积核也转换为矩阵,其中行的大小和卷积核大小一致,列的大小和不同的卷积核的数目一致(图1.14所示的是一个卷积核的卷积过程,实践中可以有多个卷积核,对应不同通道的输出)。然后对着两个矩阵做乘法计算,对应调用的函数称之为GEMM(GEneral Matrix Multiplication)。相对于全连接的矩阵乘法而言,卷积的矩阵乘法由于卷积核的大小一般比较小(1×1、3×3、5×5等),相对而言,权重数目也比较小,能有效地减少可训练参数的数目,加快模型的训练和收敛速度。因此,卷积神经网络在深度学习模型中得到了广泛的应用。以上是对卷积神经网络的一个初步介绍,我们将在后面几章对其做进一步介绍。
图1.14 卷积神经网络示意图
1.5.3 激活函数
根据线性代数的知识,我们可以知道线性变换的组合还是线性变换。因此,仅仅通过组合全连接层和卷积层,最后得到的结果是线性变换的组合,可以等价为一个线性变换,因此这对于神经网络模型是没有意义的。为了能够让神经网络引入非线性的特性,我们需要在线性变换之间插入非线性层,于是需要引入激活函数(Activation Function)的概念。所谓激活函数,就是一类非线性函数的统称,通过对线性变换中输出结果的每个分量都应用激活函数,可以输出非线性的结果。理论上,所有非线性的函数都可以用于激活函数,而在实践中,人们主要应用的包括Sigmoid函数、Tanh函数和ReLU函数(见图1.15)。Sigmoid函数的形式如式(1.29)所示(这里用σ来代表Sigmoid函数),其中横坐标为输入的值,纵坐标为输出的值。我们可以看到,Sigmoid函数的取值范围在0~1之间,当x很小的时候接近于0,x很大的时候接近于1。Tanh函数的形式如式(1.30)所示(公式中用tanh来代表Tanh函数),我们可以看到Tanh函数的取值范围在-1~1之间,实际上Tanh函数可以表示为2倍的Sigmoid函数减去1。相比于前面的函数而言,ReLU函数(Rectifier Linear Unit)的形式更简单,如式(1.31)所示,max的意思是取两个值中的较大值,于是当输入值大于0的时候,输出值等于输入值,当输入值小于0的时候,输出值等于0。另外,我们可以看到,ReLU函数相比于前面的两个函数,在x大于0的时候并没有一个边界,而是随着x趋向于无穷大,输出也趋向于无穷大,其相应的导数的值在这个过程中也恒为1(Sigmoid和Tanh在x很大和很小的时候,导数趋向于0),这个特性在后面的反向传播相关的章节会提到,是非常重要的一个特性,有助于深度学习模型的训练和收敛。同时,由于ReLU的计算非常简单(只需要比较输入和0的大小),相比于Sigmoid和Tanh函数,能够更快地进行计算。因此,在深度学习架构的激活函数中得到了广泛的应用。
图1.15 激活函数示意图
以上介绍的主要是输入层和隐藏层之间的激活函数,在最终的隐藏层和输出层之间,根据神经网络预测的目标不同,我们会使用一些不同的激活函数。首先是回归问题。前面已经介绍过回归问题的目标是预测一系列连续的值,由于这些值并没有范围的限制,这里会直接使用全连接的线性输出作为最终的预测(一般最终输出一个值,即神经网络的输出层的神经元数目为1)。因此,如果要做回归预测,一般输出层是没有激活函数的。如果是二分类的问题,可以直接使用Sigmoid函数作为输出,因为Sigmoid函数的输出在0~1之间,能够表示二分类中某一个分类的概率。如果是多分类问题,最后一层的线性输出要等于最终分类的数目,而具体输出的每一个类的概率需要通过Softmax激活函数来得到。假如最终有N个分类,那么第i个分类(i=1,…,N)的概率pi由Softmax激活函数输出,即式(1.32)得到。我们可以看到,在式(1.32)中,首先对线性变换结果的所有分量取指数函数,然后对每个分量分别除以所有分量的和,这样可以保证Softmax函数的输出求和为1,即所有分类的概率的和为1。同时可以看到,在只有两个输出的情况下,Softmax函数最终退化为Sigmoid函数。在实际的计算中,由于线性变换的某一个分量可能会非常大,计算指数函数的时候会超出浮点数的表示范围,为了能够计算这种情况下Softmax函数的输出,一般在计算Softmax函数之前会对所有的分量统一减去这些分量中最大的值,这样可以保持Softmax函数值不变,而且可以避免在计算指数函数的时候因中间结果过大超出表示范围的问题。
除以上常用的激活函数外,还有一系列不同的激活函数,比如,ReLU的一些变种,如ELU、SELU等,这些激活函数有些适用于不同的场景,比如移动端的神经网络的推理计算,有些对于ReLU进行一定的改进,比如改进了ReLU在小于0时的函数行为(ReLU函数在小于0的时候,在神经网络的前向传播过程中不携带任何信息),一般来说,可以考虑在不同条件下尝试这些激活函数,可以在一定程度上提高深度学习的效果。有关具体的激活函数的种类,大家可以参考各深度学习框架相关的文档。
线性变换和激活函数构成了神经网络的基础。通过组合线性变换和激活函数,我们可以构造出多种多样的神经网络。同时,这些神经网络也根据激活函数的不同能够进行回归和分类等任务。我们将会在后续章节详细介绍如何使用这些基础的组件进行组合,构建不同结构的深度学习网络,以使进行不同的机器学习任务。