Além das APIs gRPC, o TensorFlow ModelServer também oferece suporte a APIs RESTful. Esta página descreve esses endpoints de API e um exemplo completo de uso.
A solicitação e a resposta são um objeto JSON. A composição deste objeto depende do tipo de solicitação ou verbo. Consulte as seções específicas da API abaixo para obter detalhes.
Em caso de erro, todas as APIs retornarão um objeto JSON no corpo da resposta com error
como chave e a mensagem de erro como valor:
{
"error": <error message string>
}
API de status do modelo
Esta API segue de perto a API gRPC ModelService.GetModelStatus
. Ele retorna o status de um modelo no ModelServer.
URL
GET http://host:port/v1/models/${MODEL_NAME}[/versions/${VERSION}|/labels/${LABEL}]
Incluir /versions/${VERSION}
ou /labels/${LABEL}
é opcional. Se o status for omitido para todas as versões, será retornado na resposta.
Formato de resposta
Se for bem-sucedido, retornará uma representação JSON do protobuf GetModelStatusResponse
.
API de metadados de modelo
Esta API segue de perto a API gRPC PredictionService.GetModelMetadata
. Ele retorna os metadados de um modelo no ModelServer.
URL
GET http://host:port/v1/models/${MODEL_NAME}[/versions/${VERSION}|/labels/${LABEL}]/metadata
Incluir /versions/${VERSION}
ou /labels/${LABEL}
é opcional. Se omitido, os metadados do modelo da versão mais recente serão retornados na resposta.
Formato de resposta
Se for bem-sucedido, retornará uma representação JSON do protobuf GetModelMetadataResponse
.
API de classificação e regressão
Esta API segue de perto os métodos Classify
e Regress
da API gRPC do PredictionService
.
URL
POST http://host:port/v1/models/${MODEL_NAME}[/versions/${VERSION}|/labels/${LABEL}]:(classify|regress)
Incluir /versions/${VERSION}
ou /labels/${LABEL}
é opcional. Se omitido, a versão mais recente será usada.
Formato de solicitação
O corpo da solicitação para as APIs classify
e regress
deve ser um objeto JSON formatado da seguinte forma:
{
// 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>
é um número JSON (inteiro ou decimal), uma string JSON ou um objeto JSON que representa dados binários (consulte a seção Codificação de valores binários abaixo para obter detalhes). <list>
é uma lista desses valores. Este formato é semelhante aos protos ClassificationRequest
e RegressionRequest
do gRPC. Ambas as versões aceitam lista de objetos Example
.
Formato de resposta
Uma solicitação classify
retorna um objeto JSON no corpo da resposta, formatado da seguinte forma:
{
"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>
é uma string (que pode ser uma string vazia ""
se o modelo não tiver um rótulo associado à pontuação). <score>
é um número decimal (ponto flutuante).
A solicitação regress
retorna um objeto JSON no corpo da resposta, formatado da seguinte forma:
{
// One regression value for each example in the request in the same order.
"result": [ <value1>, <value2>, <value3>, ...]
}
<value>
é um número decimal.
Os usuários da API gRPC notarão a semelhança deste formato com os protos ClassificationResponse
e RegressionResponse
.
Prever API
Esta API segue de perto a API PredictionService.Predict
gRPC.
URL
POST http://host:port/v1/models/${MODEL_NAME}[/versions/${VERSION}|/labels/${LABEL}]:predict
Incluir /versions/${VERSION}
ou /labels/${LABEL}
é opcional. Se omitido, a versão mais recente será usada.
Formato de solicitação
O corpo da solicitação para a API predict
deve ser um objeto JSON formatado da seguinte maneira:
{
// (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>
}
Especificando tensores de entrada em formato de linha.
Este formato é semelhante ao proto PredictRequest
da API gRPC e à API de previsão CMLE . Use este formato se todos os tensores de entrada nomeados tiverem a mesma dimensão 0 . Caso contrário, use o formato colunar descrito abaixo.
No formato de linha, as entradas são codificadas para chaves de instância na solicitação JSON.
Quando houver apenas uma entrada nomeada, especifique o valor da chave de instâncias como o valor da entrada:
{
// List of 3 scalar tensors.
"instances": [ "foo", "bar", "baz" ]
}
{
// List of 2 tensors each of [1, 2] shape
"instances": [ [[1, 2]], [[3, 4]] ]
}
Os tensores são expressos naturalmente em notação aninhada, pois não há necessidade de nivelar manualmente a lista.
Para múltiplas entradas nomeadas, espera-se que cada item seja um objeto contendo o par nome de entrada/valor do tensor, um para cada entrada nomeada. Como exemplo, a seguir está uma solicitação com duas instâncias, cada uma com um conjunto de três tensores de entrada nomeados:
{
"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]]
}
]
}
Observe que cada entrada nomeada ("tag", "sinal", "sensor") é implicitamente assumida como tendo a mesma dimensão 0 ( duas no exemplo acima, pois há dois objetos na lista de instâncias ). Se você nomeou entradas com dimensão 0 diferente, use o formato colunar descrito abaixo.
Especificando tensores de entrada em formato de coluna.
Use este formato para especificar seus tensores de entrada, se as entradas nomeadas individuais não tiverem a mesma dimensão 0 ou se você desejar uma representação mais compacta. Este formato é semelhante ao campo inputs
da solicitação gRPC Predict
.
No formato colunar, as entradas são codificadas para a chave de entradas na solicitação JSON.
O valor da chave de entradas pode ser um único tensor de entrada ou um mapa do nome de entrada para tensores (listados em sua forma aninhada natural). Cada entrada pode ter formato arbitrário e não precisa compartilhar a mesma dimensão 0 (também conhecida como tamanho do lote), conforme exigido pelo formato de linha descrito acima.
A representação colunar do exemplo anterior é a seguinte:
{
"inputs": {
"tag": ["foo", "bar"],
"signal": [[1, 2, 3, 4, 5], [3, 4, 1, 2, 5]],
"sensor": [[[1, 2], [3, 4]], [[4, 5], [6, 8]]]
}
}
Observe que inputs é um objeto JSON e não uma lista como instâncias (usadas na representação de linha). Além disso, todas as entradas nomeadas são especificadas juntas, em vez de desenrolá-las em linhas individuais, feito no formato de linha descrito anteriormente. Isto torna a representação compacta (mas talvez menos legível).
Formato de resposta
A solicitação predict
retorna um objeto JSON no corpo da resposta.
Uma solicitação em formato de linha tem a resposta formatada da seguinte forma:
{
"predictions": <value>|<(nested)list>|<list-of-objects>
}
Se a saída do modelo contiver apenas um tensor nomeado, omitiremos o nome e os mapas de chaves predictions
para uma lista de valores escalares ou de lista. Se o modelo gerar vários tensores nomeados, em vez disso, geraremos uma lista de objetos, semelhante à solicitação em formato de linha mencionada acima.
Uma solicitação em formato colunar tem a resposta formatada da seguinte forma:
{
"outputs": <value>|<(nested)list>|<object>
}
Se a saída do modelo contiver apenas um tensor nomeado, omitimos o nome e outputs
mapas de chaves para uma lista de valores escalares ou de lista. Se o modelo gerar vários tensores nomeados, em vez disso, geraremos um objeto. Cada chave deste objeto corresponde a um tensor de saída nomeado. O formato é semelhante ao pedido em formato de coluna mencionado acima.
Saída de valores binários
O TensorFlow não faz distinção entre strings não binárias e binárias. Todos são do tipo DT_STRING
. Tensores nomeados que possuem _bytes
como sufixo em seu nome são considerados como tendo valores binários. Esses valores são codificados de forma diferente, conforme descrito na seção de codificação de valores binários abaixo.
Mapeamento JSON
As APIs RESTful suportam codificação canônica em JSON, facilitando o compartilhamento de dados entre sistemas. Para tipos suportados, as codificações são descritas tipo por tipo na tabela abaixo. Os tipos não listados abaixo estão implícitos como não suportados.
Tipo de dados TF | Valor JSON | Exemplo JSON | Notas |
---|---|---|---|
DT_BOOL | verdadeiro, falso | verdadeiro, falso | |
DT_STRING | corda | "Olá, mundo!" | Se DT_STRING representa bytes binários (por exemplo, bytes de imagem serializados ou protobuf), codifique-os em Base64. Consulte Codificando valores binários para obter mais informações. |
DT_INT8, DT_UINT8, DT_INT16, DT_INT32, DT_UINT32, DT_INT64, DT_UINT64 | número | 1, -10, 0 | O valor JSON será um número decimal. |
DT_FLOAT, DT_DOUBLE | número | 1,1, -10,0, 0, NaN , Infinity | O valor JSON será um número ou um dos valores de token especiais - NaN , Infinity e -Infinity . Consulte Conformidade com JSON para obter mais informações. A notação de expoente também é aceita. |
Precisão de ponto flutuante
JSON possui um único tipo de dados numérico. Assim é possível fornecer um valor para uma entrada que resulte em perda de precisão. Por exemplo, se a entrada x
for um tipo de dados float
e a entrada {"x": 1435774380}
for enviada para o modelo rodando em hardware baseado no padrão de ponto flutuante IEEE 754 (por exemplo, Intel ou AMD), então o valor será ser convertido silenciosamente pelo hardware subjacente para 1435774336
pois 1435774380
não pode ser representado exatamente em um número de ponto flutuante de 32 bits. Normalmente, as entradas para servir devem ter a mesma distribuição que o treinamento, então isso geralmente não será problemático porque as mesmas conversões aconteceram no momento do treinamento. No entanto, caso seja necessária precisão total, certifique-se de usar um tipo de dados subjacente em seu modelo que possa lidar com a precisão desejada e/ou considere a verificação do lado do cliente.
Codificando valores binários
JSON usa codificação UTF-8. Se você tiver recursos de entrada ou valores de tensor que precisam ser binários (como bytes de imagem), você deve codificar os dados em Base64 e encapsulá-los em um objeto JSON tendo b64
como chave da seguinte forma:
{ "b64": <base64 encoded string> }
Você pode especificar este objeto como um valor para um recurso ou tensor de entrada. O mesmo formato também é usado para codificar a resposta de saída.
Uma solicitação de classificação com recursos image
(dados binários) e caption
é mostrada abaixo:
{
"signature_name": "classify_objects",
"examples": [
{
"image": { "b64": "aW1hZ2UgYnl0ZXM=" },
"caption": "seaside"
},
{
"image": { "b64": "YXdlc29tZSBpbWFnZSBieXRlcw==" },
"caption": "mountains"
}
]
}
Conformidade JSON
Muitos valores de recursos ou tensores são números de ponto flutuante. Além dos valores finitos (por exemplo, 3,14, 1,0 etc.), eles podem ter valores NaN
e não finitos ( Infinity
e -Infinity
). Infelizmente a especificação JSON ( RFC 7159 ) NÃO reconhece esses valores (embora a especificação JavaScript reconheça).
A API REST descrita nesta página permite que objetos JSON de solicitação/resposta tenham tais valores. Isso implica que solicitações como a seguinte são válidas:
{
"example": [
{
"sensor_readings": [ 1.0, -3.14, Nan, Infinity ]
}
]
}
Um analisador JSON compatível com padrões (estritos) rejeitará isso com um erro de análise (devido aos tokens NaN
e Infinity
misturados com números reais). Para lidar corretamente com solicitações/respostas em seu código, use um analisador JSON que suporte esses tokens.
Os tokens NaN
, Infinity
, -Infinity
são reconhecidos pelo proto3 , módulo Python JSON e linguagem JavaScript.
Exemplo
Podemos usar o modelo de brinquedo half_plus_três para ver as APIs REST em ação.
Inicie o ModelServer com o terminal da API REST
Baixe o modelo half_plus_three
do repositório git :
$ mkdir -p /tmp/tfserving
$ cd /tmp/tfserving
$ git clone --depth=1 https://github.com/tensorflow/serving
Usaremos o Docker para executar o ModelServer. Se você deseja instalar o ModelServer nativamente em seu sistema, siga as instruções de configuração para instalar e inicie o ModelServer com a opção --rest_api_port
para exportar o endpoint da API REST (isso não é necessário ao usar o 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 ...
Faça chamadas de API REST para ModelServer
Em um terminal diferente, use a ferramenta curl
para fazer chamadas à API REST.
Obtenha o status do modelo da seguinte maneira:
$ curl http://localhost:8501/v1/models/saved_model_half_plus_three
{
"model_version_status": [
{
"version": "123",
"state": "AVAILABLE",
"status": {
"error_code": "OK",
"error_message": ""
}
}
]
}
Uma chamada predict
teria a seguinte aparência:
$ 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]
}
E uma chamada de regress
tem a seguinte aparência:
$ 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]
}
Observe que regress
está disponível em um nome de assinatura não padrão e deve ser especificada explicitamente. Um URL ou corpo de solicitação incorreto retorna um status de erro 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)" }
$