乐读窝

深度学习

乐读窝 > 文学理论 > 深度学习

第12章 应用

书籍名:《深度学习》    作者:伊恩.古德费洛



在本章中,我们将介绍如何使用深度学习来解决计算机视觉、语音识别、自然语言处理以及其他商业领域中的应用。首先我们将讨论在许多最重要的AI应用中所需的大规模神经网络的实现。接着,我们将回顾深度学习已经成功应用的几个特定领域。尽管深度学习的一个目标是设计能够处理各种任务的算法,然而截至目前深度学习的应用仍然需要一定程度的特化。例如,计算机视觉中的任务对每一个样本都需要处理大量的输入特征(像素),自然语言处理任务的每一个输入特征都需要对大量的可能值(词汇表中的词)建模。



12.1 大规模深度学习


深度学习的基本思想基于联结主义:尽管机器学习模型中单个生物性的神经元或者说是单个特征不是智能的,但是大量的神经元或者特征作用在一起往往能够表现出智能。我们必须着重强调神经元数量必须很大这个事实。相比20世纪80年代,如今神经网络的精度以及处理任务的复杂度都有一定提升,其中一个关键的因素就是网络规模的巨大提升。正如我们在第1.2.3节中看到的一样,在过去的30年内,网络规模是以指数级的速度递增的。然而如今的人工神经网络的规模也仅仅和昆虫的神经系统差不多。

由于规模的大小对于神经网络来说至关重要,因此深度学习需要高性能的硬件设施和软件实现。



12.1.1 快速的CPU实现


传统的神经网络是用单台机器的CPU来训练的。如今,这种做法通常被视为是不可取的。现在,我们通常使用GPU或者许多台机器的CPU连接在一起进行计算。在使用这种昂贵配置之前,为论证CPU无法承担神经网络所需的巨大计算量,研究者们付出了巨大的努力。

描述如何实现高效的数值CPU代码已经超出了本书的讨论范围,但是我们在这里还是要强调通过设计一些特定的CPU上的操作可以大大提升效率。例如,在2011年,最好的CPU在训练神经网络时使用定点运算能够比浮点运算跑得更快。通过调整定点运算的实现方式,Vanhoucke  et  al.(2011)获得了3倍于一个强浮点运算系统的速度。因为各个新型CPU都有各自不同的特性,所以有时候采用浮点运算实现会更快。一条重要的准则就是,通过特殊设计的数值运算,我们可以获得巨大的回报。除了选择定点运算或者浮点运算以外,其他的策略还包括了如通过优化数据结构避免高速缓存缺失、使用向量指令等。机器学习的研究者们大多会忽略这些实现的细节,但是如果某种实现限制了模型的规模,那该模型的精度就要受到影响。



12.1.2 GPU实现


许多现代神经网络的实现基于图形处理器(Graphics  Processing  Unit,GPU)。图形处理器最初是为图形应用而开发的专用硬件组件。视频游戏系统的消费市场刺激了图形处理硬件的发展。GPU为视频游戏所设计的特性也可以使神经网络的计算受益。

视频游戏的渲染要求许多操作能够快速并行地执行。环境和角色模型通过一系列顶点的3D坐标确定。为了将大量的3D坐标转化为2D显示器上的坐标,显卡必须并行地对许多顶点执行矩阵乘法与除法。之后,显卡必须并行地在每个像素上执行诸多计算,来确定每个像素点的颜色。在这两种情况下,计算都是非常简单的,并且不涉及CPU通常遇到的复杂的分支运算。例如,同一个刚体内的每个顶点都会乘上相同的矩阵,也就是说,不需要通过if语句来判断和确定每个顶点需要乘哪个矩阵。各个计算过程之间也是完全相互独立的,因此能够实现并行操作。计算过程还涉及处理大量内存缓冲以及描述每一个需要被渲染对象的纹理(颜色模式)的位图信息。总的来说,这使显卡设计为拥有高度并行特性以及很高的内存带宽,同时也付出了一些代价,如相比传统的CPU更慢的时钟速度以及更弱的处理分支运算的能力。

