如何部署模型到ModelArts并远程调用 (三):编写推理代码
编写推理代码
推理代码中的四个方法
customize_service.py,只允许放置一个,customize_service.py
| 方法名 | 说明 | 
|---|---|
 __init__ | 
   初始化方法,该方法内加载模型及标签等(pytorch和caffe类型模型必须重写,实现模型加载逻辑)。 | 
 _preprocess(self, data) | 
   预处理方法,在推理请求前调用,用于将API接口用户原始请求数据转换为模型期望输入数据。 | 
 _inference(self, data) | 
   实际推理请求方法(不建议重写,重写后会覆盖modelarts内置的推理过程,运行自定义的推理逻辑) | 
 _postprocess(self, data) | 
   后处理方法,在推理请求完成后调用,用于将模型输出转换为API接口输出 | 
通常,用户需要重写_preprocess和_postprocess方法,以实现数据的API输入的预处理和推理结果输出的后处理。 pytorch和caffe类型模型必须重写初始化方法,实现模型加载逻辑。对于其他类的模型,初始化方法和推理接口已经封装在BaseService类中,不建议重写,因为重写不当可能会导致模型加载和推理失败,运行异常 。
所有自定义的python代码必须继承自BaseService类。不同类型的模型父类导入语句见此表格。
数据输入格式
content-type的接口传入,multipart/form-data”和“application/json”
样例一:TensorFlow2.1推理脚本示例
这是一个手写数字识别模型的图像分类的推理脚本,模型基于TensorFlow2.1。TensorFlow2.1的模型可以参考此示例以及其中的注释来编写推理代码。
import logging
import threading
import numpy as np
import tensorflow as tf # 导入tensorflow
from PIL import Image
from model_service.tfserving_model_service import TfServingBaseService
logger = logging.getLogger()
logger.setLevel(logging.INFO)
class mnist_service(TfServingBaseService): # 继承平台定义的tensorflow基类
    
    def __init__(self, model_name, model_path):
        '''模型初始化方法,直接复用即可。
        实际使用中,可以不写该方法,此处为了展示平台封装的细节。
        '''
        self.model_name = model_name
        self.model_path = model_path
        self.model = None
        self.predict = None
        # label.txt文件可以在这里加载,在后处理函数里使用,label.txt文件中记录的是类别编号和类别名称的映射。label.txt放在obs的模型包的目录
        with open(os.path.join(self.model_path, 'label.txt')) as f:
            self.label = json.load(f)
        # 非阻塞方式加载saved_model模型,防止阻塞超时
        thread = threading.Thread(target=self.load_model)
        thread.start()
    def load_model(self):
        '''加载模型,从model_path中加载模型
        实际使用中,可以不写该方法,此处为了展示平台封装的细节。
        '''
        # load saved_model 格式的模型
        self.model = tf.saved_model.load(self.model_path)
        signature_defs = self.model.signatures.keys()
        signature = []
        # only one signature allowed
        for signature_def in signature_defs:
            signature.append(signature_def)
        if len(signature) == 1:
            model_signature = signature[0]
        else:
            logging.warning("signatures more than one, use serving_default signature from %s", signature)
            model_signature = tf.saved_model.DEFAULT_SERVING_SIGNATURE_DEF_KEY
        self.predict = self.model.signatures[model_signature]
    def _preprocess(self, data):
        '''前处理方法
        
        输入参数:
        	data:API的输入数据,数据的格式在推理配置文件中定义。
        	在该样例中,data的输入格式定义为了form-data,可以以字典的格式读取
        	
        返回:
        	preprocessed_data:数据经过前处理之后的格式。
        '''
        images = []
        # 从data字典中逐个取出输入图片
        for k, v in data.items():
            # 逐个以文件格式读取图片
            for file_name, file_content in v.items():
                image1 = Image.open(file_content)
                image1 = np.array(image1, dtype=np.float32)
                image1.resize((28, 28, 1))
                images.append(image1)
        images = tf.convert_to_tensor(images, dtype=tf.dtypes.float32)
        preprocessed_data = images
        
        return preprocessed_data
    def _inference(self, data):
        '''推理方法
        
        输入参数:
        	data:前处理输出的数据
        	
        返回:
        	模型推理的结果。
        '''
        return self.predict(data)
    def _postprocess(self, data):
        '''后处理方法
        
        输入参数:
        	data:模型推理结果
        	
        返回:
        	模型推理结果经过后处理的结果。
        '''
        # 在此处,可以使用label.txt中的类别编号和名称映射信息。
        return {
            "result": int(data["output"].numpy()[0].argmax())
        }
样例二:sklearn推理脚本示例
该样例是基于sklearn框架的推理代码,基于sklearn的模型可以复用该代码。sklearn的模型可以参考此示例以及其中的注释来编写推理代码。
# coding:utf-8
import collections
import json
from sklearn.externals import joblib # 导入sklearn
from model_service.python_model_service import XgSklServingBaseService
class user_Service(XgSklServingBaseService):
    # request data preprocess
    def _preprocess(self, data):
        '''前处理方法
        
        输入参数:
        	data:API的输入数据,数据的格式在推理配置文件中定义。
        	在该样例中,data的输入格式定义为了json
        	
        返回:
        	list_data:数据经过前处理之后的格式。
        '''
        list_data = []
        # 以json格式加载数据,并用字典来保存
        json_data = json.loads(data, object_pairs_hook=collections.OrderedDict)
        for element in json_data["data"]["req_data"]:
            array = []
            for each in element:
                array.append(element[each])
                list_data.append(array)
        return list_data
    # predict
    def _inference(self, data):
        '''推理方法
        
        输入参数:
        	data:前处理输出的数据
        	
        返回:
        	pre_result:模型推理的结果。
        '''
        sk_model = joblib.load(self.model_path)
        pre_result = sk_model.predict(data)
        pre_result = pre_result.tolist()
        return pre_result
    # predict result process
    def _postprocess(self, data):
        '''后处理方法
        
        输入参数:
        	data:模型推理结果
        	
        返回:
        	resp_data:模型推理结果经过后处理的结果。
        '''
        resp_data = []
        for element in data:
            resp_data.append({"predict_result": element})
        return resp_data
其他样例
更多代码样例
通过几个特定领域的AI大赛的推理代码样例,学习推理代码的写法。
图像分类猫狗分类样例
https://github.com/huaweicloud/ModelArts-Lab/tree/master/train_inference/image_recognition/codes
其中,dog_and_cat_train.py是训练代码,另外两个是推理代码和推理配置文件。
图像分类大赛样例
物体检测大赛样例
即将发布。
语音分类样例
即将发布。
附:
如何部署模型到ModelArts并远程调用 (一):保存模型为平台支持的格式
如何部署模型到ModelArts并远程调用 (三):编写推理代码
如何部署模型到ModelArts并远程调用 (四):导入模型
如何部署模型到ModelArts并远程调用 (五):如何调用在线API服务
- 点赞
 - 收藏
 - 关注作者
 
            
           
评论(0)