API RESTful

Además de las API gRPC, TensorFlow ModelServer también es compatible con las API RESTful. Esta página describe estos puntos finales de API y un ejemplo de extremo a extremo sobre el uso.

La solicitud y la respuesta es un objeto JSON. La composición de este objeto depende del tipo de petición o verbo. Consulte las secciones específicas de la API a continuación para obtener más información.

En caso de error, todas las API devolverán un objeto JSON en el cuerpo de la respuesta con error como clave y el mensaje de error como valor:

{
  "error": <error message string>
}

API de estado del modelo

Esta API sigue de cerca la API gRPC de ModelService.GetModelStatus . Devuelve el estado de un modelo en ModelServer.

URL

GET http://host:port/v1/models/${MODEL_NAME}[/versions/${VERSION}|/labels/${LABEL}]

Incluir /versions/${VERSION} o /labels/${LABEL} es opcional. Si se devuelve el estado omitido para todas las versiones en la respuesta.

Formato de respuesta

Si tiene éxito, devuelve una representación JSON del protobuf GetModelStatusResponse .

API de metadatos del modelo

Esta API sigue de cerca la API gRPC de PredictionService.GetModelMetadata . Devuelve los metadatos de un modelo en ModelServer.

URL

GET http://host:port/v1/models/${MODEL_NAME}[/versions/${VERSION}|/labels/${LABEL}]/metadata

Incluir /versions/${VERSION} o /labels/${LABEL} es opcional. Si se omite, los metadatos del modelo para la última versión se devuelven en la respuesta.

Formato de respuesta

Si tiene éxito, devuelve una representación JSON del protobuf GetModelMetadataResponse .

API de clasificación y regresión

Esta API sigue de cerca los métodos Classify y Regress de PredictionService gRPC API.

URL

POST http://host:port/v1/models/${MODEL_NAME}[/versions/${VERSION}|/labels/${LABEL}]:(classify|regress)

Incluir /versions/${VERSION} o /labels/${LABEL} es opcional. Si se omite, se utiliza la última versión.

Formato de solicitud

El cuerpo de la solicitud para las API classify y regress debe ser un objeto JSON con el formato siguiente:

{
  // Optional: serving signature to use.
  // If unspecifed default serving signature is used.
  "signature_name": <string>,

  // Optional: Common context shared by all examples.
  // Features that appear here MUST NOT appear in examples (below).
  "context": {
    "<feature_name3>": <value>|<list>
    "<feature_name4>": <value>|<list>
  },

  // List of Example objects
  "examples": [
    {
      // Example 1
      "<feature_name1>": <value>|<list>,
      "<feature_name2>": <value>|<list>,
      ...
    },
    {
      // Example 2
      "<feature_name1>": <value>|<list>,
      "<feature_name2>": <value>|<list>,
      ...
    }
    ...
  ]
}

<value> es un número JSON (completo o decimal), una cadena JSON o un objeto JSON que representa datos binarios (consulte la sección Codificación de valores binarios a continuación para obtener más detalles). <list> es una lista de tales valores. Este formato es similar a los prototipos ClassificationRequest y RegressionRequest de gRPC. Ambas versiones aceptan una lista de objetos Example .

Formato de respuesta

Una solicitud classify devuelve un objeto JSON en el cuerpo de la respuesta, con el siguiente formato:

{
  "result": [
    // List of class label/score pairs for first Example (in request)
    [ [<label1>, <score1>], [<label2>, <score2>], ... ],

    // List of class label/score pairs for next Example (in request)
    [ [<label1>, <score1>], [<label2>, <score2>], ... ],
    ...
  ]
}

<label> es una cadena (que puede ser una cadena vacía "" si el modelo no tiene una etiqueta asociada con la partitura). <score> es un número decimal (coma flotante).

La solicitud regress devuelve un objeto JSON en el cuerpo de la respuesta, con el siguiente formato:

{
  // One regression value for each example in the request in the same order.
  "result": [ <value1>, <value2>, <value3>, ...]
}

<value> es un número decimal.

