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)" }
$