与上述的实时图形算法相比,神经网络算法所需要的性能特性是相同的。神经网络算法通常涉及大量参数、激活值、梯度值的缓冲区,其中每个值在每一次训练迭代中都要被完全更新。这些缓冲太大,会超出传统的桌面计算机的高速缓存(cache),所以内存带宽通常会成为主要瓶颈。相比CPU,GPU一个显著的优势是其极高的内存带宽。神经网络的训练算法通常并不涉及大量的分支运算与复杂的控制指令,所以更适合在GPU硬件上训练。由于神经网络能够被分为多个单独的“神经元”,并且独立于同一层内其他神经元进行处理,所以神经网络可以从GPU的并行特性中受益匪浅。

GPU硬件最初专为图形任务而设计。随着时间的推移,GPU也变得更灵活,允许定制的子程序处理转化顶点坐标或者计算像素颜色的任务。原则上,GPU不要求这些像素值实际基于渲染任务。只要将计算的输出值作为像素值写入缓冲区,GPU就可以用于科学计算。Steinkrau  et  al.(2005)在GPU上实现了一个两层全连接的神经网络,并获得了相对基于CPU的基准方法3倍的加速。不久以后,Chellapilla  et  al.(2006)也论证了相同的技术可以用来加速监督卷积网络的训练。

在通用GPU发布以后,使用显卡训练神经网络的热度开始爆炸性地增长。这种通用GPU可以执行任意的代码,而并非仅仅渲染子程序。NVIDIA的CUDA编程语言使得我们可以用一种像C一样的语言实现任意代码。由于相对简便的编程模型,强大的并行能力以及巨大的内存带宽,通用GPU为我们提供了训练神经网络的理想平台。在它发布以后不久,这个平台就迅速被深度学习的研究者们所采纳(Raina  et  al.,2009b;Ciresan  et  al.,2010)。

如何在通用GPU上写高效的代码依然是一个难题。在GPU上获得良好表现所需的技术与CPU上的技术非常不同。比如说,基于CPU的良好代码通常被设计为尽可能从高速缓存中读取更多的信息。然而在GPU中,大多数可写内存位置并不会被高速缓存,所以计算某个值两次往往会比计算一次然后从内存中读取更快。GPU代码是天生多线程的,不同线程之间必须仔细协调好。例如,如果能够把数据级联(coalesced)起来,那么涉及内存的操作一般会更快。当几个线程同时需要读/写一个值时,像这样的级联会作为一次内存操作出现。不同的GPU可能采用不同的级联读/写数据的方式。通常来说,如果在n个线程中,线程i访问的是第i+j处的内存,其中j是2的某个幂的倍数,那么内存操作就易于级联。具体的设定在不同的GPU型号中有所区别。GPU另一个常见的设定是使一个组中的所有线程都同时执行同一指令。这意味着GPU难以执行分支操作。线程被分为一个个称作warp的小组。在一个warp中的每一个线程在每一个循环中执行同一指令,所以当同一个warp中的不同线程需要执行不同的指令时,需要使用串行而非并行的方式。

由于实现高效GPU代码的困难性,研究人员应该组织好他们的工作流程,避免对每一个新的模型或算法都编写新的GPU代码。通常来讲,人们会选择建立一个包含高效操作(如卷积和矩阵乘法)的软件库解决这个问题,然后再从库中调用所需要的操作确定模型。例如,机器学习库Pylearn2(Goodfellow  et  al.,2013e)将其所有的机器学习算法都通过调用Theano(Bergstra  et  al.,2010c;Bastien  et  al.,2012a)和cuda-convnet(Krizhevsky,2010)所提供的高性能操作来指定。这种分解方法还可以简化对多种硬件的支持。例如,同一个Theano程序可以在CPU或者GPU上运行,而不需要改变调用Theano的方式。其他库如TensorFlow(Abadi  et  al.,2015)和Torch(Collobert  et  al.,2011b)也提供了类似的功能。



12.1.3 大规模的分布式实现


在许多情况下,单个机器的计算资源是有限的。因此,我们希望把训练或者推断的任务分摊到多个机器上进行。

分布式的推断是容易实现的,因为每一个输入的样本都可以在单独的机器上运行。这也被称为数据并行(data  parallelism)。

