首页 > 最新资讯 > 如何用基于TensorRT的BERT模型实现实时自然语言理解?
如何用基于TensorRT的BERT模型实现实时自然语言理解?

如何用基于TensorRT的BERT模型实现实时自然语言理解?

2019-08-23 10:28

#人工智能 #深度学习


为了打造更具准确性的自然语言理解AI,如今已经有了诸如BERT、GPT-2和XL-Net等大规模语言模型被相继推出,这些模型的出现为打造各类自然语言理解(NLU)带来了飞跃性的突破。自2018年10月发布以来,BERT依旧是最常用的语言模型之一,并提供着最先进的准确性。

 
BERT的出现极大地推动了NLU的准确性,使各行业的企业都能够为客户提供优质的语言服务。然而,在实际的生产生活中,在应用这些模型时,除了准确性之外,延迟等因素也是需要被考虑的,这将直接影响用户对该语言服务的满意度。由于BERT具有12/24层叠加多头注意力网络,其推理过程需要大量的计算,而这也为BERT在实时应用中的部署带来了挑战。

 
NVIDIA近日针对BERT发布了全新优化版TensorRT,使其可以基于T4(立即免费试用) GPU,在短短2.2 毫秒内快速地完成推理任务,相比于仅采用CPU的平台,推理速度提升了17倍,同时也远低于会话AI应用所要求的10毫秒延迟闳值。这些优化功能让BERT的实际应用(例如,将BERT应用于会话AI服务中)变得切实可行。

 
作为一种高性能深度学习推理的平台,TensorRT包括一个优化器和运行时刻程序,可以最大限度地减少延迟,并最大限度地提高生产吞吐量。通过TensorRT,你可以优化所有主框架中训练的模型,以极高的准确性校准低精度,并最终部署在生产环境当中。

 
根据TensorRT示例报告显示,所有通过BERT实现此性能的优化程序和代码都已被开源发布。Transformer层是BERT编码器的一个基本构件,NVIDIA已对其进行了优化,以便于你能够将这些优化应用于任何基于BERT的自然语言理解任务。除了会话AI之外,BERT还能用于更广泛的语音和自然语言理解程序当中,所有这些应用都将从优化功能中获益良多。

 
问题回答或阅读理解是测试模型理解场景信息的一种常用方法。近几年来,在全球学术界和企业的共同努力下,问题回答能力已经得到了快速发展。本文将演示如何使用Python创建一个简单的问答应用程序,该应用程序由此前发布的TensorRT优化的BERT代码提供支持。该示例提供API,负责输入段落和问题;然后由BERT模型生成响应式回答。

 
首先,让我们简要回顾一下使用TensorRT进行BERT训练和推理的步骤。

 
BERT的训练和推理


NLP研究人员和开发者们面临的一个主要问题是在执行某一特定NLP任务时,缺少经过标记的训练数据。而NLP领域最近的一项突破完美地解决了这一难题,让大家可以使用未经过标记的文本进行NLP训练,并将NLP任务分解为两个部分:1)学习如何表示词语的含义及其之间的关系,即:利用辅助任务和大量的文本语料库建立语言模型;2)将语言模型用于一个相对较小的特定任务网络中,并在监督的情况下进行训练,从而将语言模型专门转化为实际任务。

 
这两个阶段通常被称为预训练和微调。通过该模式,开发者们可以将预训练的语言模型应用于广泛的任务当中,而无需针对任何特定任务更改模型架构。在我们的示例中,BERT提供了一个高质量的语言模型,经过微调后能够执行问答任务,同时也适用于其他任务,如:句子分类和情感分析。 

 
为了对BERT进行预训练,你可以从在线的预训练检查点(参见图1左边)开始,或者在自定义语料库上对BERT进行预训练(参见图1右边)。你还可以从检查点初始化预训练,然后针对自定义数据进行训练。虽然使用自定义数据或特定域的数据进行预培训可能会产生有趣的结果(例如BioBert),但它的计算量很大,需要大量并行计算基础设施才能在合理的时间内完成。支持GPU的多节点训练是此类场景的理想解决方案。

 
在微调步骤中,应基于经预训练的BERT语言模型的特定任务网络,采用特定任务的训练数据进行训练(用于问答的训练数据量应为三倍,包括段落、问题、答案)。请注意,与预训练相比,微调通常对于计算的要求通常要低得多。

 
运用问答型神经网络进行推理:

 
  1. 将经过微调的权重和网络定义传入TensorRT生成器,以创建TensorRT引擎;
  2. 使用此引擎启动TensorRT运行时刻程序;
  3. 向TensorRT运行时刻程序提供一段文章和一个问题,并获得网络预测的答案。


