对 PyTorch BERT 模型进行微调,并将其部署到 Amazon SageMaker 上的 Amazon Elastic Inference

2020 年 9 月 20 日

对 PyTorch BERT 模型进行微调,并将其部署到 Amazon SageMaker 上的 Amazon Elastic Inference

Original URL: https://aws.amazon.com/cn/blogs/machine-learning/fine-tuning-a-pytorch-bert-model-and-deploying-it-with-amazon-elastic-inference-on-amazon-sagemaker/

文本分类,是一种将不同文本内容划分到对应类别的技术,其拥有广泛的应用范围:电子邮件服务商通过文本分类检测垃圾邮件,营销机构借此对客户评论进行情感分析,论坛版主则借此检测不当发帖等等。

以往,数据科学家使用 tf-idf , word2vec bag-of-words (BOW) 等方法,生成用于训练分类模型的特征。尽管这些技术在诸多自然语言处理(NLP)任务中获得了巨大成功,但在不同的上下文背景之下,其往往无法准确地捕捉单词含义。最近,随着基于 Transformers 的双向编码器表示(BERT)技术在结合实际上下文准确实现单词含义编码方面带来的突出表现,人们也希望借助 BERT 的力量在文本分类任务当中获得更理想的结果。

Amazon SageMaker 是一项全托管服务,能够为开发人员及数据科学家提供快速构建、训练并部署机器学习(ML)模型的能力。Amazon SageMaker 消除了 ML 流程中各个步骤带来的繁重工作,极大降低了高质量模型的开发门槛。Amazon SageMaker Python SDK 还提供开源 API 与容器,允许您更轻松地在 Amazon SageMaker 中使用多种不同 ML 与深度学习框架,实现模型的训练与部署作业。

我们的客户经常需要快速调优并轻松部署 NLP 模型。此外,客户也希望尽可能降低推理延迟与模型推理成本。 Amazon Elastic Inference 能够将 GPU 推理加速能力附加至 CPU 类型的终端端点当中,可以在不牺牲性能的前提下显著降低深度学习的推理成本。

本文将介绍如何使用 Amazon SageMaker 对 PyTorch BERT 模型进行微调,并将其部署在应用了 Elastic Inference 的 SageMaker 终端节点上。本文中使用的全部代码皆发布在 GitHub repo 之上。关于 BERT 微调的更多详细信息,请参阅 PyTorch BERT 调优教程

BERT 是什么?

BERT 最初发布于 2018 年 11 月,这是一种革命性的模型,能够主动屏蔽句子中的一个或者多个单词。BERT 将屏蔽过单词的句子作为输入,借此自我训练以预测被屏蔽的单词内容。此外,BERT 还能够应用于预测下一句的任务。

BERT 代表着一项重大突破,已经帮助业界研究人员及数据工程师在众多 NLP 任务中取得重大成果。BERT 提供的各个单词的表征能够切实与所处上下文(即句子中的其余部分)相匹配。关于 BERT 的更多详细信息,请参阅 BERT:用于语言理解的深度双向 Transformers 预训练模型

BERT 调优

数据科学家在 NLP 项目当中面临的最大挑战之一,在于缺乏训练数据。大家往往只能获得几千条带有人工标记的文本数据,用于模型训练。但是,现代深度学习 NLP 任务又需要大量标记数据,而解决此难题的一大重要方法,就是使用迁移学习技术。

迁移学习是一种 ML 方法,旨在将预训练完成的模型(比如用于图像分类的预训练 ResNet 模型)重新用作另一不同、但具有相关性的问题。通过复用预训练模型中的参数,我们可以节约大量的训练时间与成本。

BERT 是基于 BookCorpus 与英文维基百科的数据进行训练,二者分别包含 8 亿以及 25 亿个单词 [1]。从零开始训练 BERT 的成本极为高昂,但通过迁移学习,大家可以面对新的 场景用例时使用相关少量的训练数据对 BERT 进行快速微调,借此实现常见 NLP 任务(例如文本分类与问题解答)的高质量预测结果。

解决方案概述

在本文中,我们将分步介绍数据集、训练流程以及最终的模型部署环节。

我们使用 Amazon SageMaker notebook 实例用于代码运行。关于在 Amazon SageMaker 上使用 Jupyter notebooks 的更多详细信息,请参阅使用Amazon SageMaker notebook 实例,或者 Amazon SageMaker Studio 入门指南