同样地,模型并行(model  parallelism)也是可行的,其中多个机器共同运行一个数据点,每一个机器负责模型的一个部分。对于推断和训练,这都是可行的。

在训练过程中,数据并行从某种程度上来说更加困难。对于随机梯度下降的单步来说,我们可以增加小批量的大小,但是从优化性能的角度来说,我们得到的回报通常并不会线性增长。使用多个机器并行地计算多个梯度下降步骤是一个更好的选择。不幸的是,梯度下降的标准定义完全是一个串行的过程:第t步的梯度是第t-1步所得参数的函数。

这个问题可以使用异步随机梯度下降(Asynchoronous  Stochasitc  Gradient  Descent)(Bengio  et  al.,2001b;Recht  et  al.,2011)解决。在这个方法中,几个处理器的核共用存有参数的内存。每一个核在无锁的情况下读取这些参数,并计算对应的梯度,然后在无锁状态下更新这些参数。由于一些核把其他的核所更新的参数覆盖了,因此这种方法减少了每一步梯度下降所获得的平均提升。但因为更新步数的速率增加,总体上还是加快了学习过程。Dean  et  al.(2012)率先提出了多机器无锁的梯度下降方法,其中参数是由参数服务器(parameter  server)管理而非存储在共用的内存中。分布式的异步梯度下降方法保留了训练深度神经网络的基本策略,并被工业界很多机器学习组所使用(Chilimbi  et  al.,2014;Wu  et  al.,2015)。学术界的深度学习研究者们通常无法负担那么大规模的分布式学习系统,但是一些研究仍关注于如何在校园环境中使用相对廉价的硬件系统构造分布式网络(Coates  et  al.,2013)。



12.1.4 模型压缩


在许多商业应用的机器学习模型中,一个时间和内存开销较小的推断算法比一个时间和内存开销较小的训练算法要更为重要。对于那些不需要个性化设计的应用来说,我们只需要一次性地训练模型,然后它就可以被成千上万的用户使用。在许多情况下,相比开发者,终端用户的可用资源往往更有限。例如,开发者们可以使用巨大的计算机集群训练一个语音识别的网络,然后将其部署到移动手机上。

减少推断所需开销的一个关键策略是模型压缩(model  compression)(Buciluă  et  al.,2006)。模型压缩的基本思想是用一个更小的模型代替原始耗时的模型,从而使得用来存储与评估所需的内存与运行时间更少。

当原始模型的规模很大,且我们需要防止过拟合时,模型压缩就可以起到作用。在许多情况下,拥有最小泛化误差的模型往往是多个独立训练而成的模型的集成。评估所有n个集成成员的成本很高。有时候,当单个模型很大(例如,如果它使用Dropout正则化)时,其泛化能力也会很好。

这些巨大的模型能够学习到某个函数f(x),但选用的参数数量超过了任务所需的参数数量。只是因为训练样本数是有限的,所以模型的规模才变得必要。只要我们拟合了这个函数f(x),我们就可以通过将f作用于随机采样点x来生成有无穷多训练样本的训练集。然后,我们使用这些样本训练一个新的更小的模型,使其能够在这些点上拟合f(x)。为了更加充分地利用这个新的小模型的容量,最好从类似于真实测试数据(之后将提供给模型)的分布中采样x。这个过程可以通过损坏训练样本或者从原始训练数据训练的生成模型中采样完成。

此外,我们还可以仅在原始训练数据上训练一个更小的模型,但只是为了复制模型的其他特征,比如在不正确的类上的后验分布(Hinton  et  al.,2014,2015)。



12.1.5 动态结构


一般来说,加速数据处理系统的一种策略是构造一个系统,这个系统用动态结构(dynamic  structure)描述图中处理输入所需的计算过程。在给定一个输入的情况中,数据处理系统可以动态地决定运行神经网络系统的哪一部分。单个神经网络内部同样也存在动态结构,给定输入信息,决定特征(隐藏单元)哪一部分用于计算。这种神经网络中的动态结构有时被称为条件计算(conditional  computation)(Bengio,2013;Bengio  et  al.,2013b)。由于模型结构许多部分可能只跟输入的一小部分有关,只计算那些需要的特征就可以起到加速的目的。

