本文经AI新媒体量子位(公众号ID:QbitAI)授权转载,转载请联系出处。4月9日,NVIDIAxQubit分享了一门nlp在线课程。NVIDIAGPU计算专家、FasterTransformer2.0的开发者之一薛博阳先生与上百位开发者共同探讨:FasterTransformer2.0的新特性介绍如何针对Decoder和Decoding进行优化如何使用Decoder和Decoding加速效果如何Decoder和Decoding能不能带?应读者要求,我们将分享的内容进行整理,与大家一起学习。文末有直播回放和PPT链接,可以直接观看。以下为本次分享内容:大家好,今天给大家介绍一下FasterTransformer2.0的原理和应用。什么是更快的变压器?首先参加本次直播的小伙伴们应该对Transformer的架构有一定的了解。这种架构是在论文“AttentionisAllYouNeed”中提出的。BERTEncoder中大量使用了Transformer,效果非常好。因此,Transformer成为NLP领域非常流行的深度学习网络架构。然而,Transformer的计算量通常非常大。因此,Transformer的延迟往往难以满足实际应用的需要。△AttentionisAllYouNeedScreenshotTransformer架构可以应用于Encoder或Decoder。在编码器中,Transformer包含1个多头注意力和1个前馈网络,在解码器中,它包含2个多头注意力和1个前馈网络。其中,纯Encoder架构在目前很多应用中都有不错的表现,比如问答系统、广告推荐系统等,因此对Encoder进行优化是非常有必要的。在某些场景下,比如翻译场景,我们需要Encoder和Decoder两种架构。在这种架构下,Decoder消耗的时间比例非常高,可能达到90%以上,是推理的主要瓶颈。因此,对Decoder的优化也是一项重要的工作,可以带来明显的加速效果。在实际应用中,FasterTransformer1.0版本对BERT中的Encoder做了很多优化和加速。2.0版本主要增加了对Decoder的优化,其优越的性能将助力翻译、对话机器人、文本补全纠错等各种生成场景。上表比较了Encoder和Decoder在计算量上的差异。当我们需要对一个句子进行编码和解码时,Encoder可以同时对很多词进行编码,甚至可以直接对一个句子进行编码。但是Decoder是一个解码过程,一次只能解码一个word。所以,在解码一个句子的时候,我们需要多个Decoder转发,这对GPU来说就更加不友好了。△FasterTransformer框架上图列出了FasterTransformer中针对BERT优化的模块。在编码方面,基于BERT,提供了相当于BERTTransformer的单层模块,供用户调用。当我们需要多层Transformer时,只需要多次调用Encoder即可。解码方面更复杂。为了平衡灵活性和效率,我们提供了两个不同大小和效果的模块:Decoder(黄色块)由单层Transformer层组成,包含两个attention和一个前馈网络;而Decoding(蓝色方块)除了多层Transformer层外,还包括其他功能,比如embedding_lookup、beamsearch、positionEncoding等。我们使用一个简单的虚拟代码来显示Decoder和Decoding之间的区别。Decoding中通常有两个终止条件。一是是否达到预设的最大序列长度,二是是否所有句子都翻译完毕,不终止循环继续。以句子长度为128的句子翻译场景为例,如果Decoder由6层Transformer层组成,Decoder总共需要调用128x6=768次;如果使用Decoding,Decoding只需要调用一次,所以Decoding的推理效率更高。总结首先,FasterTransformer提供了一个高度优化的Transformer层:Encoder是基于BERT实现的;Decoder以OpenNMT-TensorFlow开源库为标准;解码包括翻译的全过程,也是基于OpenNMT-TensorFlow。其次,FasterTransformer2.0底层由CUDA和cuBLAS实现,支持FP16和FP32两种计算模式,目前提供C++API和TFOP。现在,FasterTransformer2.0已经开源,您可以在NVIDIA/DeepLearningExamples大师的GitHub上的DeepLearningExamples/FasterTransformer/v2获取所有源代码。如何优化?我们以编码器为例。△TFEncoderTransformerlayerparameters:noXLA,batchsize1,12heads,sizeperhead64,FP32图中蓝色方块表示GPU实际在运行,空白的表示GPU空闲,所以GPU闲置了很多时间。GPU闲置的原因是内核太小,GPU必须不断闲置等待CPU启动内核。这也称为内核启动限制问题。如何解决这个问题呢?我们尝试开启TF的XLA,其他参数不变。图中我们可以看到,从原来计算1层Transformer层,需要50个kernel减少到24个左右。大部分kernel变宽了,虽然有speedup,但是idletime还是多了.因此,我们提出FasterTransformer来针对Encoder进行优化。首先,我们选择矩阵计算部分,使用NVIDIA高度优化的库cuBLAS进行计算。在其他部分,我们尽可能地集成可以融合的内核。最终结果如上图右侧所示。整体优化后,我们只需要8次矩阵计算加上6个核就可以完成单层Transformer层的计算,也就是说需要的核从24个减少到14个。可以看到优化后每个核都比较大,时间比例小的内核数量也减少了。但是还是有很多空白段。我们直接调用C++API,如图,GPU的空闲时间几乎没有了。因此,在小批量的情况下,我们建议使用C++API以获得更快的速度。当batchsize越大,GPU空闲时间就会越少。接下来,让我们看一下解码器。参数:无XLA,batchsize1,8heads,sizeperhead64,FP32经过统计,TF计算一个Transformer层需要用到70个左右的kernels。直观上看,体积很小,时间比很短的kernel比较多。因此,当batchsize比较小的时候,优化效果会更加明显。Decoder的优化和上面提到的Encoder是一样的。特别是Decoder中的矩阵计算量很小,所以我们用一个kernel来完成整个multi-headattention。经过优化,原来需要70个内核的计算只需要16个内核就可以完成。在较大的Decoding模块中,另一个占用较多时间的kernel是beamsearch。这里我们针对topk进行优化。在GPU中,可以同时执行多个block和多个warp,并行操作可以大大节省时间。△更多优化细节如何使用FasterTransformer?你可以在大师NVIDIA/DeepLearningExamplesGitHub根目录的DeepLearningExamples/FasterTransformer/v2中找到相应的信息:对于Decoder和Decoding,FasterTransformer提供了C++和TensorFlowOP接口。C++接口首先创建一个Eecoder,超参数如图所示:二、设置训练好的模型的权重;设置好后直接呼叫前转即可。TFOP接口首先,我们需要加载OP。这里我们以Decoder为例,它会自动创建TF需要使用的库。调用接口时,先导入.so文件(图中红色标注):然后调用Decoder,放入input、weight、hyperparameters,然后做一个sessionforoutputrun。这里需要注意的是参数中有一个虚拟输入(伪输入)。这个输入是为了避免TensorFlow的decoder和FasterTransformerDecoder并行,因为我们发现Decoder中的内存在并行执行的时候可能会被污染。在实际应用中,可以去掉这个输入。优化效果最后我们来看看优化效果如何。首先测试环境设置:使用的GPU是NVIDIA的TeslaT4和V100。TeslaV100中Encoder模块的结果超参数设置:12层,32序列长度,12个头,64sizeperhead(BERTbase),FP16下结果如上图,batchsize从100逐渐增加到500,FasterTransformer与开启XLA的TF相比,可以提供大约1.4倍的加速。TeslaT4中Decoder和Decoding模块结果超参数设置:Batchsize1,beamwidth4,8heads,64sizeperhead,6layers,vocabularysize30000,FP32结果如上图,不同序列长度下,对比对于TF,FasterTransformerDecoder可以带来3.4倍的加速效果,Decoding可以带来7-8倍的加速,效率更高。超参数设置:Batchsize256,sequencelength32,beamwidth4,8heads,64sizeperhead,6layers,vocabularysize30000结果如上图所示,当batchsize固定为较高值时,下不同的FP,FasterTransformerDecoder和Decoding也带来一定的加速效果。