整个工作流程如图2所示。

 
图1:从预训练的检查点生成BERT TensortRT引擎

 
图2:利用TensorRT运行时刻引擎针对BERT QA任务执行推理的工作流

 
现在开始运行示例!


通过以下步骤设置你的环境以进行BERT推理:

 
  1. 创建具有前提条件的docker镜像
  2. 编译TensorRT优化插件
  3. 基于微调的权重创建TensorRT引擎
  4. 根据给定的段落和查询进行推理

 
我们使用脚本来执行这些步骤,具体步骤请参见TensorRT BERT示例报告。虽然我们提出了可用于每个脚本的若干选项,但你也可以在命令提示符下执行以下代码,从而快速启动:
 

# Clone the TensorRT repository and navigate to BERT demo directory

git clone --recursive https://github.com/NVIDIA/TensorRT && cd TensorRT/demo/BERT

 

# Create and launch the docker image

sh python/create_docker_container.sh

 

# Build the plugins and download the fine-tuned models

cd TensorRT/demo/BERT && sh python/build_examples.sh

 

# Build the TensorRT runtime engine

python python/bert_builder.py -m /workspace/models/fine-tuned/bert_tf_v2_base_fp16_384_v2/model.ckpt-8144 -o bert_base_384.engine -b 1 -s 384 -c /workspace/models/fine-tuned/bert_tf_v2_base_fp16_384_v2



现在,输入一个段落,通过提出几个问题,看看它能破译多少信息。
 

python python/bert_inference.py -e bert_base_384.engine -p "TensorRT is a high performance deep learning inference platform that delivers low latency and high throughput for apps such as recommenders, speech and image/video on NVIDIA GPUs. It includes parsers to import models, and plugins to support novel ops and layers before applying optimizations for inference. Today NVIDIA is open sourcing parsers and plugins in TensorRT so that the deep learning community can customize and extend these components to take advantage of powerful TensorRT optimizations for your apps." -q "What is TensorRT?" -v /workspace/models/fine-tuned/bert_tf_v2_base_fp16_384_v2/vocab.txt -b 1

 

Passage: TensorRTis a high performance deep learning inference platform that delivers low latency and high throughput for apps such as recommenders, speech and image/video on NVIDIA GPUsIt includes parsers to import models, and plugins to support novel ops and layers before applying optimizations for inference. Today NVIDIA is open sourcing parsers and plugins in TensorRT so that the deep learning community can customize and extend these components to take advantage of powerful TensorRT optimizations for your apps.

 

Question: What is TensorRT?

 

Answer:'a high performance deep learning inference platform'



给定同一篇文章,提出不同的问题。
 

Question: Whatis included in TensorRT?

 

Answer: 'parsers to import models, and plugins to support novel ops and layers before applying optimizations for inference'

 

根据所提供的段落内容,模型提供了准确的答案。该示例使用FP16精度执行TensorRT推理。这有助于实现NVIDIA GPU中Tensor 核心所能达到的最高性能。在此测试中,我们测试了TensorRT的精度,相当于精度为FP16的框架内推理。

 
现在,让我们重新审视一下该脚本中可用的选项。create_docker_container.sh脚本基于NGC中的TensorRT容器,并使用BERT示例中提供的Dockerfile构建docker镜像。它安装了所有必要的数据包,并启动创建的镜像bert_tensorrt,成为一个正常工作的容器。其执行脚本如下: 

 

sh create_docker_container.sh


