1.2.1 从机器学习到深度学习

1.机器学习

传统的程序设计范式是:给定程序的输入x,手工编码程序的操作步骤f,然后得到对应的程序输出y=fx)。机器学习的程序设计范式则与此不同。以监督学习(Supervised learning)为例:首先需要收集一定量的程序输入xtrain和输出ytrain,然后通过调整模型参数来近似拟合输入、输出之间的映射关系,最后再通过该映射关系预测新的输入x对应的程序输出。当程序的输入和输出的对应关系f非常明确的时候(例如排序算法),前一种方法简单高效;然而有时候对应关系非常复杂,甚至连程序员自己也无法说清楚完成任务的具体步骤而不得不诉诸某种生物本能(例如人脸识别),后一种方法就有了用武之地。两种程序设计范式的流程如图1-2所示。

图1-2 两种程序设计范式

机器学习之父Tom Michael给机器学习下了一个较为现代的定义[13]:“A computer program is said to learn from experience E with respect to some class of tasks T and performance measure P, if its performance at tasks in T, as measured by P, improves with experience E.”(如果一个计算机程序完成某项任务T的性能指标P会随着经验E而上升,我们就称它能够从任务T及其性能指标P的经验E中学习(1))这里所说的经验E就是图1-2中需要收集的程序输入xtrain和输出ytrain,称作训练数据(Training data)。

2.深度学习

一般来说,训练数据越多,模型效果越好。但由于受到模型容量(Capacity,通俗地讲就是模型的表达能力、拟合数据的能力)的限制,过于简单的模型可能无法挖掘出海量数据中的丰富信息,使得模型性能无法随着数据量的增长而无限增长,而是会达到某个上限就停滞不前。大部分经典的机器学习模型,例如支持向量机[14]、隐马尔科夫模型[15]等,都会受到模型容量的限制;而神经网络模型的容量却深不可测,更大的数据量搭配更大的神经网络模型,几乎总是能够提升模型效果。另外,其他模型通常需要手动提取特征(Feature,即对模型预测有帮助的输入模式),例如图像处理中常用的SIFT特征[16]、语音识别中的MFCC特征[17]、文本处理中的TF-IDF特征[18]等;而神经网络模型具有较强的特征提取能力,能够从原始输入中自行组合出高级特征,完成它所要学习的任务,从而大大减轻了开发者的工作量。

(1)感知机

如前所述,神经网络的历史可以追溯到1943年的MP神经元模型[1],其结构如图1-3所示。该模型认为,一个神经元是一个多输入、单输出的信息处理单元,每个输入神经元xi通过连接的权重wi来表明它的贡献强度。神经元具有空间整合特性和阈值特性:如果所有输入神经元的贡献总和超过某个阈值θ,神经元就表现为激活状态,输出1;否则神经元抑制,输出0。阈值参数也被称为偏置项(bias),可以通过额外引入一个输入值恒为x0=-1、权重为θ(2)的连接实现。显然,通过适当设置权值,MP神经元可以实现布尔逻辑中的与、或、非三种运算。神经元中提供非线性操作的部分被称为激活函数(Activation Function),MP神经元的激活函数是阶跃函数(Step Function):在定义域大于0的部分输出1,小于0的部分输出0(3)

图1-3 MP神经元模型

1957年,美国心理学家罗森布拉特(Frank Rosenblatt)提出了感知机(Perceptron)[19],即将MP神经元的输出保持为二元离散值0和1,但允许输入和权值为实数,同时使用权重学习算法来代替手工设置权重。由于学习算法的引入,感知机具有了在一定程度上模拟人脑的能力,因此引起了广泛的关注。

不过,正如马文·明斯基(Marvin Minsky)1969年在其《感知机:计算几何简介》[20]一书中指出的那样,单层感知机无法解决如图1-4所示的异或门电路(XOR Circuit)问题:假设正方形的两组对角分别属于两个不同的类别,那么单层感知机无法正确地将所有点进行分类。这是因为单层感知机的决策边界(Decision Boundary,使输出发生变化的输入空间中的临界位置)是一个超平面(二维情况下退化为直线),而正方形的相对的两组顶点不是线性可分的(Linearly Separable,即可以用一个超平面分隔开)。马文·明斯基也正是在这一年获得图灵奖,他的批判使得人工智能陷入了长达近20年的低潮期,史称“人工智能寒冬”。

图1-4 异或门电路问题

