本章介绍如何发布模型和训练代码,以供他人使用。你可以通过软盘、U盘等把代码分享给他人,但该方法并不理想,可能许多年前这样就够了,但是现在不行。
更好的与他人共享代码与合作的方式是使用代码管理系统,如 Git。现在假设你已经学会使用 Git ,并且准备好代码,写好了文档,然后已经开源了项目。这样足够了吗?还不够,因为你在本机上写的代码不一定能运行在他人机器上,造成该问题的原因有很多。因此在发布代码时,最好可以复制机器环境,可以使用 Docker 容器来发布。
使用以下命令按照 Docker。
sudo apt install docker.io
sudo systemctl start docker
sudo systemctl enable docker
sudo groupadd docker
sudo usermod -aG docker $USER
这些命令在 Ubuntu 18.04 上可运行,Docker 的优点是可以装在任何机器上。
Docker 容器可以看作小型虚拟机,你可以给代码创建容器,然后所有人都可以获取并使用该容器。下边展示如何把之前训练 BERT 模型的代码打包成容器。
首先,你需要准备 Python 工程的依赖需求文件 requirements.txt 。然后创建 Docker 打包配置文件 Dockerfile。
FROM nvidia/cuda:10.1-cudnn7-runtime-ubuntu18.04
RUN apt-get update && apt-get install -y \
git curl ca-certificates python3 python3-pip sudo \
&& rm -rf /var/lib/apt/lists/*
RUN useradd -m abhishek
RUN chown -R abhishek:abhishek /home/abhishek/
COPY --chown=abhishek *.* /home/abhishek/app/
USER abhishek
RUN mkdir /home/abhishek/data/
RUN cd /home/abhishek/app/ && pip3 install -r requirements.txt
RUN pip3 install mkl
WORKDIR /home/abhishek/app
然后执行 Docker 打包命令 docker build -f Dockerfile -t bert:train . 。最终得到名为 bert:train 的镜像。
需要注意的是,容器具有自己的文件系统,因此要修改训练代码中的 HOME_DIR 以正确定位模型预训练参数文件。
接下来还得安装 NVIDIA docker runtime 才能运行该容器。
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key
add -
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidiadocker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list
sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit
sudo systemctl restart docker
接下来介绍如何将训练好的模型做为服务提供给终端用户。
最常见的 Python 构建服务方法是 Flask 框架。
import config, flask, time, torch
import torch.nn as nn
from flask import Flask, request
from model import BERTBaseUncased
app = Flask(__name__)
MODEL = None
DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'
def sentence_pred(sentence):
tokenizer = config.TOKENIZER
max_len = config.MAX_LEN
s = str(sentence)
s = " ".join(s.split())
inputs = tokenizer.encode_plus(
s, None, max_length=max_len
)
ids = inputs['input_ids']
mask = inputs['attention_mask']
token_type_ids = inputs['token_type_ids']
padding_len = max_len - len(ids)
ids = ids + ([0] * padding_len)
mask = mask + ([0] * padding_len)
token_type_ids = token_type_ids + ([0] * padding_len)
ids = torch.tensor(ids, dtype=torch.long).unsqueeze(0).to(DEVICE)
mask = torch.tensor(mask, dtype=torch.long).unsqueeze(0).to(DEVICE)
token_type_ids = torch.tensor(token_type_ids, dtype=torch.long).unsqueeze(0).to(DEVICE)
outputs = MODEL(ids=ids, mask=mask, token_type_ids=token_type_ids)
outputs = torch.sigmoid(outputs).cpu().detach().numpy()
return outputs[0][0]
@app.route('/predict', methods=['GET'])
def predict():
sentence = request.args.get('sentence')
start_time = time.time()
pred = sentence_pred(sentence)
response = {
'positive': str(pred),
'sentence': sentence,
'time': str(time.time() - start_time)
}
if __name__ == '__main__':
MODEL = BERTBaseUncased()
MODEL.load_state_dict(torch.load(
config.MODEL_PATH, map_location=torch.device(DEVICE)
))
MODEL.to(DEVICE)
MODEL.eval()
app.run(host='0.0.0.0')
执行该脚本即可启动服务。但是该服务一次只能处理一个请求,需要使用 gunicorn 库来提升请求处理数量。
在改造适配 gunicorn 之后便可将代码打包为 Docker 镜像,发布给其他人使用。
本章介绍了 Docker、Flask 构建服务、Gunicorn 提升请求数量,当然有许多没讲到的知识,但本章为你提供了一个良好的开端。
永远记得要保持代码文档完备且可用,这样别人使用你的代码时就不需要多次询问你,会帮你节省大量时间。