赞
踩
Torchserve 是 AWS 和 Facebook 推出的 pytorch 模型服务库,整体架构如下
torchserve架构图.png
特点
适用性
torchserve镜像
接口地址:http://localhost:8080/predictions/bert
传入参数:data 字段
参数格式:Torchserve传入数据为 json 格式
响应参数:字符串
使用步骤
安装 Java 依赖
TorchServe 由 Java 实现,因此需要最新版本的 OpenJDK 来运行
sudo apt install openjdk-11-jdk
安装 torchserve 及其依赖库
pip install torchserve torchvision torchtext torch-model-archiver torch-workflow-archiver
安装 Torchserve 最好的方法是使用docker镜像
docker pull pytorch/torchserve:latest
模型文件打包
在启动服务之前需要准备下列文件,以bert-ner任务为例
.pth/bin模型文件(必需)
保存bert模型文件,文件结构如下
bert_model
|____ config.json
|____ pytorch_model.bin
|____ vocab.txt
- import torch
- import torch.nn as nn
- from transformers import BertForTokenClassification,BertTokenizer
- from transformers import WEIGHTS_NAME, CONFIG_NAME
- import os
-
- model = BertForTokenClassification.from_pretrained("Bert/bert", num_labels = 7)
- model.load_state_dict(torch.load('Bert/model/Bert.pkl'))
- tokenizer = BertTokenizer.from_pretrained('Bert/bert')
- output_dir = "./bert_model/"
-
- model_to_save = model.module if hasattr(model, 'module') else model
- #如果使用预定义的名称保存,则可以使用`from_pretrained`加载
- output_model_file = os.path.join(output_dir, WEIGHTS_NAME)
- output_config_file = os.path.join(output_dir, CONFIG_NAME)
-
- torch.save(model_to_save.state_dict(), output_model_file)
- model_to_save.config.to_json_file(output_config_file)
- tokenizer.save_vocabulary(output_dir)
model.py(非必需):该文件负责定义模型结构
额外文件,bert模型需要依赖 config.json,vocab.txt文件
bert_model
|____ config.json
|____ vocab.txt
handle.py(必需):该文件需要负责数据处理以及模型推理,文件中必须要有执行的入口(entry point)。入口点只接受data和context参数,data为请求数据,context包含服务上下文信息,例如model_name,model_dir,manifest,batch_size,gpu 等。服务启动后将执行该入口点。入口点有两种实现方式
module level entry point:定义一个模块级函数作为执行的入口点,该函数可以有任何函数名称,但必须接受data,context参数并返回预测结果
- # Create model object
- model = None
-
- def entry_point_function_name(data, context):
- """
- Works on data and context to create model object or process inference request.
- Following sample demonstrates how model object can be initialized for jit mode.
- Similarly you can do it for eager mode models.
- :param data: Input data for prediction
- :param context: context contains model server system properties
- :return: prediction output
- """
- global model
-
- if not data:
- manifest = context.manifest
- properties = context.system_properties
- model_dir = properties.get("model_dir")
- device = torch.device("cuda:" + str(properties.get("gpu_id")) if torch.cuda.is_available() else "cpu")
-
- # Read model serialize/pt file
- serialized_file = manifest['model']['serializedFile']
- model_pt_path = os.path.join(model_dir, serialized_file)
- if not os.path.isfile(model_pt_path):
- raise RuntimeError("Missing the model.pt file")
-
- model = torch.jit.load(model_pt_path)
- else:
- #infer and return result
- return model(data)
class level entry point:定义一个类作为执行的入口点,类名任意,但必须包含initialize和 handle 类方法。handle方法只接受data,context
- class ModelHandler(object):
- """
- A custom model handler implementation.
- """
-
- def __init__(self):
- self._context = None
- self.initialized = False
- self.model = None
- self.device = None
-
- def initialize(self, context):
- """
- Invoke by torchserve for loading a model
- :param context: context contains model server system properties
- :return:
- """
-
- # load the model
- self.manifest = context.manifest
-
- properties = context.system_properties
- model_dir = properties.get("model_dir")
- self.device = torch.device("cuda:" + str(properties.get("gpu_id")) if torch.cuda.is_available() else "cpu")
-
- # Read model serialize/pt file
- serialized_file = self.manifest['model']['serializedFile']
- model_pt_path = os.path.join(model_dir, serialized_file)
- if not os.path.isfile(model_pt_path):
- raise RuntimeError("Missing the model.pt file")
-
- self.model = torch.jit.load(model_pt_path)
-
- self.initialized = True
-
-
- def handle(self, data, context):
- """
- Invoke by TorchServe for prediction request.
- Do pre-processing of data, prediction using model and postprocessing of prediciton output
- :param data: Input data for prediction
- :param context: Initial context contains model server system properties.
- :return: prediction output
- """
- pred_out = self.model.forward(data)
- return pred_out
以下为bert-ner任务的handle.py文件
- from abc import ABC
- import json
- import os
- from transformers import BertForTokenClassification
- import torch
- from ts.torch_handler.base_handler import BaseHandler
-
- class BertHandler(BaseHandler,ABC):
- def __init__(self) -> None:
- # 父类 BertHandler 提供了基本的数据处理方法,实际任务中可按需求重写
- super(BertHandler,self).__init__()
- # 导入vocab.txt
- self.vocab = self._load_vocab('vocab.txt')
- self.max_length = 100
- self.input_text = None
- self.initialized = False
-
- def initialize(self,ctx):
- '''初始化类成员,加载模型
- 参数 ctx : 服务系统设置,服务启动后自动传入,具体属性可参考BaseHandler源码
- '''
- # 模型文件及其依赖文件位置由model_dir属性指定,后续引用文件使用model_dir+filename
- properties = ctx.system_properties
- model_dir = properties.get("model_dir")
- # 加载模型
- self.model = BertForTokenClassification.from_pretrained(model_dir,num_labels = 7)
- if torch.cuda.is_available():
- self.model.cuda()
- self.model.eval()
- self.initialized = True
-
- def _load_vocab(self,vocab_file):
- vocab = {}
- index = 0
- with open(vocab_file, "r", encoding="utf-8") as reader:
- while True:
- token = reader.readline()
- if not token:
- break
- token = token.strip()
- vocab[token] = index
- index += 1
- return vocab
-
- def preprocess(self,data):
- '''获取响应数据,数据预处理
- 参数 data:请求数据,格式为json
- 返回:模型输入张量
- '''
- # 请求数据包含在body字段中
- preprocessed_data = data[0].get("body").get("data")
- text = preprocessed_data
- tokens = [i for i in text]
- if len(tokens) > self.max_length-2:
- tokens = tokens[0:(self.max_length-2)]
- self.input_text = tokens
- tokens_f =['[CLS]'] + tokens + ['[SEP]']
- input_ids = [int(self.vocab[i]) if i in self.vocab else int(self.vocab['[UNK]']) for i in tokens_f]
- while len(input_ids) < self.max_length:
- input_ids.append(0)
- token_list = torch.tensor([input_ids], dtype=torch.long)
- return token_list
-
- def inference(self,data):
- '''模型预测
- 参数 data:模型输入张量
- 返回:模型预测结果
- '''
- with torch.no_grad():
- if torch.cuda.is_available():
- model_output = self.model(data.cuda(), token_type_ids=None, attention_mask=(data>0).cuda(), labels=torch.tensor([0 * self.max_length]).cuda()).logits
- else:
- model_output = self.model(data, token_type_ids=None, attention_mask=(data>0), labels=torch.tensor([0 * self.max_length])).logits
-
- return model_output
-
- def postprocess(self,inference_output):
- '''处理模型输出
- 参数 inference_output:模型输出
- 返回:响应数据
- '''
- tag = torch.squeeze(inference_output)
- tag = torch.argmax(tag, dim=1)
- tag = tag[1:1+len(self.input_text)]
- tmp = ''
- postprocess_output = []
- for t in range(len(self.input_text)):
- if tag[t] == 0:
- pass
- elif tag[t] == 1:
- tmp += self.input_text[t]
- elif tag[t] == 2:
- tmp += self.input_text[t]
- if t==len(self.input_text)-1:
- postprocess_output.append(tmp)
- tmp = ''
- elif tag[t+1] == 4:
- postprocess_output.append(tmp)
- tmp = ''
- else:
- pass
- elif tag[t] == 3:
- tmp += self.input_text[t]
- postprocess_output.append(tmp)
- tmp = ''
- # torchserve支持批量推理,因此返回数据需要增加一个batchsize维度
- return [postprocess_output]
-
- def handle(self,data,context):
- if not self.initialized:
- self.initialize(context)
- if data is None:
- return None
- model_input = self.preprocess(data)
- model_output = self.inference(model_input)
- return self.postprocess(model_output)
在命令行中输入下列命令,将上述文件打包成模型存档bert.mar
- torch-model-archiver --model-name bert --version 1.0 \
- --serialized-file bert_model/pytorch_model.bin\
- --extra-files bert_model/vocab.txt \
- --handler handle.py
注:
- serialized-file有多个模型,extra-files有多个依赖文件可使用逗号隔开
- 如实际任务需提供多个接口,需要对应打包多个mar文件,通过 Inference API 指定模型名称调用
在工作区创建model_store文件夹,将 bert.mar文件移至model_store 文件夹
- mkdir model_store
- mv bert.mar model_store/
启动服务
torchserve --start --model-store model_store --models bert.mar
使用 Inference API 进行推理,默认端口号 8080
curl http://localhost:8080/predictions/bert -T 请求数据
或
- import requests
- res = requests.post("http://localhost:8080/predictions/bert",json=data)
使用 Management API 管理模型,默认端口号8081
模型注册与注销
服务启动后需要注册模型使之生效
curl -X POST "http://localhost:8081/models?url=bert.mar"
注销模型,使模型失效
curl -X DELETE http://localhost:8081/models/bert
分配workers
curl -v -X PUT "http://localhost:8081/models/bert?min_worker=3"
查看模型信息
curl "http://localhost:8081/models"
Triton推理服务能够托管/部署来自于本地存储或谷歌云平台的基于GPU或CPU的基础设施任何框架(无论是Teriton推理服务器能够托管/部署来自于本地存储或谷歌云平台的基于GPU或CPU的基础设施任何框架(无论是Tensorflow、TensorRT、Pytorch、Caffee、ONNX、Runtime的训练模型或一些自定义的框架)
特点
使用步骤
拉取镜像
docker pull nvcr.io/nvidia/tritonserver:21.05-py3
准备模型文件及依赖文件,按照下面的方式组织文件目录结构
- models/
- └── pytorch_model # 模型名字,需要和 config.pbtxt 中的名字对上
- ├── 1 # 模型版本号
- │ └── model.pt # 上面保存的模型
- ├── config.pbtxt # 模型配置文件,规定输入输出数据类型维度
- ├── extrafiles # 额外文件
配置文件config.pbtxt包含允许的输入/输出类型和形状,批次大小,版本控制,平台的详细信息,服务器不知道这些配置的详细信息,因此,将它们写入单独的配置文件中
- name: "bert"
- platform: "pytorch_libtorch"
- input [
- {
- name: "input__0"
- data_type: TYPE_INT32
- dims: [1, 100]
- } ,{
- output {
- name: "output__0"
- data_type: TYPE_FP32
- dims: [1, 100]
- }
启动服务
- docker run --rm --gpus all \
- -p8000:8000 -p8001:8001 -p8002:8002 \
- -v model_repository:/models \
- nvcr.io/nvidia/tritonserver:21.05-py3 \
- tritonserver --strict-model-config=false --model-repository=/models
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。