好在异或门电路问题可以被多层感知机(Multi-Layer Perceptron)解决。严格来讲,多层感知机是一个错误的名字,因为它并不是由感知机逐层堆叠而成的。感知机的输出为离散的0和1,而多层感知机允许中间层的神经元输出值为实数,这些中间层就被称为隐藏层(Hidden Layer)。最初的多层感知机常用Sigmoid激活函数:

Sigmoid函数直译为S-型函数,这是因为它的图像呈现出拉长的S型,参见图1-5。

还有另一个常用的激活函数与多层感知机密切相关,即双曲正切函数tanh:

图1-5 各种激活函数及其导数

这两种激活函数的导函数都有非常简洁的形式,在公式推导中很有用:

实际上,双曲正切函数和Sigmoid函数仅仅相差一次拉伸和平移:

因此两者形状相似,但是双曲正切函数取值范围更大,也更少发生梯度消失(Gradient Vanishing)(4)

近年来,矫正线性单元(Rectified Linear Unit)被广泛使用[12],尤其是在计算机视觉(Computer Vision)任务中。此激活函数及其导数的表达式为:

此激活函数的导数为阶跃函数,在实数轴正半轴上的导数恒为1,因此不容易发生梯度消失或者梯度爆炸(Gradient Explosion)(5),有利于模型训练;但它在实数轴负半轴上的导数为0,假如初始化不当可能发生ReLU神经元死亡(Dying ReLU)现象,即某个神经元及与之相连的权重不再更新,失去学习能力。幸运的是,这种退化现象可以通过将偏置参数初始化为正值、减小学习率等方法来缓解,因此通常不需要过多关注。

这些光滑、可微的激活函数显然比阶跃函数对模型训练更加友好。当采用Sigmoid激活函数时,多层感知机实际上是由逻辑回归(Logistic Regression)模型堆叠而成的。一个包含两个隐藏层的多层感知机示意图如图1-6所示,从输入到输出层各层分别有3、5、4、2个神经元。

图1-6 含有两个隐藏层的多层感知机

在大型神经网络中,如果单独画出每一个神经元,这会导致示意图上的连线庞杂且凌乱不堪,因此也常常把一组神经元用一个矩形表示以精简画面(图1-6中的矩形阴影框)。本书后续章节的编排也将遵循这种惯例,使用圆形表示单个神经元,其输出为单个实数;使用矩形表示一组神经元,视神经元的排布状况,其输出可能为一个向量或矩阵(通常细长的矩形表示输出为向量,长宽较为均衡的矩形表示输出为矩阵)(6)

(2)损失函数

损失函数(Loss Function)用来度量模型的预测结果与真实结果的差异大小。损失函数越小,说明模型预测得越准确,模型就越有效。模型的训练过程通过最小化损失函数来完成。网络隐藏层的激活函数选择较为自由,而输出层的激活函数往往和损失函数配套使用,使得模型梯度的表达式更加统一、模型收敛更快。常见的搭配包括:

· 输出层采用线性激活函数(Linear Activation Function,也称为恒等激活函数,即不作任何变换),损失函数采用均方误差(Mean Square Error)函数,常用在回归问题中。如果用y分别表示真实值和模型的预测值(均为n维向量),则:

其中分母上的2是为了消除平方函数求导后的系数,简化导数的表达式。

· 输出层采用Sigmoid激活函数,损失函数采用二元交叉熵(Binary Cross Entropy)函数,常用在二分类问题中。如果用y分别表示真实值和模型经过激活函数后的预测值(均为实数),则:

· 输出层采用Softmax激活函数,损失函数采用交叉熵(Cross Entropy)函数,常用在多分类问题中。Softmax函数可以将任意n维向量x转化成一个多项分布(Multinomial Distribution):

即先计算x中每个元素xi的指数,然后再用这些指数之和对结果进行归一化。这里的分母也被称为配分函数(Partition Function)。需要注意的是,Softmax函数并非max函数的近似,而是argmax函数的近似——正因如此,它也可以被用于软寻址,即主要从某个位置读取信息,但同时也少量读取其他位置的信息,并且整个过程可微,便于优化。至于对max函数的近似(7),机器学习中通常采用如下函数:

容易验证,Softmax函数恰好是该函数的导数。

如果用y分别表示真实值和模型经过激活函数后的预测值(均为n维向量),则多分类交叉熵损失函数可以写作:

在多分类的情形下,负对数似然(Negative Log Likelihood)与交叉熵等价,所以该公式也可以看成是最大似然估计的训练目标。如果将输出层经过激活函数前的预测值记为z,容易验证在这几种选择下,网络的损失函数对z的导数形式完全相同,都是预测值与真实标签y的残差 (8)。这并非是简单的巧合,背后更深刻的原因可以在广义线性模型(Generalized Linear Model)中找到,这些激活函数的选择可以看成是相应损失函数所隐含的分布(分别是高斯分布、伯努利分布、多项分布)对应的规范连接函数(Canonical Link Function)[21]