在完成环境搭建后,下载BERT的微调权重。请注意,你不需要用预先训练的权重来创建TensorRT引擎(只是微调的权重)。除了微调权重之外,还可以使用关联的配置文件,该文件指定诸如注意头数量、层数等参数以及vocab.txt file文件,该文件包含了在训练过程中学习到的词汇。它们将通过自NGC下载的微调模型打包,并可利用build_examples.sh脚本下载。作为该脚本的一部分,你可以为要下载的BERT模型指定一组特定的微调权重。命令行参数控制稍后将用于模型构建和推理的具体BERT模型,其使用方式如下:


用法:sh build_examples.sh [base | large] [ft-fp16 | ft-fp32] [128 | 384]

 
  • base | large——确定是下载Bert-Base还是下载Bert-Large模型进行优化
  • ft-fp16 | ft-fp32——确定是下载精度FP16还是FP32的微调BERT模型
  • 128 | 384 —— 确定下载序列长度为128还是384的BERT模型

 
示例:
 

# Running with default parameters

sh build_examples.sh

 

# Running with custom parameters (BERT-large, FP132 fine-tuned weights, 128 sequence length)

sh build_examples.sh large ft-fp32 128


该脚本将首先使用示例报告中的代码,并构建用于BERT推理的TensorRT插件。接下来,它下载并安装NGC CLI,以便从NVIDIA NGC模型报告中下载经过微调的模型。build_examples.sh的命令行参数指定要使用TensorRT优化的模型。默认情况下,它下载经过微调的BERT-base,精度为FP16,序列长度为384。  

 
除了经过微调的模型之外,我们还使用配置文件列举模型参数,以及用于将BERT模型输出转换为文本答案的词汇文件。下载所选模型的模型和配置信息后,将为TensorRT构建BERT插件。这些插件的共享对象文件被放在BERT推理示例的build目录中。

 
接下来,我们就可以构建TensorRT引擎,并将其用于问答示例(即推理)。脚本bert_builder.py基于下载的BERT微调模型构建用于推理的TensorRT引擎。它使用在上一步中构建的定制TensorRT插件以及微调模型。请确保这个脚本的序列长度与已下载的模型序列长度匹配。按如下方式使用脚本: 

 
用法:python bert_builder.py -m -o -b -s -c

 
  • -m,–微调模型的检查点文件
  • -o,–输出TensoRT引擎文件(即bert.engine)的路径
  • -b,–用于推理的批处理程序大小(默认值=1)
  • -s,–与下载的BERT微调模型匹配的序列长度
  • -c,–包含BERT参数配置文件的目录(注意头、隐藏层等)

 
示例:
 

python python/bert_builder.py -m /workspace/models/fine-tuned/bert_tf_v2_base_fp16_384_v2/model.ckpt-8144 -o bert_base_384.engine -b -s 384 -c /workspace/models/fine-tuned/bert_tf_v2_base_fp16_384_v2

 


现在你应该已经有了一个TensorRT引擎(即bert.engine)用于问答的推理脚本(bert_inference.py)。我们将在后面的章节中阐述构建TensorRT引擎的过程。现在,你可以向bert_inference.py提供一个段落和一个查询,并查看模型是否能够正确地回答你的查询。与推理脚本交互的方法有几种:可以将段落和问题作为命令行参数提供(使用–passage和–question标志),也可以从给定文件传递(使用–passage_file和–question_file标志)。如果在执行过程中没有给出这两个标志,那么在执行开始后,将提示用户输入段落和问题。bert_inference.py脚本的参数如下: 

 
用法:python bert_inference.py --bert_engine [--passage | --passage_file] [--question | --question_file] --vocab_file --batch_size

 
  • -e, –bert_engine——上一步骤中创建的TensorRT引擎的路径
  • -p,–passage——Bert QA段落文本
  • -pf,–passage_file——包含段落文本的文件
  • -q,–question——用于BERT 问答查询/问题的文本
  • -qf,–question_file——包含查询/问题文本的文件
  • -v,–vocab__file——包含整个单词字典的文件
  • -b,–batch_size——用于推理的批处理程序大小

 
基于TensorRT进行BERT 推理