本文中的 notebook 与代码皆发布于 GitHub 之上。您可以克隆 GitHub repo 并打开 Jupyter notebook 文件

问题与数据集

在本文中,我们使用语言可接受性语料库(CoLA),这是一套对从已出版语言学文献中收集到的10657 个英语句子进行符合语法与不符合语法标记的数据集。在我们的notebook 中,将使用以下代码下载并解压这些数据:

Python

复制代码
if not os.path.exists("./cola_public_1.1.zip"):
!curl -o ./cola_public_1.1.zip https://nyu-mll.github.io/CoLA/cola_public_1.1.zip
if not os.path.exists("./cola_public/"):
!unzip cola_public_1.1.zip

在训练数据中,我们只需要其中两列——句子本体及其标签:

Python

复制代码
df = pd.read_csv(
"./cola_public/raw/in_domain_train.csv",
sep="\t",
header=None,
usecols=[1, 3],
names=["label", "sentence"],
)
sentences = df.sentence.values
labels = df.label.values

如果我们输出部分句子,即可看到该数据集如何根据句子语法的完整性进行句子标记。具体参见以下代码:

Python

复制代码
print(sentences[20:25])
print(labels[20:25])
["The professor talked us." "We yelled ourselves hoarse."
"We yelled ourselves." "We yelled Harry hoarse."
"Harry coughed himself into a fit."]
[0 1 0 0 1]

接下来,我们对数据集进行拆分以进行训练与测试,而后将其上传至 Amazon S3 以供后续使用。SageMaker Python SDK 可帮助我们快速完成上传操作:

Python

复制代码
from sagemaker.session import Session
from sklearn.model_selection import train_test_split
train, test = train_test_split(df)
train.to_csv("./cola_public/train.csv", index=False)
test.to_csv("./cola_public/test.csv", index=False)
session = Session()
inputs_train = session.upload_data("./cola_public/train.csv", key_prefix="sagemaker-bert/training/data")
inputs_test = session.upload_data("./cola_public/test.csv", key_prefix="sagemaker-bert/testing/data")

训练脚本

在本文中,我们使用 PyTorch-Transformers 库。此库中包含用于 BERT 等多种 NLP 模型的 PyTorch 实现与预训练模型权重。详见以下代码:

Python

复制代码
model = BertForSequenceClassification.from_pretrained(
"bert-base-uncased", # Use the 12-layer BERT model, with an uncased vocab.
num_labels=2, # The number of output labels--2 for binary classification.
output_attentions=False, # Whether the model returns attentions weights.
output_hidden_states=False, # Whether the model returns all hidden-states.
)

根据 SageMaker PyTorch 镜像的规定,我们的训练脚本应将在训练过程中学习到的模型文件保存至文件路径model_dir。训练完成之后,Amazon SageMaker 将保存在model_dir中的模型文件上传至 Amazon S3 以进行下一步部署。脚本将使用以下代码保存训练得出的模型工件:

Python

复制代码
model_2_save = model.module if hasattr(model, "module") else model
model_2_save.save_pretrained(save_directory=args.model_dir)

我们将此脚本保存为 train_deploy.py文件,并将该文件放置在名为code/的目录当中。大家可以在该目录中查看完整的训练脚本。

由于 PyTorch-Transformer 本身并不包含在 Amazon SageMaker PyTorch 镜像当中,因此我们需要提供对应的requirements.txt文件,保证 Amazon SageMaker 能够安装该库以进行训练与推理。 requirements.txt文件属于文本文件,其中包含使用pip install进行安装的条目列表。您也可以指定需要安装的各条目的具体版本。要安装 PyTorch-Transformer,我们需要将以下行添加至 requirements.txt 文件当中。

Python

复制代码
transformers==2.3.0

您可以在 GitHub repo 上查看完整文件,也可以通过 code/目录进行查看。关于requirements.txt文件的更多详细信息,请参阅 Requirements 文件

在 Amazon SageMaker 上执行训练

我们使用 Amazon SageMaker 对我们的自定义 PyTorch 代码执行模型训练与部署。Amazon SageMaker Python SDK 能够极大降低在 Amazon SageMaker 中运行 PyTorch 脚本的难度。接下来,我们可以使用 SageMaker Python SDK 对经过训练的模型加以部署,并运行实际预测。关于将 SDK 与 PyTorch 配合使用的更多详细信息,请参阅将PyTorch 与SageMaker Python SDK 配合使用