多层感知机的每一层都有权重矩阵W和偏置向量b两个参数。如果将损失函数f对网络某一层l经过激活函数前的值zl的梯度记为:δl=∂f/∂zl,则很容易通过多元函数求导的链式法则(Chain Rule),从后往前将网络所有参数的梯度全部求得。这一算法被称为反向传播(Back Propagation)[22],在历史上被重复发明过很多次(9)。在前述三种选择下,多层感知机的反向传播算法的流程可以表述为:

算法1-1 多层感知机的反向传播算法

(3)梯度下降法

得到模型参数的梯度之后,就可以采用梯度下降法(Gradient Descent)来优化模型参数。经过反复迭代,损失函数会越来越小,网络的预测也越来越准确,最终完成网络的训练过程。如果把损失函数比作一座山,梯度下降就是沿着山峰海拔下降最快的方向走,直到无法继续下降(10)时停止。

梯度下降法有三种形式:随机梯度下降(Stochastic Gradient Descent)、批梯度下降(Batch Gradient Descent)、最小批梯度下降(Mini-batch Gradient Descent)。随机梯度下降每次迭代时从训练数据中随机取一个样本,计算梯度并完成梯度下降;批梯度下降选取所有训练样本,计算总的梯度并进行下降;最小批梯度下降则是两者的折中,每次随机选一小批数据来计算梯度,再进行梯度下降。这三种算法相比较,随机梯度下降噪声太大(每个样本的梯度方向可能相差较远),而且无法充分利用显卡等计算设备的并行运算能力;批梯度下降在每次迭代时都要遍历整个数据集,这需要大量计算,此外实践表明批梯度下降法训练的模型泛化(Generalization,指模型在未见过的样本上的预测能力)性能不够好,因而也不够实用;实践中常用的便是最小批梯度下降算法。本书后面提及梯度下降时,都是指最小批梯度下降算法(如算法1-2所示)及其变种,例如动量法(Momentum)[23]、Adam[24]等。

算法1-2 最小批梯度下降法

(4)神经网络的复苏

当数据和算力准备成熟时,神经网络方法也迎来了复苏。例如,Yann Lecun在1994年提出了最早的卷积神经网络(Convolutional Neural Network)LeNet-5[25],可以用于识别手写数字;Hochreiter & Schmidhuber于1997年提出长短期记忆网络LSTM(Long-Short Term Memory)[26]的原型,可以改善普通的循环神经网络(Recurrent Neural Network)中的梯度流,学习到更长的时序依赖。

再后来,学者们已经不满足于浅层神经网络了。虽然神经网络的通用近似定理(Universal Approximation Theorem)[27]告诉我们,具有单个隐藏层和合适激活函数的前馈神经网络已经足够逼近任何连续函数,但是这可能需要指数量级的神经元。深层神经网络的特征提取能力更强,在同样的参数量下,可以对输入空间进行更精细地划分,拟合更复杂的函数。然而,神经网络模型对应的函数是高度非凸(11)的,这使得深层模型的训练举步维艰。终于,Hinton等人[28]经过数年的潜心钻研,于2006年提出了逐层预训练(Layerwise Pretraining)算法,把深层神经网络的训练问题转化为多个浅层神经网络的训练问题,使得深层神经网络的训练变得可行。随着相关研究的不断深入,我们现在已经有能力直接从头训练深层神经网络了。

因此,深度学习的历史虽然不是很长,但是可以明显划分为两个时代:

· 2006年至2012年,通过逐层预训练的方式来解决深层神经网络难以训练的问题。将神经网络的权值预训练到比较合适的初值之后,再到真正的目标任务上开始训练。

· 2012年以后,通过使用更好的初始化方法(Xavier初始化[29]、He初始化[30]等)、优化算法(Adam等[24])、激活函数(ReLU[12]等)和更大的数据集(ImageNet[31]等),已经可以直接端对端(End-to-End)地训练神经网络了。

在自然语言处理领域,深度学习主要有两个标志性的大事件:

· 2013年Word2vec出现,推动了词向量的广泛使用(12),从此神经网络方法慢慢替代传统自然语言处理算法。

· 2018年BERT问世,开启了自然语言处理的ImageNet时代。从此只需使用预训练模型在下游任务上进行精调(Finetune)。