在其他同学的文章中已经介绍过了,阿里新的自动语音识别系统的第一个落地点,被选定在客服电话语音识别上。
这个落地项目非常难,首先就在于我们面对的语音数据非常多样化:比如各种随意的对话、不完整的句子、各种话题以及各种传输差异和环境噪声。面对如此复杂的语音数据,我们后端的语音识别声学模型就一定要尽可能的覆盖各种可能的场景,包括各种对话、各种声道、各种噪音甚至各种口音,而要覆盖这些场景,就要求我们用海量的数据来训练语音识别声学模型。面对如此海量的数据,如何快速有效的、迭代式的训练语音识别声学模型、不断调优,从而体现大数据的价值,就成了一个非常紧迫的技术课题。
当今的工业界自动语音识别系统的声学模型都是基于海量的数据训练得到的。数万小时的训练数据是一种常见的声学模型训练配置,国际知名大公司(Google, Microsoft)以及国内的几大互联网巨头(腾讯、百度)莫不如此。要达到顶尖的识别率和顶级的语音识别公司抗衡,我们必须要具备大规模训练的能力。语音识别通常的帧率是100fps,对应每一万小时数据就有3.6 billion个训练样本。完成一次训练,还需要在这些样本上迭代多遍至收敛。这么多的样本是什么概念呢,按照样本数目算的话,Google Brain那个著名的猫的模型也不过用了10 million 样本 (randomly selected YouTube video thumbnails)。当然,不同领域不太好直接的去对比,但这也说明语音识别领域需要的数据是相当多的。如果用当今最新的单个GPU卡进行训练的话将会需要2到4周的时间。这样的周转周期对于互联网时代快速迭代更新模型上线的要求而言,显然是无法接受的。此外,模型训练还涉及到参数调优(tuning)的问题。如果一个模型需要2到4周才能调整一次训练参数,真正训练出合适的模型用于应用就更慢了。
什么是“GPU多机多卡middleware”
摆在我们面前的问题是如何快速的训练语音识别声学模型,进而加速迭代速度,降低参数调优的成本。通常的技术路线是改造现有GPU训练工具,变单机单卡为多机多卡,通过大规模分布式机器学习系统来加速。
但直接这么做会面临一个问题:大家知道,deep learning领域的研究近年来呈爆炸式增长,各种老的、新的模型层出不穷。在deep learning应用最广泛的语音领域也是一样,从一开始的DNN,到后来的CNN,再后来的RNN、LSTM,几乎所有传统上的模型甚至其组合都有被尝试过,并取得了性能上的进步。每一种模型的背后,都有一个甚至多个开源GPU训练工具,例如DNN的Kaldi、HTK;CNN的caffe、cuda-convnet;LSTM的CURRENNT等等。如果我们一个一个的将上述工具转换为多机多卡版本,其工作量可想而知。如果再有更新的模型及其训练工具被开放出来(如最近开源的Google TensorFlow),所有的多机多卡改造工作又得再重来一遍。
针对这一问题,我们想到:在对单机单卡程序进行改造的过程中,有很多工作是重复的、可以抽象的。例如:
- 多机多卡必然涉及运算节点间的通讯问题,将常用的通讯模式(p2p send / recv、AllReduce、Broadcast)、更优化的通讯方式(GPU Direct RDMA)等进行封装并优化是非常必要的;
- Scalable的多机多卡优化算法必然要涉及对传统SGD算法的修改,常用的方法包括ASGD、model averaging(MA)等,对这些优化算法进行封装将对极大的方便多机多卡改造;
- 多机多卡改造只是手段,其真正的目的是提供使用big data的能力。当训练数据上量以后,数据的流转、同步、caching等都需要统一的协调,计算任务的分配也需要scheduling,对这些支撑能力的封装也是有益的。
基于上述思考,我们把典型的基于GPU的大规模分布式机器学习算法增加一层抽象,称为“GPU多机多卡middleware”,并构建以下的hierarchy:
其中,“单GPU程序”就是现有的任意一个open source GPU训练工具,通过对它进行尽可能少的代码修改,调用GPU多机多卡middleware的API,即可很容易的获得通讯、流程控制、参数更新、数据分发、辅助调参5大支持,并通过middleware将强有力的分布式存储运算资源提供给单GPU程序,使之得到快速的大规模扩展。
例如,在上图的示例中,GPU多机多卡middleware可以帮助各GPU运算节点通过IB网络高速互联,并将存储于分布式共享存储中的数据(ODPS、OSS)源源不断送到运算节点。
为什么是“GPU多机多卡middleware”
我们选择开发GPU多机多卡middleware,而不是一个全能的多机多卡训练工具,是基于如下的设计理念:目前deep learning的研究和工程实践方兴未艾,各种新的模型结构、训练工具层出不穷,很难有一个“one size fits all”的工具同时满足所有人的需求。例如,在图像处理领域比较流行的caffe和cuda-convnet,在LSTM模型上比较流行的CURRENNT和RNNLib,都是各有各的优势与不足,并各有各的拥趸。更有意思的是,我们了解到很多用户在使用这些open source工具时,都或多或少对它们进行了自己的改造、升级与扩充,这样就产生了无数基于这些工具的变体。
但相同的是,这些林林总总的工具的变体在处理大数据时,都有将它们变身多机版、从而提高训练速度的需求。我们的GPU多机多卡middleware就基于这样的需求来设计抽象,使得以上的程序都可以通过插入middleware较快的实现基于ASGD或MA的多机多卡训练。对于用户来说,在插入middleware后,他们此前各自基于open source工具所做的独有修改都可以得以充分保留。他们熟悉的环境、已经生成的训练测试数据、乃至单机baseline都可以复用并与新的多机版本互相参照。一句话,middleware不是给你一个新的工具,而是将你手头熟悉的工具插上多机多卡的翅膀。
GPU多机多卡Middleware功能详解
GPU多机多卡middleware的主要功能是将GPU集群的硬件资源加以整合,提供通用的通讯、流程控制、数据分发、模型参数更新等模块,从而使得某个现成的单机版GPU程序通过较少的修改插入middleware后,就可以变身多机多卡程序。另外,考虑到集团内业务一般需要的模型大小,我们暂时只考虑data parallelism,暂不考虑model parallelism,这样就进一步简化了实现。
具体来说,GPU多机多卡middleware提供如下一些通用的基础功能,分别是:
- 通讯,通过包装MPI,提供计算节点之间p2p通讯(包括send / recv)和collective通讯(包括AllReduce等),并支持InfiniBand和GPU Direct RDMA以提高通讯效率。使得单机GPU程序不用考虑通讯的细节,通过简单调用middleware的通讯API即可实现高速多机通讯。
- 流程控制,主要用来控制整体机器学习的流程,协调各个GPU卡,实现data parallelism,包括什么时候进行参数更新,什么时候分发什么数据到哪个单卡GPU,什么时候调整参数等。
- 参数更新,负责从单卡GPU程序得到模型参数,然后在多机多卡之间进行参数更新。现在支持的有主流的MA (model averaging)以及ASGD (asynchronous stochastic gradient descent),只需要在需要的时候将模型参数交给Middleware,Middleware就会自动按照指定的模式完成参数更新。使得单机版GPU程序把自己算出的model parameters通过简单的调用middleware API即可完成模型的更新、同步。
- 数据分发,由于集团GPU集群现在不存在大规模分布式存储系统,而且在多机多卡训练之间也要有一定的数据分配机制,防止数据重复利用。数据分发正是用来输送训练数据到GPU卡,并实现智能的按需缓存,在运算的后台下载下一份训练数据,使得GPU不会“停工待料”。
- 辅助调参,负责在需要时控制所有机卡同时的进行各种参数调整,避免机卡训练参数不一致。
为了尽可能的不影响单卡训练程序的效率,middleware的大部分功能都是在后台用另外的线程实现。另外,为了节省机卡资源,middleware并没有单独占用一块GPU卡来进行参数交换,而是在需要的时候利用每个卡自身的一小部分资源进行参数交换。这一点看起来很简单,要基于MPI实现却并非像想象的那么简单。我们通过巧妙的通讯设计,实现了一个master-slave通讯模型,且在rank 0节点上同时运行master线程和slave线程,并正常通讯。
Middleware应用于语音识别DNN声学模型训练
我们在Kaldi的DNN训练工具上插入了middleware进行了大规模机器学习扩展,并在不同的机卡规模上测试了计算加速比,即训练同样多数据所花的时间。同时在3个epoch的训练数据后(这里3个epoch是我们baseline的配置),我们对比了模型的最终效果,如下表所示:
Frame Acc. (%) | CER (%) | 处理一个 epoch 所需时间(小时) | End-to-end 训练时间 | |
---|---|---|---|---|
单机单卡(Baseline) | 59.9 | 4.98 | 120.0 | 15天 |
4机8卡(Middleware) | 59.6 | 4.89 | 16.6 | 2天 |
8机16卡(Middleware) | 59.1 | 4.92 | 8.4 | 1 天 |
这里Frame Acc是帧准确率(越大越好),CER是字错误率(越小越好),都是我们用来检验DNN声学模型的客观指标,其中CER是最终的指标。我们可以看到,相对于单机单卡的Baseline,当我们增加机卡数目达到8机16卡时,整体3个epoch的训练时间可以从15天缩短到1天,而且最终的指标CER比Baseline还略好。也就是说,之前我们在单机单卡时两周才能迭代一次,而现在在8机16卡的情况下一周可以迭代7次了。
Middleware应用于自然语言处理LSTM模型训练
如何使机器看起来更像一个人?一方面是让机器可以向人一样出色的完成一些任务,另一个方面就是使机器能够自由的跟人对话。我们iDST-NLP团队的同学就开发过一个功能丰富的自由聊天引擎,业内的很多大公司也有一些类似的聊天引擎,比如Apple的Siri,微软的Cortana以及百度的小度,这些聊天引擎这些年正在变得越来越好,一方面是由于采用了大量的数据来训练背后的模型,另一方面是出现了更强大的模型,比如RNN/LSTM等。
RNN/LSTM等模型之所以能够更好的利用数据主要是因为考虑了前后相关信息,而这一点会使得模型训练速度变慢。虽然可以通过stream等方式进行一定程度的加速,但LSTM模型的训练仍然很痛苦。比如我们iDST-NLP团队的同学在一个不大的训练集上训练一个LSTM聊天模型,花费的时间就至少需要两天。一旦增加训练集,那么花费的时间也随之增加。因此,我们在Kaldi的LSTM模型训练工具上插入了Middleware,这里的训练工具实际上是Kaldi的变体,已经经过了我们相关同学在算法上的修改,在插入Middleware之后,并不会改变单卡程序的行为,能够最大限度保留用户的修改,减少重复建设。实际的训练效果如下表所示:
最好的Validation Acc. (%) | 处理一个 epoch 所需时间(小时) | |
---|---|---|
单机单卡 (Baseline) | 33.4 | 3.75 |
2机4卡(Middleware) | 34.5 | 1.12 |
具体的收敛曲线如下图所示:
我们可以看到,Baseline在第10个epoch才收敛,达到了33.2%,而Middleware版本的训练工具在第8个epoch收敛,达到了34.5%。通过对比两者计算一个epoch数据的时间,我们可以得到计算加速比为3.35,而如果按照Baseline在第10个epoch收敛来计算的话,收敛加速比可以达到3.74。至于为什么多机多卡工具反而比单机SGD收敛的更快更好,这主要是因为我们采用了ASGD来训练模型,并且利用middleware的辅助调参功能进行必要的参数调整,使得最终在更短的时间内用更少的epoch得到了更好的模型。
Middleware应用于语音识别BLSTM声学模型训练
LSTM模型考虑了单向的序列相关信息,如果要考虑双向的序列信息,那就是BLSTM模型了。这种模型训练更慢,但是对speech/NLP等领域而言,效果会更好。对自动语音识别来说,BLSTM是一种比较新的声学模型,业内很多公司还是停留在探索的阶段。我们用很短的时间,在团队同学修改过的Kaldi BLSTM训练工具上插入了middleware,大幅度提高了效率,具体效果如下:
CER(%) | 处理一个 epoch 所需时间(小时) | |
---|---|---|
单机单卡 (Baseline) | 12.9 | 12.3 |
4机8卡(Middleware) | 13.0 | 1.8 |
从上面的数据我们可以得到计算加速比为6.83倍,并且两者的收敛epoch数没有明显的区别(多机多卡版甚至需要更少的epoch就收敛了),因此收敛加速比应该是类似的。需要指出的是,我们这里的实验仅仅是在几百小时的数据集上的结果,如果是5000~10000小时的大数据集,那么一个epoch的时间将会是上百小时,需要一周左右的时间,如果是3个epoch就会是将近一个月,对快速迭代的业务来说已经不可接受。而采用4机8卡,一个epoch就只需要一天多,3个epoch不到4天时间;如果进一步增加机卡数目,加速比还会进一步的提高。
Middleware应用于Caffe图像分类AlexNet模型训练
我们还跟YunOS相册分类团队的同学合作,在他们修改过的Caffe上插入了middleware,使他们的Caffe迅速成为多机多卡版Caffe,并且完全保留他们之前所做的修改。
在图像分类领域,比较有名的数据集是ImageNet-1k,包含128万张图片,一般的训练需要进行几十个epoch。比如在AlexNet上官方的结果就是在第62个epoch上得到的。而这在单个Nvidia K40卡上一般需要5、6天的时间,这一点对于调整参数来说是很痛苦的。如果后续有更大规模的数据处理模型训练的需求,花费更多的时间几乎就是不可接受的。
在扩展中,我们发现如果让Caffe来动态的加载middleware分发的数据,会比较麻烦。因此我们采用的方法是预先处理得到各卡对应的数据,而不用Middleware数据分发功能,只使用Middleware参数更新功能和辅助调参功能。这样,几乎只需要对Caffe进行非常少量的修改就可以达到多机多卡扩展的目的。
但是,虽然工程上的工作不多,我们却需要花费一定的时间来调试参数。由于CNN对参数比较敏感,而且每次实验只有到最后才能知道最终的准确率,虽然我们加速了训练过程,整个调整参数的过程仍然是很痛苦的。
到现在,我们在官方验证集上得到的top1准确率的结果如下表所示:
Acc.(%) | 处理一个 epoch 所需时间(小时) | |
---|---|---|
官方Baseline(单机单卡) | 57.1 | 2.22 |
2机4卡(Middleware) | 55.5 | 0.625 |
因此,计算加速比为3.55,准确率为官方Baseline的97%,是第53个epoch的时候得到的。
总结及展望
我们通过GPU多机多卡middleware将我们用于语音识别的DNN、LSTM、BLSTM等单机版程序通通插上了多机多卡的翅膀,并每天在训练模型;我们用middleware帮助iDST-NLP团队将聊天LSTM模型训练变为多机多卡训练;我们用middleware和云OS同学合作,将他们的改版caffe变为多机多卡版,训练CNN进行相册分类……我们希望middleware能够插入更多的已有单机版程序,并实现更大的业务价值。