Los usuarios de la API de gRPC notarán la similitud de este formato con los prototipos ClassificationResponse y RegressionResponse .

Predecir API

Esta API sigue de cerca la API PredictionService.Predict gRPC.

URL

POST http://host:port/v1/models/${MODEL_NAME}[/versions/${VERSION}|/labels/${LABEL}]:predict

Incluir /versions/${VERSION} o /labels/${LABEL} es opcional. Si se omite, se utiliza la última versión.

Formato de solicitud

El cuerpo de la solicitud para la API predict debe tener el formato de objeto JSON de la siguiente manera:

{
  // (Optional) Serving signature to use.
  // If unspecifed default serving signature is used.
  "signature_name": <string>,

  // Input Tensors in row ("instances") or columnar ("inputs") format.
  // A request can have either of them but NOT both.
  "instances": <value>|<(nested)list>|<list-of-objects>
  "inputs": <value>|<(nested)list>|<object>
}

Especificación de tensores de entrada en formato de fila.

Este formato es similar al prototipo PredictRequest de la API de gRPC y la API de predicción de CMLE . Utilice este formato si todos los tensores de entrada con nombre tienen la misma dimensión 0 . Si no es así, utilice el formato de columnas que se describe más adelante.

En el formato de fila, las entradas se codifican para la clave de instancias en la solicitud JSON.

Cuando solo hay una entrada con nombre, especifique el valor de la clave de instancias para que sea el valor de la entrada:

{
  // List of 3 scalar tensors.
  "instances": [ "foo", "bar", "baz" ]
}

{
  // List of 2 tensors each of [1, 2] shape
  "instances": [ [[1, 2]], [[3, 4]] ]
}

Los tensores se expresan de forma natural en notación anidada, ya que no es necesario aplanar manualmente la lista.

Para múltiples entradas con nombre, se espera que cada elemento sea un objeto que contenga un par de valor de tensor/nombre de entrada, uno para cada entrada con nombre. Como ejemplo, la siguiente es una solicitud con dos instancias, cada una con un conjunto de tres tensores de entrada con nombre:

{
 "instances": [
   {
     "tag": "foo",
     "signal": [1, 2, 3, 4, 5],
     "sensor": [[1, 2], [3, 4]]
   },
   {
     "tag": "bar",
     "signal": [3, 4, 1, 2, 5]],
     "sensor": [[4, 5], [6, 8]]
   }
 ]
}

Tenga en cuenta que cada entrada con nombre ("etiqueta", "señal", "sensor") se supone implícitamente que tiene la misma dimensión 0 ( dos en el ejemplo anterior, ya que hay dos objetos en la lista de instancias ). Si ha nombrado entradas que tienen una dimensión 0-ésima diferente, use el formato de columnas que se describe a continuación.

Especificación de tensores de entrada en formato de columna.

Utilice este formato para especificar sus tensores de entrada, si las entradas nombradas individuales no tienen la misma dimensión 0 o si desea una representación más compacta. Este formato es similar al campo inputs de la solicitud de gRPC Predict .

En el formato de columnas, las entradas se codifican con la clave de entradas en la solicitud JSON.

El valor de la clave de entrada puede ser un tensor de entrada único o un mapa del nombre de entrada a los tensores (enumerados en su forma anidada natural). Cada entrada puede tener una forma arbitraria y no necesita compartir la misma dimensión 0 (también conocido como tamaño de lote) como lo requiere el formato de fila descrito anteriormente.

La representación en columnas del ejemplo anterior es la siguiente:

{
 "inputs": {
   "tag": ["foo", "bar"],
   "signal": [[1, 2, 3, 4, 5], [3, 4, 1, 2, 5]],
   "sensor": [[[1, 2], [3, 4]], [[4, 5], [6, 8]]]
 }
}

Tenga en cuenta que las entradas son un objeto JSON y no una lista como instancias (utilizadas en la representación de fila). Además, todas las entradas con nombre se especifican juntas, en lugar de desplegarlas en filas individuales en el formato de fila descrito anteriormente. Esto hace que la representación sea compacta (pero tal vez menos legible).

