我们深知一款没应用的平台软件是缺乏生命力的,因此我们会陆续推出一些使用案例,一方面让大家更直观地了解MindSpore的应用场景,也能帮助用户端到端地上手。
l 首先看下案例效果:
该案例目前支持三种模式:随机生成、续写诗句和藏头诗,使用MindSpore官网提供的预训练checkpoint在古诗词数据集上Fine-tuning 20个epoch,可获得如下效果
随机生成:
大堤柳暗,春深树根。
东望一望,断回还家。
山色渐风雨,东风多雨禾。
无情与去,万里所思。
续写天下为公:
天下为公少,唯君北向西。
远山无路见,长水见人偏。
一路巴猿啸,千峰楚客啼。
幽深有诗策,无以话年华。
藏头诗人工智能:
人君离别难堪望,工部张机自少年。
智士不知身没处,能令圣德属何年。
l 性能数据:
BERT网络对于数据的利用率是偏低的,一句样本只使用了15%的信息用于训练,而BERT训练的数据集又是容易大量获取的,那么提高训练吞吐量就是一个显而易见的决定性问题。MindSpore将BERT模型作为benchmark网络之一进行调优,通过例如算子融合、集合通信优化等手动,从提高计算效率,降低通信开销等多个方面提高性能,同时配合硬件亲和的算子优化等方法,在单机8卡的标准昇腾服务器,实现了每秒处理超过2700条样本。实际使用过程中根据不同场景还可以利用梯度累加等手段进一步提升吞吐量。
具体测试环境及数据如下:
网络结构 | 运行环境 | 版本 | 吞吐量 |
L=24,H=1024, A=16 Seq_length=128 | 8 * Ascend910 | 0.7.0 | 2712.44 |
业界其他开源框架数据可参考
BERT For TensorFlow:github.com/NVIDIA/DeepL
本案例中,MindSpore使用了当前业界NLP领域的当红炸子鸡模型BERT:
图 1 BERT模型结构[1]
BERT模型结构如图 1所示,模型的输入是两“句”话,这里的一“句”话并不是以句号分隔,一段连续的话(包含若干个句号)都是一“句”话。以中文为例,一“句”话的每一个字都对应为一个token,会在首位插入[CLS],在两“句”话中间插入[SEP],这两个特殊标识位。
深度学习模型无法直接处理汉字或者单词,需要将汉字/单词映射为词向量,在BERT-base模型中,每一个token会被映射为一个768维度的向量,每一个汉字/单词都有其对应的词向量,此词向量也是需要被更新的参数。
如果只有一个词向量,那么输入中不同位置上的同样的字就会映射为相同的向量,所以需要加入其对应的位置信息。而位置信息包含两个部分,每个字所处的绝对位置以及这个字处于那一“句”话,会将这两个位置信息都映射为其对应的向量。所以每一个token对应的向量是
Input_vector = word_vector + position_vector + token_type_vector
BERT模型是基于Encoder 的结构,关于Transformer结构的详细介绍可以参考Jay Alammar的博客[2]。BERT-base模型是12层Encoder,词向量维度是768;BERT-large模型是24层的Encoder,词向量维度是1024,对应的参数量分别是1.1亿和3.4亿个。
每一个token对应的输入向量经过模型处理之后,得到一个对应的输出向量,维度保持不变,此输出向量包含了高维的语义信息,可以用来做后续任务。
l 模型的训练的两个阶段:Pre-training和Fine-tuning
Pre-training
Pre-training阶段会用海量的无标签数据进行自编码训练,期望通过海量的数据训练,让BERT学到人类语言的一般规律,因此训练任务的设计尤为重要。BERT中的Pre-training包含两项任务MLM(Masked Language Model)和NSP(Next Sentence Prediction)。
MLM任务是在输入时,随机将部分token置换为一个特殊标识符[MASK],然后由其对应的输出词向量预测此原始输入token。这个任务不需要额外的标签,而且迫使模型通过遮盖token的上下文推断出原始token,这样必须要理解一些语义信息,学习上下文的对应关系。
BERT的输入是两“句”相连的话A和B,NSP任务是以50%的概率随机替换掉B,使得A与B不是相连的两“句”话,然后由[CLS]对应的输出向量判断输入的两“句”话是否是相连的。在MLM基础上再增加一个NSP任务,是考虑到实际任务中并没有MLM这种任务,增加一个更符合实际任务类型的预训练任务。
从上述描述中可以看出,Pre-training并不需要任务数据标签,通过预训练阶段的任务设置,BERT可以从无标签数据中学到基础语义逻辑,然后配合Finetune过程完成特定任务训练。
Fine-tuning
在Pre-training中,并没有针对特定下游任务训练,会得到一个通用模型,此模型在不同下游任务上表现不尽相同,因此增加一个针对特定下游任务的Fine-tuning过程。Fine-tuning会使用特定下游任务的有标签的数据,进行少量训练。
通常情况下,Pre-training需要极大的数据与算力,而Fine-tuning是在相对小型的数据集上进行微调,大家可以在MindSpore官网下载BERT的预训练ckpt[3]。
l 模型改造:
BERT采用双向注意力机制即当前token可以看到它之前和之后的所有token,这样可以更好地理解整个输入的语义,从而可以更好地对输入做推断任务。然而对于生成式任务来说,当前token应当只能看到它之前的token,在之前token的基础上生成下一个token,因此需要对BERT模型的注意力机制进行调整。
BERT中是通过attention_mask来控制注意力机制,对应位置为1表示可以看到此token,默认attention_mask是全1的,即每个token之间都可以互相看到。要改为生成式模型,需要控制此attention_mask为下三角矩阵,即每个token只能看到自己及之前的token。
数据集为40000多首古诗词,取一首古诗词作为输入,经过BERT模型得到对应输出向量,训练任务设置为让每一个token的对应输出向量尽可能接近其下一个输入token,如图 2所示。当前设置[CLS]直接映射为第一个输入token其实略显草率,后续对此位置做优化调整。
图 2 训练流程示意图
如此一来,我们改造后的BERT模型就可以胜任智能写诗的任务了。在推理时,每次生成一个字,然后拼接起来作为下一次的输入,循环往复,直至生成完整诗句。
l 以Serving方式部署推理服务:
MindSpore目前还提供了部署推理服务—MindSpore Serving,这是一个轻量级、高性能的服务模块,旨在帮助MindSpore开发者在生产环境中高效部署在线推理服务。上述的智能写诗模型的训练及部署预测服务,具体流程如图 3所示。
图 3 训练及部署流程图
在云侧或本地服务器训练完毕后,可以将模型导出为MINDIR格式,然后通过MindSpore Serving在服务器侧启动部署推理服务,用户在网页上发出推理任务申请,通过MindSpore Serving在服务器侧完成推理并将生成的诗句返回给用户。
l 完整样例代码介绍:
模型相关代码及部署推理服务相关代码均可由此点击下载,代码结构如下
- └─BERT_poetry
- ├── src
- ├── BERT_for_pre_training.py # 封装BERT-Base正反向网络类
- ├── BERT_model.py # 定义BERT正向网络结构
- ├── finetune_config.py # Fine-tuning配置文件
- ├── fused_layer_norm.py # 定义fused_layer_norm
- ├── __init__.py # __init__
- ├── utils.py # 定义Fine-tuning正向网络结构
- ├── poetry_dataset.py # 部分代码取自[4],解析poetry.txt,生成所需dataset
- ├── vocab.txt # 词汇表
- ├── generator.py # 部分代码取自[4],推理生成诗句使用函数
- ├── poetry.py # 训练、推理、导出函数
- ├── serving
- ├── ms_serving # 启动服务器侧serving
- ├── BERT_flask.py # 服务器侧接收requests请求
- ├── poetry_client.py # 客户端代码
- ├── ms_service_pb2_grpc.py # 定义了grpc相关函数供BERT_flask.py使用
- └── ms_service_pb2.py # 定义了protocol buffer相关函数供BERT_flask.py使用
先在环境中安装BERT4keras[5],会用到其中的分词器Tokenizer和加载词表的函数load_vocab,使用的数据集可以从[4]中下载poetry.txt。
l 参考资料
[1] BERT:Pre-training of Deep Bidirectional Transformers for Language Understanding
[2] jalammar.github.io/illu
[3] https://www.MindSpore.cn/docs/zh-CN/master/network_list.html
[4] github.com/AaronJny/Dee
[5] github.com/bojone/BERT4