Estos contenidos se han traducido de forma automática para su comodidad, pero Huawei Cloud no garantiza la exactitud de estos. Para consultar los contenidos originales, acceda a la versión en inglés.
Actualización más reciente 2024-06-25 GMT+08:00

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

  1. 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

  2. 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.
  3. 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)
  4. 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"
      
       }

Ejemplo de script de inferencia de TensorFlow

A continuación se muestra un ejemplo de TensorFlow MnistService. Para obtener más ejemplos de código de inferencia TensorFlow consulte TensorFlow y TensorFlow 2.1.Para obtener más información sobre el código de inferencia de otros motores, consulte PyTorch y Caffe.
  • 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()