Formato de respuesta

La solicitud predict devuelve un objeto JSON en el cuerpo de la respuesta.

Una solicitud en formato de fila tiene la respuesta con el siguiente formato:

{
  "predictions": <value>|<(nested)list>|<list-of-objects>
}

Si la salida del modelo contiene solo un tensor con nombre, omitimos el nombre y los mapas clave predictions en una lista de valores escalares o de lista. Si el modelo genera múltiples tensores con nombre, generamos una lista de objetos en su lugar, similar a la solicitud en formato de fila mencionada anteriormente.

Una solicitud en formato de columnas tiene la respuesta con el siguiente formato:

{
  "outputs": <value>|<(nested)list>|<object>
}

Si la salida del modelo contiene solo un tensor con nombre, omitimos el nombre y outputs mapas clave en una lista de valores escalares o de lista. Si el modelo genera múltiples tensores con nombre, generamos un objeto en su lugar. Cada clave de este objeto corresponde a un tensor de salida con nombre. El formato es similar a la solicitud en formato de columna mencionada anteriormente.

Salida de valores binarios

TensorFlow no distingue entre cadenas binarias y no binarias. Todos son de tipo DT_STRING . Se considera que los tensores con nombre que tienen _bytes como sufijo en su nombre tienen valores binarios. Dichos valores se codifican de manera diferente, como se describe en la sección de codificación de valores binarios a continuación.

Mapeo JSON

Las API RESTful admiten una codificación canónica en JSON, lo que facilita el intercambio de datos entre sistemas. Para los tipos admitidos, las codificaciones se describen tipo por tipo en la siguiente tabla. Se supone que los tipos que no se enumeran a continuación no son compatibles.

Tipo de datos TF Valor JSON ejemplo JSON notas
DT_BOOL verdadero Falso verdadero Falso
DT_STRING cadena "¡Hola Mundo!" Si DT_STRING representa bytes binarios (por ejemplo, bytes de imagen serializados o protobuf), codifíquelos en Base64. Consulte Codificación de valores binarios para obtener más información.
DT_INT8, DT_UINT8, DT_INT16, DT_INT32, DT_UINT32, DT_INT64, DT_UINT64 número 1, -10, 0 El valor JSON será un número decimal.
DT_FLOTANTE, DT_DOBLE número 1,1, -10,0, 0, NaN , Infinity El valor JSON será un número o uno de los valores de token especiales: NaN , Infinity e -Infinity . Consulte la conformidad con JSON para obtener más información. También se acepta la notación exponencial.

Precisión de punto flotante

JSON tiene un tipo de datos de un solo número. Por lo tanto, es posible proporcionar un valor para una entrada que resulte en una pérdida de precisión. Por ejemplo, si la entrada x es un tipo de datos float y la entrada {"x": 1435774380} se envía al modelo que se ejecuta en hardware basado en el estándar de punto flotante IEEE 754 (por ejemplo, Intel o AMD), entonces el valor será ser convertido silenciosamente por el hardware subyacente a 1435774336 ya que 1435774380 no se puede representar exactamente en un número de punto flotante de 32 bits. Por lo general, las entradas para servir deben tener la misma distribución que el entrenamiento, por lo que esto generalmente no será problemático porque se produjeron las mismas conversiones en el momento del entrenamiento. Sin embargo, en caso de que se necesite precisión total, asegúrese de usar un tipo de datos subyacente en su modelo que pueda manejar la precisión deseada y/o considere la verificación del lado del cliente.

Codificación de valores binarios

JSON utiliza la codificación UTF-8. Si tiene características de entrada o valores de tensor que deben ser binarios (como bytes de imagen), debe codificar los datos en Base64 y encapsularlos en un objeto JSON que tenga b64 como clave de la siguiente manera:

{ "b64": <base64 encoded string> }

Puede especificar este objeto como un valor para una entidad de entrada o un tensor. El mismo formato también se utiliza para codificar la respuesta de salida.

A continuación se muestra una solicitud de clasificación con características image (datos binarios) y caption :