动态结构计算是一种基础的计算机科学方法,广泛应用于软件工程项目。应用于神经网络的最简单的动态结构基于决定神经网络(或者其他机器学习模型)中的哪些子集需要应用于特定的输入。

在分类器中加速推断的可行策略是使用级联(cascade)的分类器。当目标是检测罕见对象(或事件)是否存在时,可以应用级联策略。要确定对象是否存在,我们必须使用具有高容量、运行成本高的复杂分类器。然而,因为对象是罕见的,我们通常可以使用更少的计算拒绝不包含对象的输入。在这些情况下,我们可以训练一序列分类器。序列中的第一个分类器具有低容量,训练为具有高召回率。换句话说,它们被训练为确保对象存在时,我们不会错误地拒绝输入。最后一个分类器被训练为具有高精度。在测试时,我们按照顺序运行分类器进行推断,一旦级联中的任何一个拒绝它,就选择抛弃。总的来说,这允许我们使用高容量模型以较高的置信度验证对象的存在,而不是强制我们为每个样本付出完全推断的成本。有两种不同的方式可以使得级联实现高容量。一种方法是使级联中靠后的成员单独具有高容量。在这种情况下,由于系统中的一些个体成员具有高容量,因此系统作为一个整体显然也具有高容量。还可以使用另一种级联,其中每个单独的模型具有低容量,但是由于许多小型模型的组合,整个系统具有高容量。Viola  and  Jones(2001)使用级联的增强决策树实现了适合在手持数字相机中使用的快速并且鲁棒的面部检测器。本质上,它们的分类器使用滑动窗口方法来定位面部。分类器会检查许多的窗口,如果这些窗口内不包含面部则被拒绝。级联的另一个版本使用早期模型来实现一种硬注意力机制:级联的先遣成员定位对象,并且级联的后续成员在给定对象位置的情况下执行进一步处理。例如,Google使用两步级联从街景视图图像中转换地址编号:首先使用一个机器学习模型查找地址编号,然后使用另一个机器学习模型将其转录(Goodfellow  et  al.,2014d)。

决策树本身是动态结构的一个例子,因为树中的每个节点决定应该使用哪个子树来评估输入。一个结合深度学习和动态结构的简单方法是训练一个决策树,其中每个节点使用神经网络作出决策(Guo  and  Gelfand,1992),虽然这种方法没有实现加速推断计算的目标。

类似地,我们可以使用称为选通器(gater)的神经网络来选择在给定当前输入的情况下将使用几个专家网络(expert  network)中的哪一个来计算输出。这个想法的第一个版本被称为专家混合体(mixture  of  experts)(Nowlan,1990;Jacobs  et  al.,1991),其中选通器为每个专家输出一个概率或权重(通过非线性的softmax函数获得),并且最终输出由各个专家输出的加权组合获得。在这种情况下,使用选通器不会降低计算成本,但如果每个样本的选通器选择单个专家,我们就会获得一个特殊的硬专家混合体(hard  mixture  of  experts)(Collobert  et  al.,2001,2002),这可以加速推断和训练。当选通器决策的数量很小时,这个策略效果会很好,因为它不是组合的。但是当我们想要选择不同的单元或参数子集时,不可能使用“软开关”,因为它需要枚举(和计算输出)所有的选通器配置。为了解决这个问题,许多工作探索了几种方法来训练组合的选通器。Bengio  et  al.(2013b)提出使用选通器概率梯度的若干估计器,而Bacon  et  al.(2015)、Bengio  et  al.(2015a)使用强化学习技术(策略梯度(policy  gradient))来学习一种Dropout的条件形式(作用于隐藏单元块),减少了实际的计算成本,而不会对近似的质量产生负面影响。

另一种动态结构是开关,其中隐藏单元可以根据具体情况从不同单元接收输入。这种动态路由方法可以理解为注意力机制(attention  mechanism)(Olshausen  et  al.,1993)。目前为止,硬性开关的使用在大规模应用中还没有被证明是有效的。较为先进的方法一般采用对许多可能的输入加权平均,因此不能完全得到动态结构所带来的计算益处。先进的注意力机制将在第12.4.5.1节中描述。