首先,我们使用 PyTorch estimator 进行模型训练。在创建此 estimator 时,请注意明确指定以下内容:

  • entry_point – PyTorch 脚本的名称
  • source_dir – 训练脚本与 requirements.txt文件的位置
  • framework_version : 我们希望使用的 PyTorch 版本

PyTorch estimator 支持多机分布式 PyTorch 训练。要使用此功能,我们只需将train_instance_count的值设定为大于 1 即可。我们的训练脚本仅支持面向 GPU 实例进行分布式训练。

在估计器创建完成之后,我们调用fit()以启动一项训练作业。接下来,我们使用之前上传训练数据时获得的 Amazon S3 URI,详见以下代码:

Python

复制代码
from sagemaker.pytorch import PyTorch
estimator = PyTorch(
entry_point="train_deploy.py",
source_dir="code",
role=role,
framework_version="1.3.1",
py_version="py3",
train_instance_count=2,
train_instance_type="ml.p3.2xlarge",
hyperparameters={
"epochs": 1,
"num_labels": 2,
"backend": "gloo",
}
)
estimator.fit({"training": inputs_train, "testing": inputs_test})

在训练开始之后,Amazon SageMaker 会显示训练进度(如以下代码所示),具体包括轮次、训练损失以及测试数据精度:

Python

复制代码
2020-06-10 01:00:41 Starting - Starting the training job...
2020-06-10 01:00:44 Starting - Launching requested ML instances......
2020-06-10 01:02:04 Starting - Preparing the instances for training............
2020-06-10 01:03:48 Downloading - Downloading input data...
2020-06-10 01:04:15 Training - Downloading the training image..
2020-06-10 01:05:03 Training - Training image download completed. Training in progress.
...
Train Epoch: 1 [0/3207 (0%)] Loss: 0.626472
Train Epoch: 1 [350/3207 (98%)] Loss: 0.241283
Average training loss: 0.5248292144022736
Test set: Accuracy: 0.782608695652174
...

我们可以监控训练进度,请保证在继续进行 notebook 中的后续部分之前,确认训练流程已经成功完成。

部署脚本

在模型训练完成之后,我们通过在 PyTorch estimator 上调用 deploy 将模型托管在 Amazon SageMaker 终端节点之上。该终端节点将运行一套 Amazon SageMaker PyTorch 模型服务器。我们需要对此服务器中的两项组件加以配置:模型加载与模型服务。这两个组件的实现通过推理脚本train_deploy.py完成,完整文件可通过 GitHub repo 获取。

model_fn()函数用于加载已保存模型,并返回一个模型对象以供模型服务组件使用。SageMaker PyTorch 模型服务器通过调用model_fn加载我们的模型:

Python

复制代码
def model_fn(model_dir):
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = BertForSequenceClassification.from_pretrained(model_dir)
return model.to(device)

input_fn() 对预测输入进行反序列化与数据转换。在本用例中,我们的请求正文将首先被序列化为 JSON 格式,而后发送至模型服务端点。接下来,我们首先在 input_fn()中对 JSON 格式的请求正文进行反序列化,然后根据 BERT 的要求将输入以 torch.tensor的形式返回:

Python

复制代码
def input_fn(request_body, request_content_type):
if request_content_type == "application/json":
sentence = json.loads(request_body)
input_ids = []
encoded_sent = tokenizer.encode(sentence,add_special_tokens = True)
input_ids.append(encoded_sent)
# pad shorter sentences
input_ids_padded =[]
for i in input_ids:
while len(i) < MAX_LEN:
i.append(0)
input_ids_padded.append(i)
input_ids = input_ids_padded
# mask; 0: added, 1: otherwise
[int(token_id > 0) for token_id in sent] for sent in input_ids
# convert to PyTorch data types.
train_inputs = torch.tensor(input_ids)
train_masks = torch.tensor(attention_masks)
# train_data = TensorDataset(train_inputs, train_masks)
return train_inputs, train_masks

predict_fn() 执行预测并返回结果。详见以下代码:

Python

复制代码
def predict_fn(input_data, model):
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
model.eval()
input_id, input_mask = input_data
input_id.to(device)
input_mask.to(device)
with torch.no_grad():
return model(input_id, token_type_ids=None,attention_mask=input_mask)[0]

预构建的 Amazon SageMaker PyTorch 镜像中的默认支持对预测结果进行序列化。

部署端点