{
  "signature_name": "classify_objects",
  "examples": [
    {
      "image": { "b64": "aW1hZ2UgYnl0ZXM=" },
      "caption": "seaside"
    },
    {
      "image": { "b64": "YXdlc29tZSBpbWFnZSBieXRlcw==" },
      "caption": "mountains"
    }
  ]
}

Conformidad con JSON

Muchos valores de características o tensores son números de punto flotante. Aparte de los valores finitos (p. ej., 3,14, 1,0, etc.), estos pueden tener valores NaN y no finitos ( Infinity e -Infinity ). Desafortunadamente, la especificación JSON ( RFC 7159 ) NO reconoce estos valores (aunque la especificación JavaScript sí).

La API REST descrita en esta página permite que los objetos JSON de solicitud/respuesta tengan dichos valores. Esto implica que solicitudes como la siguiente son válidas:

{
  "example": [
    {
      "sensor_readings": [ 1.0, -3.14, Nan, Infinity ]
    }
  ]
}

Un analizador JSON que cumpla con los estándares (estrictos) rechazará esto con un error de análisis (debido a tokens NaN e Infinity mezclados con números reales). Para manejar correctamente las solicitudes/respuestas en su código, use un analizador JSON que admita estos tokens.

Los tokens NaN , Infinity , -Infinity son reconocidos por proto3 , el módulo Python JSON y el lenguaje JavaScript.

Ejemplo

Podemos usar el modelo toy half_plus_three para ver las API REST en acción.

Inicie ModelServer con el punto final de la API REST

Descargue el modelo half_plus_three del repositorio de git :

$ mkdir -p /tmp/tfserving
$ cd /tmp/tfserving
$ git clone --depth=1 https://github.com/tensorflow/serving

Usaremos Docker para ejecutar ModelServer. Si desea instalar ModelServer de forma nativa en su sistema, siga las instrucciones de configuración para instalar e inicie ModelServer con la opción --rest_api_port para exportar el punto final de la API REST (esto no es necesario cuando se usa Docker).

$ cd /tmp/tfserving
$ docker pull tensorflow/serving:latest
$ docker run --rm -p 8501:8501 \
    --mount type=bind,source=$(pwd),target=$(pwd) \
    -e MODEL_BASE_PATH=$(pwd)/serving/tensorflow_serving/servables/tensorflow/testdata \
    -e MODEL_NAME=saved_model_half_plus_three -t tensorflow/serving:latest
...
.... Exporting HTTP/REST API at:localhost:8501 ...

Hacer llamadas API REST a ModelServer

En una terminal diferente, use la herramienta curl para realizar llamadas a la API REST.

Obtenga el estado del modelo de la siguiente manera:

$ curl http://localhost:8501/v1/models/saved_model_half_plus_three
{
 "model_version_status": [
  {
   "version": "123",
   "state": "AVAILABLE",
   "status": {
    "error_code": "OK",
    "error_message": ""
   }
  }
 ]
}

Una llamada predict se vería de la siguiente manera:

$ curl -d '{"instances": [1.0,2.0,5.0]}' -X POST http://localhost:8501/v1/models/saved_model_half_plus_three:predict
{
    "predictions": [3.5, 4.0, 5.5]
}

Y una llamada regress se ve de la siguiente manera:

$ curl -d '{"signature_name": "tensorflow/serving/regress", "examples": [{"x": 1.0}, {"x": 2.0}]}' \
  -X POST http://localhost:8501/v1/models/saved_model_half_plus_three:regress
{
    "results": [3.5, 4.0]
}

Tenga en cuenta que regress está disponible en un nombre de firma no predeterminado y debe especificarse explícitamente. Una URL o un cuerpo de solicitud incorrectos devuelven un estado de error HTTP.

$ curl -i -d '{"instances": [1.0,5.0]}' -X POST http://localhost:8501/v1/models/half:predict
HTTP/1.1 404 Not Found
Content-Type: application/json
Date: Wed, 06 Jun 2018 23:20:12 GMT
Content-Length: 65

{ "error": "Servable not found for request: Latest(half)" }
$