Especificaciones para la codificación de inferencia de modelo
En esta sección se describe cómo compilar código de inferencia de modelo de ModelArts. Para obtener detalles sobre los ejemplos de código de script personalizados (incluidos ejemplos de código de inferencia) de motores de IA comunes, consulte Ejemplos de scripts personalizados. A continuación también se proporciona un ejemplo de código de inferencia para el motor TensorFlow y un ejemplo de personalización de la lógica de inferencia en un script de inferencia.
Especificaciones para compilar código de inferencia
- En el archivo de código de inferencia de modelo customize_service.py, agregue una clase de modelo hijo. Esta clase de modelo hijo hereda las propiedades de su clase de modelo padre. Para obtener más información sobre las instrucciones de importación de diferentes tipos de clases de modelo padre, consulte Tabla 1.
Tabla 1 Importar instrucciones de diferentes tipos de clases de modelo padre Tipo de modelo
Clase de padres
Instrucción de importación
TensorFlow
TfServingBaseService
from model_service.tfserving_model_service import TfServingBaseService
MXNet
MXNetBaseService
from mms.model_service.mxnet_model_service import MXNetBaseService
PyTorch
PTServingBaseService
from model_service.pytorch_model_service import PTServingBaseService
Caffe
CaffeBaseService
from model_service.caffe_model_service import CaffeBaseService
- Se pueden reescribir los siguientes métodos:
Tabla 2 Métodos a reescribir Método
Descripción
__init__(self, model_name, model_path)
Método de inicialización, que es adecuado para modelos creados basados en marcos de aprendizaje profundo. Los modelos y las etiquetas se cargan con este método. Este método debe ser reescrito para modelos basados en PyTorch y Caffe para implementar la lógica de carga del modelo.
__init__(self, model_path)
Método de inicialización, que es adecuado para modelos creados basados en marcos de aprendizaje automático. La ruta del modelo (self.model_path) se inicializa usando este método. En Spark_MLlib, este método también inicializa SparkSession (self.spark).
_preprocess(self, data)
Método de preproceso, que se llama antes de una solicitud de inferencia y se usa para convertir los datos de solicitud originales de una API en los datos de entrada esperados de un modelo
_inference(self, data)
Método de solicitud de inferencia. No se recomienda reescribir el método porque una vez que se reescribe el método, el proceso de inferencia integrado de ModelArts se sobrescribirá y se ejecutará la lógica de inferencia personalizada.
_postprocess(self, data)
Método de posprocesamiento, que se llama después de completar una solicitud de inferencia y se utiliza para convertir la salida del modelo en la salida de la API
- Puede elegir reescribir los métodos de preproceso y postproceso para implementar el preprocesamiento de la entrada de API y el postprocesamiento de la salida de inferencia.
- La reescritura del método init de la clase de modelo padre puede hacer que una aplicación de IA se ejecute de forma anormal.
- El atributo que se puede utilizar es la ruta local donde reside el modelo. El nombre del atributo es self.model_path. Además, los modelos basados en PySpark pueden usar self.spark para obtener el objeto SparkSession en customize_service.py.
Se requiere una ruta absoluta para leer archivos en el código de inferencia. Puede obtener la ruta local del modelo desde el atributo self.model_path.
- Cuando se utiliza TensorFlow, Caffe, o MXNet, self.model_path indica la ruta del archivo modelo. Vea el siguiente ejemplo:
# Store the label.json file in the model directory. The following information is read: with open(os.path.join(self.model_path, 'label.json')) as f: self.label = json.load(f)
- Cuando se utiliza PyTorch, Scikit_Learn o PySpark, self.model_path indica la ruta del archivo modelo. Vea el siguiente ejemplo:
# Store the label.json file in the model directory. The following information is read: dir_path = os.path.dirname(os.path.realpath(self.model_path)) with open(os.path.join(dir_path, 'label.json')) as f: self.label = json.load(f)
- Cuando se utiliza TensorFlow, Caffe, o MXNet, self.model_path indica la ruta del archivo modelo. Vea el siguiente ejemplo:
- data importados a través de la API para el procesamiento previo, la solicitud de inferencia real y el procesamiento posterior pueden ser multipart/form-data o application/json.
- Solicitud de multipart/form-data
curl -X POST \ <modelarts-inference-endpoint> \ -F image1=@cat.jpg \ -F images2=@horse.jpg
Los datos de entrada correspondientes son los siguientes:
[ { "image1":{ "cat.jpg":"<cat..jpg file io>" } }, { "image2":{ "horse.jpg":"<horse.jpg file io>" } } ]
- Solicitud de application/json
curl -X POST \ <modelarts-inference-endpoint> \ -d '{ "images":"base64 encode image" }'
El dato de entrada correspondiente es python dict.
{ "images":"base64 encode image" }
- Solicitud de multipart/form-data
Ejemplo de script de inferencia de TensorFlow
- Código de inferencia
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
from PIL import Image import numpy as np from model_service.tfserving_model_service import TfServingBaseService class MnistService(TfServingBaseService): def _preprocess(self, data): preprocessed_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((1, 784)) preprocessed_data[k] = image1 return preprocessed_data def _postprocess(self, data): infer_output = {} for output_name, result in data.items(): infer_output["mnist_result"] = result[0].index(max(result[0])) return infer_output
- Solicitud
curl -X POST \ Real-time service address \ -F images=@test.jpg
- Respuesta
{"mnist_result": 7}
El ejemplo de código anterior cambia el tamaño de las imágenes importadas al formulario del usuario para adaptarse a la forma de entrada del modelo. La imagen 32x32 se lee de la biblioteca Pillow y se redimensiona a 1x784 para que coincida con la entrada del modelo. En el procesamiento posterior, convierta la salida del modelo en una lista para mostrar la API RESTful.
Ejemplo de Script de Inferencia XGBoost
Para obtener más información sobre el código de inferencia de otros motores de aprendizaje automático, consulte PySpark y Scikit Learn.
# coding:utf-8 import collections import json import xgboost as xgb from model_service.python_model_service import XgSklServingBaseService class UserService(XgSklServingBaseService): # request data preprocess def _preprocess(self, data): list_data = [] 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): xg_model = xgb.Booster(model_file=self.model_path) pre_data = xgb.DMatrix(data) pre_result = xg_model.predict(pre_data) pre_result = pre_result.tolist() return pre_result # predict result process def _postprocess(self, data): resp_data = [] for element in data: resp_data.append({"predict_result": element}) return resp_data
Ejemplo de Script de Inferencia de la Lógica de Inferencia Personalizada
Primero, defina un paquete de dependencias en el archivo de configuración. Para más detalles, consulte Ejemplo de un archivo de configuración de modelo que utiliza un paquete de dependencia personalizado. A continuación, utilice el siguiente ejemplo de código para implementar la carga y la inferencia del modelo en formato saved_model.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
# -*- coding: utf-8 -*- import json import os import threading import numpy as np import tensorflow as tf from PIL import Image from model_service.tfserving_model_service import TfServingBaseService import logging logger = logging.getLogger(__name__) class MnistService(TfServingBaseService): def __init__(self, model_name, model_path): self.model_name = model_name self.model_path = model_path self.model_inputs = {} self.model_outputs = {} # The label file can be loaded here and used in the post-processing function. # Directories for storing the label.txt file on OBS and in the model package # with open(os.path.join(self.model_path, 'label.txt')) as f: # self.label = json.load(f) # Load the model in saved_model format in non-blocking mode to prevent blocking timeout. thread = threading.Thread(target=self.get_tf_sess) thread.start() def get_tf_sess(self): # Load the model in saved_model format. # The session will be reused. Do not use the with statement. sess = tf.Session(graph=tf.Graph()) meta_graph_def = tf.saved_model.loader.load(sess, [tf.saved_model.tag_constants.SERVING], self.model_path) signature_defs = meta_graph_def.signature_def self.sess = sess signature = [] # only one signature allowed for signature_def in signature_defs: signature.append(signature_def) if len(signature) == 1: model_signature = signature[0] else: logger.warning("signatures more than one, use serving_default signature") model_signature = tf.saved_model.signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY logger.info("model signature: %s", model_signature) for signature_name in meta_graph_def.signature_def[model_signature].inputs: tensorinfo = meta_graph_def.signature_def[model_signature].inputs[signature_name] name = tensorinfo.name op = self.sess.graph.get_tensor_by_name(name) self.model_inputs[signature_name] = op logger.info("model inputs: %s", self.model_inputs) for signature_name in meta_graph_def.signature_def[model_signature].outputs: tensorinfo = meta_graph_def.signature_def[model_signature].outputs[signature_name] name = tensorinfo.name op = self.sess.graph.get_tensor_by_name(name) self.model_outputs[signature_name] = op logger.info("model outputs: %s", self.model_outputs) def _preprocess(self, data): # Two request modes using HTTPS # 1. The request in form-data file format is as follows: data = {"Request key value":{"File name":<File io>}} # 2. Request in JSON format is as follows: data = json.loads("JSON body transferred by the API") preprocessed_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((1, 28, 28)) preprocessed_data[k] = image1 return preprocessed_data def _inference(self, data): feed_dict = {} for k, v in data.items(): if k not in self.model_inputs.keys(): logger.error("input key %s is not in model inputs %s", k, list(self.model_inputs.keys())) raise Exception("input key %s is not in model inputs %s" % (k, list(self.model_inputs.keys()))) feed_dict[self.model_inputs[k]] = v result = self.sess.run(self.model_outputs, feed_dict=feed_dict) logger.info('predict result : ' + str(result)) return result def _postprocess(self, data): infer_output = {"mnist_result": []} for output_name, results in data.items(): for result in results: infer_output["mnist_result"].append(np.argmax(result)) return infer_output def __del__(self): self.sess.close() |