要部署我们的端点,需要在 PyTorch estimator 对象上调用deploy(),并提供所需数量的实例与实例类型:

Python

复制代码
predictor = estimator.deploy(initial_instance_count=1, instance_type="ml.m4.xlarge")

接下来,我们通过配置让预测变量使用"application/json"作为内容类型,而后将请求发送至我们的端点:

Python

复制代码
from sagemaker.predictor import json_deserializer, json_serializer
predictor.content_type = "application/json"
predictor.accept = "application/json"
predictor.serializer = json_serializer
predictor.deserializer = json_deserializer

最后,我们使用预测变量对象以调用该端点:

Python

复制代码
result = predictor.predict("Somebody just left - guess who.")
print(np.argmax(result, axis=1))
[1]

预测出的类别为 1,符合我们的预期,因为用于测试的句子确实拥有正确的语法表达。

使用 Elastic Inference 部署端点

要为推理任务选择正确的实例类型,我们需要在不同数量的 GPU、CPU 以及内存资源之间做出权衡。在独立 GPU 实例上针对其中某一种资源进行优化,往往会导致其他资源得不到充分利用。Elastic Inference 则能够为特定端点提供适量的 GPU 驱动型推理加速资源,解决了这一难题。自 2020 年 3 月起,用户已经可以在 Amazon SageMaker 与 Amazon EC2 上获得 Elastic Inference 对 PyTorch 的支持能力。

要使用 Elastic Inference,我们需要首先将训练完成的模型转换为 TorchScript。关于更多详细信息,请参阅使用Amazon Elastic Inference 在Amazon SageMaker for PyTorch 模型上降低ML 推理成本

我们首先从Amazon S3 处下载训练完成的模型文件。模型文件的位置为 estimator.model_data。接下来,我们使用以下代码将模型转换为 TorchScript:

Python

复制代码
model_torchScript = BertForSequenceClassification.from_pretrained("model/", torchscript=True)
device = "cpu"
for_jit_trace_input_ids = [0] * 64
for_jit_trace_attention_masks = [0] * 64
for_jit_trace_input = torch.tensor([for_jit_trace_input_ids])
for_jit_trace_masks = torch.tensor([for_jit_trace_input_ids])
traced_model = torch.jit.trace(
model_torchScript, [for_jit_trace_input.to(device), for_jit_trace_masks.to(device)]
)
torch.jit.save(traced_model, "traced_bert.pt")
subprocess.call(["tar", "-czvf", "traced_bert.tar.gz", "traced_bert.pt"])

要加载 TorchScript 模型并将其应用于实际预测,我们还需要对模型的加载与预测函数做出些许调整。我们需要创建一个新的脚本 deploy_ei.py,其内容与train_deploy.py脚本略有不同。

要加载模型,我们使用 torch.jit.load替代之前使用的 BertForSequenceClassification.from_pretrained调用:

Python

复制代码
loaded_model = torch.jit.load(os.path.join(model_dir, "traced_bert.pt"))

要进行预测,我们在最终 return 语句当中使用torch.jit.optimized_execution

Python

复制代码
with torch.no_grad():
with torch.jit.optimized_execution(True, {"target_device": "eia:0"}):
return model(input_id,attention_mask=input_mask)[0]

完整的 deploy_ei.py脚本可通过 GitHub repo 获取。使用这套脚本,我们即可通过 Elastic Inference 进行模型部署:

Python

复制代码
predictor = pytorch.deploy(
initial_instance_count=1,
instance_type="ml.m5.large",
accelerator_type="ml.eia2.xlarge"
)

通过使用 accelerator_type="ml.eia2.xlarge"参数,我们即可将 Elastic Inference 加速器附加至终端节点当中。

资源清理

在实验完成之后,请及时删除期间创建的 Amazon SageMaker 端点以及 Amazon SageMaker notebook 实例,以避免产生不必要的费用。具体参见以下代码:

Python

复制代码
predictor.delete_endpoint()

总结

在本文中,我们使用 Amazon SageMaker 以 BERT 为起点,训练出一套能够标记句子语法完整性的模型。接下来,我们将模型分别部署在使用 Elastic Inference 与不使用 Elastic Inference 的 Amazon SageMaker 终端节点。您也可以使用这套解决方案对 BERT 做其他方向的微调,或者使用PyTorch-Transformers 提供的其他预训练模型。关于将PyTorch 与Amazon SageMaker 配合使用的更多详细信息,请参阅将PyTorch 与Amazon SageMaker 配合使用