有关推理过程的逐步描述和演练,请参阅示例文件夹中的python脚本bert_inference.py和详细的Jupyter笔记本BERT_TRT.ipynb。在本节中,我们将回顾使用Tensorrt进行推理的一些关键参数和概念。  


BERT(更具体地说是编码器层)使用以下参数来控制其操作:

 
  1. 批处理程序大小
  2. 序列长度
  3. 注意力头文件数量

 
这些参数的值取决于所选的BERT模型,用于设置TensorRT计划文件(执行引擎)的配置参数。


对于每个编码器,还要指定隐藏层的数量和注意力头文件的大小。你还可以从TensorFlow检查点文件中读取所有上述参数。

 
由于我们使用的BERT模型已针对SQuAD数据集上的问题回答下游任务进行了微调,因此网络的输出(即输出完全连接层)将是一个文本范围,其中答案出现在段落中(在示例中称为h_output)。一旦我们生成了TensorRT引擎,我们就可以对其进行序列化,并在稍后与TensorRT运行时一起使用。  

 
在推理过程中,我们将内存从CPU复制到GPU,反之亦然,异步地将张量分别向GPU内存输入和输出。异步内存复制操作通过将计算与设备和主机之间的内存复制操作重叠来隐藏内存传输的延迟。异步内存复制和内核执行如图3所示。

 
图3:TensorRT运行时进程


如图3所示,BERT模型的输入包括:

 
  1. input_ids:带有段落的标记ids的张量,与用作推理输入的问题连接在一起;
  2. segment_ids:区分段落和问题;
  3. input_mask:表示序列中哪些元素是标记,哪些是填充元素。

 
输出(start_logits)和end_logits表示答案的范围,网络根据问题在段落内进行预测。

 
在Jupyter笔记本中,我们使用层的加速和高效实施,这些层作为TensorRT插件实施。请参阅TensorRT文档以了解有关TensorRT插件以及如何在TensorRT中导入自定义层的更多信息。

 
优化BERT以用于推理


让我们来浏览一下在TensorRT优化BERT中实施的关键优化。


BERT架构基于Transformer,由12个用于BERT-base和24个用于BERT-large的Transformer单元组成。在被Transformer处理之前,输入令牌通过一个嵌入层传递,该层查找它们的向量表示并编码它们在句子中的位置。每个Transformer单元由两个连续的残差块组成,每个残差块后接层归一化。第一残差块取代第一完全连接(FC)层并通过多头自注意力机制激活,第二残差块使用高斯误差线性单元(GELU)激活。图4说明了Transformer单元的结构。    

 
为了利用TensorRT对BERT进行优化,我们重点对Transformer单元进行了优化。由于在BERT中堆叠了多个Transformer单元,因此我们能够通过这组优化获得显著的性能提升。

 
为了在TensorRT中使用这些优化,我们使用自定义插件来加速BERT模型中Transformer编码器元素的关键操作。插件将多个操作融合到一个CUDA核心的子图中。每个子图由几个基本计算组成,每个计算都需要对GPU的全局内存(即设备内存中最慢的内存)进行读写。通过将基本操作融合到单个CUDA核心中,我们允许在以最少次数访问全局内存的情况下,在更大的子图上进行计算。让我们更详细地看看这些插件。

 
图4:通过TensorRT 对Transformer  BERT编码器单元进行优化


Gelu激活执行以下元素计算,其中a、b和c是一些标量常量:

gelu(x) = a * (1 + tanh* (x + c * x^3) ))

 

在TensorRT中使用基本层的简单实施需要以下操作:
 

Result=  x^3

Result c Result

Result=  x + Result

Result =  b * Result

Result = tanh(Result)

Result = x * Result

Result= a Result