使用动态结构化系统的主要障碍是由于系统针对不同输入的不同代码分支导致的并行度降低。这意味着网络中只有很少的操作可以被描述为对样本小批量的矩阵乘法或批量卷积。我们可以写更多的专用子程序,用不同的核对样本做卷积,或者通过不同的权重列来乘以设计矩阵的每一行。不幸的是,这些专用的子程序难以高效地实现。由于缺乏高速缓存的一致性,CPU实现会十分缓慢。此外,由于缺乏级联的内存操作以及warp成员使用不同分支时需要串行化操作,GPU的实现也会很慢。在一些情况下,我们可以通过将样本分成组,并且都采用相同的分支并且同时处理这些样本组的方式来缓解这些问题。在离线环境中,这是最小化处理固定量样本所需时间的一项可接受的策略。然而在实时系统中,样本必须连续处理,对工作负载进行分区可能会导致负载均衡问题。例如,如果我们分配一台机器处理级联中的第一步,另一台机器处理级联中的最后一步,那么第一台机器将倾向于过载,最后一个机器倾向于欠载。如果每个机器被分配以实现神经决策树的不同节点,也会出现类似的问题。



12.1.6 深度网络的专用硬件实现


自从早期的神经网络研究以来,硬件设计者就已经致力于可以加速神经网络算法的训练和/或推断的专用硬件实现。读者可以查看早期的和更近的专用硬件深度网络的评论(Lindsey  and  Lindblad,1994;Beiu  et  al.,2003;Misra  and  Saha,2010)。

不同形式的专用硬件(Graf  and  Jackel,1989;Mead  and  Ismail,2012;Kim  et  al.,2009;Pham  et  al.,2012;Chen  et  al.,2014b,a)的研究已经持续了好几十年,比如专用集成电路(application-specific  integrated  circuit,ASIC)的数字(基于数字的二进制表示)、模拟(Graf  and  Jackel,1989;Mead  and  Ismail,2012)(基于以电压或电流表示连续值的物理实现)和混合实现(组合数字和模拟组件)。近年来更灵活的现场可编程门阵列(field  programmable  gated  array,FPGA)实现(其中电路的具体细节可以在制造完成后写入芯片)也得到了长足发展。

虽然CPU和GPU上的软件实现通常使用32位或64位的精度来表示浮点数,但是长期以来使用较低的精度在更短的时间内完成推断也是可行的(Holt  and  Baker,1991;Holi  and  Hwang,1993;Presley  and  Haggard,1994;Simard  and  Graf,1994;Wawrzynek  et  al.,1996;Savich  et  al.,2007)。这已成为近年来更迫切的问题,因为深度学习在工业产品中越来越受欢迎,并且由于更快的硬件产生的巨大影响已经通过GPU的使用得到了证明。激励当前对深度网络专用硬件研究的另一个因素是单个CPU或GPU核心的进展速度已经减慢,并且最近计算速度的改进来自核心的并行化(无论CPU还是GPU)。这与20世纪90年代的情况(上一个神经网络时代)的不同之处在于,神经网络的硬件实现(从开始到芯片可用可能需要两年)跟不上快速进展和价格低廉的通用CPU的脚步。因此,在针对诸如手机等低功率设备开发新的硬件设计,并且想要用于深度学习的一般公众应用(例如,具有语音、计算机视觉或自然语言功能的设施)时,研究专用硬件能够进一步推动其发展。

最近对基于反向传播神经网络的低精度实现的工作(Vanhoucke  et  al.,2011;Courbariaux  et  al.,2015;Gupta  et  al.,2015)表明,8位和16位之间的精度足以满足使用或训练基于反向传播的深度神经网络的要求。显而易见的是,在训练期间需要比在推断时更高的精度,并且数字某些形式的动态定点表示能够减少每个数需要的存储空间。传统的定点数被限制在一个固定范围之内(其对应于浮点表示中的给定指数)。而动态定点表示在一组数字(例如一个层中的所有权重)之间共享该范围。使用定点代替浮点表示并且每个数使用较少的比特能够减少执行乘法所需的硬件表面积、功率需求和计算时间。而乘法已经是使用或训练反向传播的现代深度网络中要求最高的操作。