参考文献

[1] Yukun Zhu, Ryan Kiros, Rich Zemel, Ruslan Salakhutdinov, Raquel Urtasun, Antonio Torralba 以及 Sanja Fidler。2015 年。《书籍与电影的映射:在观看电影与阅读书籍中实现相似故事的视觉解释》,IEEE 国际计算机视觉会议论文集,第 19 至 27 页。

作者介绍

本篇作者

Qingwei Li

Amazon Web Services 机器学习专家。在超出研究补助预算却未能成功拿下预想中的诺贝尔奖之后,他开始转向运筹学领域。目前,他帮助金融服务与保险业客户在 AWS 上构建机器学习解决方案。在业余时间,他喜欢阅读和教学。

David Ping

AWS 首席解决方案架构师。他与我们的客户一道使用 AWS 构建云与机器学习解决方案。他住在纽约都会区,喜欢学习各类最新的机器学习技术。

Lauren Yu

Amazon SageMaker 软件开发工程师。她主要研究 SageMaker Python SDK,以及用于将 PyTorch、TensorFlow、MXNet 同 Amazon SageMaker 整合起来的工具包解决方案。业余时间,她喜欢在 Amazon 交响乐团与 Doppler Quartet 乐队中演奏中提琴。

本文转载自亚马逊 AWS 官方博客。

原文链接

对 PyTorch BERT 模型进行微调,并将其部署到 Amazon SageMaker 上的 Amazon Elastic Inference

2020 年 9 月 20 日 14:00 906

评论

发布
暂无评论
发现更多内容

作业二:根据当周学习情况,完成一篇学习总结

LN

分布式账本简介

程序那些事

区块链 分布式系统 区块链技术 hyperledger fabric

从软件架构说起

傻傻的帅

架构 架构要素 架构设计原则

ChaosBlade:从零开始的混沌工程(二)

郭旭东

云原生 混沌工程

架构师训练营第一周总结

极客大学架构师训练营

谈谈阿里云发布新一代容器、Serverless 等云原生产品

关贺宇

阿里云 容器 云原生 中间件

深圳各大知名办公园区引进 GoWork 智能楼宇管理系统,开启商业地产行业的春天

Geek_116789

架构师训练营第一周学习总结

hiqian

读笔 | 听说你也想辞职去摆摊?何不先收下这份秘籍

张鸱鸺

读书笔记 摆地摊 社会话题

架构师训练营第一周命题作业

兔狲

架构师训练营 - 第一周 - 食堂就餐卡系统设计

韩挺

Hyperledger Fabric基础知识

程序那些事

区块链 以太坊 超级账本 hyperledger fabric

架构师训练营第一周-食堂就餐卡系统设计

王铭铭

ZooKeeper核心原理及应用场景

古月木易

架构师训练营-第一周-食堂就餐卡系统设计

Anrika

架构师 极客大学架构师训练营

架构师训练营 - 第一周 - 学习总结

韩挺

作业一:食堂就餐卡系统设计

LN

IT自由职业者是怎么样的感受和体验

奈学教育

IT

IT自由职业者是怎么样的感受和体验

古月木易

IT职场

开启“观察者模式”,跳出灵魂看自己

小天同学

日常思考 个人感悟

第一周练习1 食堂就餐卡系统设计

王鑫龙

极客大学架构师训练营

架构师训练营-第一周学习总结

hellohuan

极客大学架构师训练营

Fabric的6大特性

程序那些事

区块链 blockchain 区块链技术 hyperledger fabric

食堂就餐卡系统设计

hellohuan

架构 极客大学架构师训练营

第一周学习总结:架构方法

晓雷

【架构师训练营】第1周作业2—学习总结

花生无翼

推荐几款基于 Markdown 语法在线制作简历的平台

JackTian

GitHub 网站 markdown 简历 工具软件

如何使用UML做需求分析与系统架构

柳旭

UML 架构文档

PostgreSQL权限控制

唯爱

干货|微服务线上生命周期管理

博文视点Broadview

容器 微服务 微服务架构 微服务冶理 架构师

ZooKeeper核心原理及应用场景

奈学教育

zookeeper

Milvus Community Conf 2020

Milvus Community Conf 2020

对 PyTorch BERT 模型进行微调,并将其部署到 Amazon SageMaker 上的 Amazon Elastic Inference-InfoQ