对于k层,简单的实施将需要k-1不必要的全局内存往返,我们将其合并到单个CUDA核心中的元素计算中。有关详细信息,请参阅插件目录中的geluPlugin.cu。 

 
跳过层(Skip)和层归一化(Layer-Normalization,LN)层在每个Transformer层中出现两次,并融合在一个内核中。请参阅插件目录中skipLayerNormPlugin.cu中此融合操作的实施。  

 
Transformer中的自注意力机制(参见图5)基于使用完全连接(FC)层的输入嵌入计算查询(Q)、密钥(K)和值(V)的表示法。所有这些FC层的输入和输出尺寸都是B x S x (N * H),其中B是批处理程序大小,S是序列长度,N是注意力头文件数量,H是隐藏层大小。然后将每个FC层的输出转换成大小为B x N x S x H的结果矩阵。将3个FC层组合成一个更大的层会得到B x S x (3 * N * H)的输出尺寸。  

 
在融合的FC层之后,有三个转换操作可以融合成一个更大的转换操作,从而产生3 x B x N x S x H的输出尺寸。通过融合FC层,然后在更大的张量上执行单个转换,Q、K和V表示形式被连续地放置在内存中,用于后面的操作。这样将获得更快的内存访问,增加模型的吞吐量。 

 
 我们还融合了元素方式缩放和SoftMax层(参见图5a和5b的右侧)。


请参阅插件目录中的qkvToContextPlugin.cu了解自注意力的实现。

 
图5a:优化前的自注意力层

 
图5b:优化后的自注意力层

 
BERT推理性能标准化


BERT可以应用于线上用例和离线用例。线上NLU应用程序(例如:会话AI)在推理过程中会有严格的延迟要求。为了响应单个用户查询,需要按顺序执行多个模型。当用作服务时,客户的等待总时间包括了计算时间以及输入和输出的网络延迟。时间越长,客户体验越差。

 
虽然单个模型的确切延迟可能因应用程序而异,但多个实时应用程序需要语言模型在10 毫秒内执行。使用Tesla T4 GPU,经TensorRT优化的BERT可以在2.2 毫秒内完成问答任务的推理推理工作,该问答任务类似于SQuAD中批处理程序大小=1、序列长度=128的可用任务。使用TensorRT优化示例,你可以针对BERT-base执行最大批处理程序为8的操作,对于Transformer层较少的模型,可以在10毫秒延迟预算内执行更大的批处理程序的操作。然而,在一个只由CPU搭建的平台上,当批处理程序大小为1时,用高度优化的代码执行相同的任务需要40毫秒;而当采用更高的批处理程序大小时,平台没有完成运行,并且错误地退出了。

 
图6:计算在NVIDIAT4 GPU和仅有CPU的服务器上执行BERT-base的延迟(单位:毫秒)


该基准测试通过一个问答任务测量延迟。在此问答任务网络执行过程中,我们把通过张量作为输入,将logits作为输出,计算从输入到输出的过程中计算时间的延迟。你可以在sampleBERT.cpp中找到用于对示例进行基准测试的代码。

 
结论


NVIDIA正在逐步地将多种优化功能开源,使大家可以利用基于T4 GPU的TensorRT在2.2毫秒内完成BERT推理。这些优化代码在TensorRT开源报告中作为开源示例提供。如需在GCP上运行该示例,你可以从谷歌云人工智能中心访问。该报告演示了如何优化 Transformer层,Transformer层是当前BERT和其他几种语言模型的核心要素。我们希望你能够轻松地自定义这些要素,以适用于你的自定义模型和应用程序。本文概述了如何使用TensorRT示例、关键优化和性能结果。我们进一步阐述了如何将BERT示例用作简单应用程序和Jupyter笔记本的一部分工作流,你可以在其中传递一个段落并提出与之相关的问题。

 
这些新优化和新性能首次可将BERT实际用于要求低延迟的各种应用(例如,会话AI)。在接下来的几个月里,我们希望分享更多的示例,以展示如何将BERT用作其他工作流的一部分。


我们一直在寻找分享新示例和应用的新思路。你会将BERT用于哪些NLP应用?将来你又希望从我们这里了解到哪些示例?

 
如果你对TensorRT示例报告存在任何有疑问,请访问NVIDIA TensorRT开发者论坛,首先可查看TensorRT社区的其他成员是否提供了解决方案。也欢迎你登录https://developer.nvidia.com/nvidia-developer-program,参与NVIDIA Registered Developer Program项目,提交程序缺陷。  

相关新闻