HuggingFace BERT模型部署和微调训练#
HuggingFace 是一个开源开放的AI社区平台,允许用户共享自己的AI项目、数据集和模型,同时也为用户提供了各种机器学习工具,包括transformers
、diffusers
、accelerate
等。通过HuggingFace社区,用户可以轻松地构建和训练自己的模型,并将其应用于各种实际场景中。
当前文档中,我们以HuggingFace提供的BERT预训练模型-英文-base预训练模型为示例,展示如何在PAI微调训练和部署BERT模型,主要内容包括以下:
SDK安装和配置:
安装所需的SDK,并完成PAI Python SDK配置。
直接部署BERT模型创建推理服务
将HuggingFace上的BERT模型直接模型部署到PAI-EAS,创建一个在线推理服务。
使用BERT模型微调训练
基于BERT模型,我们使用公共数据集进行微调训练,以获得一个可以用于情感分类的模型,然后将输出的模型部署到PAI-EAS,创建一个在线推理服务。
Step1: SDK的安装配置#
我们将使用PAI提供的Python SDK,提交训练作业,部署模型。请通过以下命令安装PAI Python SDK,以及需要使用到的Huggingface datasets等依赖库。
!python -m pip install --upgrade alipai
!python -m pip install datasets huggingface_hub
SDK需要配置访问阿里云服务需要的AccessKey,以及当前使用的工作空间和OSS Bucket。在PAI Python SDK安装之后,通过在命令行终端中执行以下命令,按照引导配置密钥,工作空间等信息。
# 以下命令,请在命令行终端中执行.
python -m pai.toolkit.config
我们可以通过执行以下代码验证当前的配置是否成功。
import pai
from pai.session import get_default_session
print(pai.__version__)
sess = get_default_session()
assert sess.workspace_name is not None
Step2: 部署BERT模型创建推理服务#
PAI-EAS (Elastic Algorithm Service) 是PAI平台上的模型在线预测服务,支持使用镜像模式部署模型,并且提供了常见的机器学习框架的推理镜像。 在以下示例中,我们将使用PAI-EAS提供的镜像,将HuggingFace上的BERT模型直接部署到PAI,创建一个在线推理服务。
BERT是Google提出的一种预训练语言模型,使用自监督学习方法在大型英文语料库上进行训练。他可以直接用于"完形填空"的任务,也可以作为下游任务的预训练模型,通过微调训练,用于分类,问答等不同的任务。我们通过以下代码下载HuggingFace提供的BERT模型,用于创建一个支持“完形填空”的推理服务。
对于如何在离线模式下保存和使用HuggingFace模型,用户可以参考HuggingFace的官方文档: HuggingFace Offline Mode
from huggingface_hub import snapshot_download
# 下载BERT模型(PyTorch版本)
model_dir = snapshot_download(
repo_id="bert-base-uncased",
local_dir="./bert",
allow_patterns=[
"config.json",
"pytorch_model.bin",
"vocab.txt",
"tokenizer_config.json",
"tokenizer.json",
],
)
用户也可以通过以下的方式保存模型(需要用户在本地installtransformers
, pytorch
等依赖库):
from transformers import BertTokenizer, BertModel
# 下载模型
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertModel.from_pretrained("bert-base-uncased")
# 保存模型到本地路径
model_dir = "./bert/"
model.save_pretrained(model_dir)
tokenizer.save_pretrained(model_dir)
保存的模型,可以直接通过transformers
库加载使用:
from transformers import BertTokenizer, BertModel
model = BertModel.from_pretrained("./bert/")
tokenizer = BertTokenizer.from_pretrained("./bert/")
将保存在本地的BERT模型和tokenizer上传到OSS Bucket,拿到模型的OSS路径。
from pai.common.oss_utils import upload
# 上传模型
bert_model_uri = upload(
source_path=model_dir, oss_path="huggingface/model/bert/", bucket=sess.oss_bucket
)
print(bert_model_uri)
在部署模型之前,我们需要准备模型推理服务的代码,用于加载模型,提供HTTP服务。在以下示例中,我们使用FastAPI编写了一个简单的HTTP服务,用于加载模型,提供预测服务。
# 创建推理服务使用的代码
!mkdir -p serving_src
完整的推理服务程序代码如下:
%%writefile serving_src/run.py
import os
import logging
import uvicorn, json, datetime
from fastapi import FastAPI, Request
from transformers import pipeline, AutoTokenizer, AutoModelForSequenceClassification
# 用户指定模型,默认会被加载到当前路径下
MODEL_PATH = "/eas/workspace/model/"
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("model_server")
app = FastAPI()
@app.post("/")
async def predict(request: Request):
global bert_pipeline
json_data = await request.json()
logger.info("Input data: %s", json_data)
result = bert_pipeline(json_data["text"])
logger.info("Prediction result: %s", result)
return result
if __name__ == '__main__':
task = os.environ.get("HF_TASK", "fill-mask")
bert_pipeline = pipeline(task=task, model=MODEL_PATH, tokenizer=MODEL_PATH)
uvicorn.run(app, host='0.0.0.0', port=int(os.environ.get("LISTENING_PORT", 8000)))
SDK 提供的 pai.model.InferenceSpec
用于描述如何加载模型,以及如何提供预测服务。在以下代码中,我们使用 pai.model.container_serving_spec
方法,使用 PAI 提供的推理镜像和本地代码 serving_src
,创建一个 InferenceSpec
对象。对应的本地代码会被上传保存到用户OSS,然后通过挂载的方式将相应的代码准备到运行容器中。
from pai.model import Model, container_serving_spec
from pai.image import retrieve, ImageScope
# 使用 PAI 提供的 PyTorch CPU 推理镜像
image_uri = retrieve(
"PyTorch",
framework_version="latest",
accelerator_type="CPU",
image_scope=ImageScope.INFERENCE,
).image_uri
print(image_uri)
# 构建一个使用镜像部署的InferenceSpec,可以用于BERT模型部署为推理服务.
bert_inference_spec = container_serving_spec(
# 模型服务的启动命令
command="python run.py",
# 模型服务依赖的代码
source_dir="./serving_src",
image_uri=image_uri,
requirements=[
"transformers",
"fastapi",
"uvicorn",
# 推理 pipeline 使用 device_map="auto" 时需要安装
"accelerate",
],
)
print(bert_inference_spec.to_dict())
模型部署#
通过构建Model,调用Model.deploy
方法,可以将模型部署到PAI-EAS,生成在线服务。
关于如何使用SDK部署模型的详细介绍,用户可以参考文档:PAI Python SDK部署推理服务
from pai.model import Model
from pai.common.utils import random_str
m = Model(
inference_spec=bert_inference_spec,
model_data=bert_model_uri,
)
p = m.deploy(
service_name="hf_bert_serving_{}".format(random_str(6)), # 推理服务名称.
instance_type="ecs.c6.xlarge", # 服务使用的机器实例规格: 4 vCPU, 8 GB
)
deploy方法返回的Predictor对象,指向了新创建的推理服务,他提供了.predict
方法,支持用户向推理服务发送预测请求。
res = p.predict(data={"text": "Hello, I'm a [MASK] model."})
print(res)
在测试完成之后,我们可以通过predictor.delete_service
删除推理服务,释放资源。
# 执行完成之后,删除对应的服务
p.delete_service()
Step3: Finetune BERT预训练模型#
BERT使用自监督学习方法在大型英文语料库上进行训练,他学习到了英语语言的内在表示,可以通过微调的方式,应用于不同的下游任务,从而获得更好的性能。在当前示例中,我们将使用Huggingface上 Yelp英文评论数据集yelp_review_full 对BERT模型进行微调,以获得一个可以用于情感分类的模型。
准备模型和数据集#
在当前步骤中,我们将准备微调训练使用的数据集,然后上传到OSS上供训练作业使用。
通过HuggingFace提供的transformers和datasets库可以使用读取本地文件的方式(离线模式),或是从HuggingFace Hub下载模型和数据的方式。为了提高训练作业的执行速度,我们在当前示例中,将模型和数据集准备到OSS,挂载到训练作业执行环境中,供训练作业直接加载使用。
from datasets import load_dataset
from pai.common.oss_utils import upload
data_path = "./train_data"
# 从HuggingFace Hub加载数据集
dataset = load_dataset("yelp_review_full")
# 保存到数据集,保存的数据集可以通过`datasets.load_from_disk`加载使用
dataset.save_to_disk(data_path)
train_data_uri = upload(
source_path=data_path,
oss_path="huggingface/dataset/yelp_review_full/",
bucket=sess.oss_bucket,
)
print(train_data_uri)
准备训练代码#
参考HuggingFace提供的对于Masked Language Model 的微调文档,我们编写了以下训练脚本,它将使用我们上传的数据集完成模型的微调。
# 创建代码保存目录
!mkdir -p train_src
在我们编写的训练作业脚本中,通过环境变量的方式获取训练作业的超参,输出数据,输出模型保存地址。对于PAI训练服务提供的环境变量的详细介绍,可以见文档:训练作业预置环境变量
完整的训练代码如下:
%%writefile train_src/finetune.py
import os
from datasets import load_dataset, load_from_disk
from transformers import AutoTokenizer, AutoModelForSequenceClassification, TrainingArguments, Trainer, DataCollatorWithPadding, HfArgumentParser
import numpy as np
import evaluate
def compute_metrics(eval_pred):
logits, labels = eval_pred
predictions = np.argmax(logits, axis=-1)
return metric.compute(predictions=predictions, references=labels)
def tokenize_function(examples):
return tokenizer(examples["text"], padding="max_length", truncation=True)
def train():
# 通过环境变量获取预训练模型地址, 训练数据,以及模型保存地址
model_name_or_path = os.environ.get("PAI_INPUT_MODEL", "bert-base-cased")
input_train_data = os.environ.get("PAI_INPUT_TRAIN_DATA")
output_dir=os.environ.get("PAI_OUTPUT_MODEL", "./output")
# 使用环境变量获取训练作业超参
num_train_epochs=int(os.environ.get("PAI_HPS_EPOCHS", 2))
save_strategy=os.environ.get("PAI_HPS_SAVE_STRATEGY", "epoch")
print("Loading Model...")
model = AutoModelForSequenceClassification.from_pretrained(model_name_or_path, num_labels=5)
tokenizer = AutoTokenizer.from_pretrained(model_name_or_path)
print("Loading dataset from disk...")
dataset = load_from_disk(input_train_data)
tokenized_datasets = dataset.map(lambda examples: tokenizer(examples["text"], padding="max_length", truncation=True, max_length=512),
batched=True)
data_collator = DataCollatorWithPadding(tokenizer)
small_train_dataset = tokenized_datasets['train'].shuffle(seed=42).select(range(1000))
small_eval_dataset = tokenized_datasets['test'].shuffle(seed=42).select(range(1000))
training_args = TrainingArguments(
output_dir=output_dir,
# 使用环境变量获取训练作业超参
num_train_epochs=num_train_epochs,
# 使用环境变量获取训练作业保存策略
save_strategy=save_strategy,
)
print("TrainingArguments: {}".format(training_args.to_json_string()))
metric = evaluate.load('accuracy')
print("Training...")
trainer = Trainer(
model=model,
args=training_args,
train_dataset=small_train_dataset,
eval_dataset=small_eval_dataset,
data_collator=data_collator,
tokenizer=tokenizer,
compute_metrics=compute_metrics,
)
trainer.train()
print("Saving Model...")
trainer.save_model()
if __name__ == "__main__":
train()
我们的训练作业将使用PAI提供的PyTorch镜像执行,需要在镜像中安装 transformers
和 evaluate
库才能够执行相应的训练脚本。通过在训练作业目录下提供 requirements.txt
文件,PAI的训练服务会自动安装指定的第三方依赖。
%%writefile train_src/requirements.txt
transformers
datasets
evaluate
提交训练作业#
通过PAI Python SDK提供的训练作业APIpai.estimator.Estimator
,我们可以将训练脚本提交到PAI执行。在以下代码中,我们将指定使用的训练代码 train_src
,使用PAI提供的PyTorch GPU镜像训练,提交运行微调训练作业。对于使用SDK提交训练作业的详细介绍,用户可以参考文档:PAI Python SDK提交训练作业。
from pai.huggingface.estimator import HuggingFaceEstimator
from pai.image import retrieve
# 使用 PAI 提供的 PyTorch GPU 训练镜像
image_uri = retrieve(
"PyTorch", framework_version="latest", accelerator_type="GPU"
).image_uri
# 配置训练作业
est = HuggingFaceEstimator(
command="python finetune.py", # 训练作业启动命令
source_dir="./train_src/", # 训练作业代码
instance_type="ecs.gn6i-c4g1.xlarge", # 训练使用的作业机器类型, 4 vCPU, 15 GB, 1* T4 GPU
transformers_version="latest",
hyperparameters={ # 训练作业超参,用户可以通过环境变量,或是
"save_strategy": "epoch",
"epochs": "1",
},
base_job_name="hf-bert-training",
)
# est = Estimator(
# image_uri=image_uri, # 训练作业使用的镜像
# command="python finetune.py", # 训练作业启动命令
# source_dir="./train_src/", # 训练作业代码
# instance_type="ecs.gn6i-c4g1.xlarge", # 训练使用的作业机器类型, 4 vCPU, 15 GB, 1* T4 GPU
# hyperparameters={ # 训练作业超参,用户可以通过环境变量,或是
# "save_strategy": "epoch",
# "epochs": "1",
# },
# base_job_name="hf-bert-training",
# )
print(est)
print(est.hyperparameters)
# 提交训练作业到PAI执行
# 提交之后SDK会打印作业URL,我们可以作业详情页查看训练日志,输出模型,资源使用情况等
est.fit(
# 作业使用的预训练模型和数据集使用inputs方式传递
# 相应的OSS URI会被挂载到作业环境中,用户可以通过 `PAI_INPUT_{ChannelNameUpperCase}` 的环境变量获取挂载后的路径
inputs={
"model": bert_model_uri,
"train_data": train_data_uri,
}
)
# 训练任务产出的模型地址
print(est.model_data())
部署Finetune获得的模型#
我们将复用以上推理服务的代码,将微调训练获得的模型部署到PAI-EAS,创建一个在线推理服务。
Note: 微调模型用于情感分析任务,我们显式得修改HuggingFace pipeline的Task参数。这里我们通过环境变量的方式传入Task参数。
from pai.model import Model, container_serving_spec
from pai.image import retrieve, ImageScope
# 使用 PAI 提供的 PyTorch CPU 推理镜像
image_uri = retrieve(
"PyTorch",
framework_version="latest",
accelerator_type="CPU",
image_scope=ImageScope.INFERENCE,
).image_uri
# 构建一个使用镜像部署的InferenceSpec,可以用于将以上产出的BERT模型部署为推理服务.
inference_spec = container_serving_spec(
# 模型服务的启动命令
command="python run.py",
# 模型服务依赖的代码
source_dir="./serving_src",
image_uri=image_uri,
requirements=[
"transformers",
"fastapi",
"uvicorn",
],
# 使用情感分析任务pipeline,通过环境变量的方式传递给到推理服务脚本。
environment_variables={"HF_TASK": "sentiment-analysis"},
)
print(inference_spec.to_dict())
from pai.model import Model
from pai.common.utils import random_str
# 使用训练作业产出的模型
model_data = est.model_data()
m = Model(
inference_spec=inference_spec,
model_data=model_data,
)
p = m.deploy(
service_name="hf_bert_ft_serving_{}".format(random_str(6)), # 推理服务名称
instance_type="ecs.c6.xlarge", # 服务使用的机器实例规格: 4 vCPU, 8 GB
)
通过Predictor向新创建的推理服务发送预测请求,获取模型预测结果。
res = p.predict({"text": "i am so happy today"})
print(res)
res = p.predict({"text": "i am so sad today"})
print(res)
在测试完成之后,我们通过predictor.delete_service
删除推理服务,释放资源。
# 执行完成之后,删除对应的服务
p.delete_service()