除了 gRPC API,TensorFlow ModelServer 还支持 RESTful API。本页介绍了这些 API 端点以及使用方面的端到端 示例。
请求和响应是一个 JSON 对象。此对象的组成取决于请求类型或动词。有关详细信息,请参阅以下 API 特定部分。
在发生错误的情况下,所有 API 都将在响应正文中返回一个 JSON 对象,其中 error
作为键,错误消息作为值。
{
"error": <error message string>
}
模型状态 API
此 API 紧密遵循 ModelService.GetModelStatus
gRPC API。它返回 ModelServer 中模型的状态。
URL
GET http://host:port/v1/models/${MODEL_NAME}[/versions/${VERSION}|/labels/${LABEL}]
包含 /versions/${VERSION}
或 /labels/${LABEL}
是可选的。如果省略,则响应中将返回所有版本的狀態。
响应格式
如果成功,则返回 GetModelStatusResponse
protobuf 的 JSON 表示形式。
模型元数据 API
此 API 紧密遵循 PredictionService.GetModelMetadata
gRPC API。它返回 ModelServer 中模型的元数据。
URL
GET http://host:port/v1/models/${MODEL_NAME}[/versions/${VERSION}|/labels/${LABEL}]/metadata
包含 /versions/${VERSION}
或 /labels/${LABEL}
是可选的。如果省略,则响应中将返回最新版本的模型元数据。
响应格式
如果成功,则返回 GetModelMetadataResponse
protobuf 的 JSON 表示形式。
分类和回归 API
此 API 紧密遵循 PredictionService
gRPC API 的 Classify
和 Regress
方法。
URL
POST http://host:port/v1/models/${MODEL_NAME}[/versions/${VERSION}|/labels/${LABEL}]:(classify|regress)
包含 /versions/${VERSION}
或 /labels/${LABEL}
是可选的。如果省略,则使用最新版本。
请求格式
classify
和 regress
API 的请求正文必须是格式如下所示的 JSON 对象。
{
// 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>
是 JSON 数字(整数或小数)、JSON 字符串或表示二进制数据的 JSON 对象(有关详细信息,请参阅下面的 编码二进制值 部分)。<list>
是此类值的列表。此格式类似于 gRPC 的 ClassificationRequest
和 RegressionRequest
proto。这两个版本都接受 Example
对象的列表。
响应格式
classify
请求在响应正文中返回一个 JSON 对象,格式如下所示。
{
"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>
是一个字符串(如果模型没有与分数关联的标签,则可以是空字符串 ""
)。<score>
是一个小数(浮点数)。
regress
请求在响应正文中返回一个 JSON 对象,格式如下所示。
{
// One regression value for each example in the request in the same order.
"result": [ <value1>, <value2>, <value3>, ...]
}
<value>
是一个小数。
gRPC API 的用户会注意到此格式与 ClassificationResponse
和 RegressionResponse
proto 的相似之处。
预测 API
此 API 紧密遵循 PredictionService.Predict
gRPC API。
URL
POST http://host:port/v1/models/${MODEL_NAME}[/versions/${VERSION}|/labels/${LABEL}]:predict
包含 /versions/${VERSION}
或 /labels/${LABEL}
是可选的。如果省略,则使用最新版本。
请求格式
predict
API 的请求正文必须是格式如下所示的 JSON 对象。
{
// (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>
}
以行格式指定输入张量。
此格式类似于 gRPC API 的 PredictRequest
proto 和 CMLE 预测 API。如果所有命名输入张量具有相同的第 0 维,请使用此格式。如果它们没有,请使用下面描述的列格式。
在行格式中,输入被键入到 JSON 请求中的instances 键。
当只有一个命名输入时,将instances 键的值指定为输入的值。
{
// List of 3 scalar tensors.
"instances": [ "foo", "bar", "baz" ]
}
{
// List of 2 tensors each of [1, 2] shape
"instances": [ [[1, 2]], [[3, 4]] ]
}
张量以嵌套表示法自然地表达,因为不需要手动展平列表。
对于多个命名输入,每个项目都应是一个包含输入名称/张量值对的对象,每个命名输入一个。例如,以下是具有两个实例的请求,每个实例都有一组三个命名输入张量。
{
"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]]
}
]
}
请注意,每个命名输入(“tag”、“signal”、“sensor”)隐式地假定具有相同的第 0 维(在上面的示例中为两个,因为instances 列表中有两个对象)。如果您有具有不同第 0 维的命名输入,请使用下面描述的列格式。
以列格式指定输入张量。
如果单个命名输入没有相同的第 0 维,或者您想要更紧凑的表示形式,请使用此格式来指定您的输入张量。此格式类似于 gRPC Predict
请求的 inputs
字段。
在列格式中,输入被键入到 JSON 请求中的inputs 键。
inputs 键的值可以是单个输入张量,也可以是输入名称到张量的映射(以其自然嵌套形式列出)。每个输入都可以具有任意形状,并且不需要与上面描述的行格式所需的相同第 0 维(也称为批次大小)共享。
先前示例的列表示形式如下所示。
{
"inputs": {
"tag": ["foo", "bar"],
"signal": [[1, 2, 3, 4, 5], [3, 4, 1, 2, 5]],
"sensor": [[[1, 2], [3, 4]], [[4, 5], [6, 8]]]
}
}
请注意,inputs 是一个 JSON 对象,而不是像instances(在行表示形式中使用)那样的列表。此外,所有命名输入都是一起指定的,而不是像之前描述的行格式那样展开成单个行。这使得表示形式紧凑(但可能不太易读)。
响应格式
predict
请求在响应正文中返回一个 JSON 对象。
以 行格式 的请求具有如下格式的响应。
{
"predictions": <value>|<(nested)list>|<list-of-objects>
}
如果模型的输出只包含一个命名张量,我们将省略名称,并且 predictions
键映射到标量或列表值的列表。如果模型输出多个命名张量,我们将输出一个对象列表,类似于上面提到的行格式请求。
以 列格式 的请求具有如下格式的响应。
{
"outputs": <value>|<(nested)list>|<object>
}
如果模型的输出只包含一个命名张量,我们将省略名称,并且 outputs
键映射到标量或列表值的列表。如果模型输出多个命名张量,我们将输出一个对象。此对象的每个键对应于一个命名输出张量。格式类似于上面提到的列格式请求。
二进制值的输出
TensorFlow 不区分非二进制字符串和二进制字符串。所有都是 DT_STRING
类型。名称中以_bytes
作为后缀的命名张量被认为具有二进制值。此类值以不同的方式编码,如下面的 编码二进制值 部分所述。
JSON 映射
RESTful API 支持 JSON 中的规范编码,这使得在系统之间共享数据变得更加容易。对于支持的类型,编码在下面的表格中按类型逐一描述。下面未列出的类型被隐式地认为是不支持的。
TF 数据类型 | JSON 值 | JSON 示例 | 说明 |
---|---|---|---|
DT_BOOL | true, false | true, false | |
DT_STRING | string | "Hello World!" | 如果 DT_STRING 表示二进制字节(例如,序列化图像字节或 protobuf),请将这些字节编码为 Base64。有关更多信息,请参阅 编码二进制值。 |
DT_INT8, DT_UINT8, DT_INT16, DT_INT32, DT_UINT32, DT_INT64, DT_UINT64 | number | 1, -10, 0 | JSON 值将是一个十进制数。 |
DT_FLOAT, DT_DOUBLE | number | 1.1, -10.0, 0, NaN , Infinity |
JSON 值将是一个数字或其中一个特殊标记值 - NaN 、Infinity 和 -Infinity 。有关更多信息,请参阅 JSON 符合性。指数表示法也被接受。 |
浮点精度
JSON 只有一个数字数据类型。因此,有可能提供一个输入值,导致精度损失。例如,如果输入 x
是一个 float
数据类型,并且输入 {"x": 1435774380}
被发送到基于 IEEE 754 浮点标准的硬件(例如 Intel 或 AMD)上运行的模型,那么该值将被底层硬件静默转换为 1435774336
,因为 1435774380
无法在 32 位浮点数中精确表示。通常,服务端的输入应该与训练时的分布相同,因此这通常不会造成问题,因为在训练时发生了相同的转换。但是,如果需要完全精度,请确保在您的模型中使用可以处理所需精度的底层数据类型,或者考虑客户端检查。
编码二进制值
JSON 使用 UTF-8 编码。如果您有需要为二进制的输入特征或张量值(如图像字节),您 *必须* 对数据进行 Base64 编码,并将其封装在以 b64
为键的 JSON 对象中,如下所示
{ "b64": <base64 encoded string> }
您可以将此对象指定为输入特征或张量的值。相同的格式也用于编码输出响应。
下面显示了一个带有 image
(二进制数据)和 caption
特征的分类请求
{
"signature_name": "classify_objects",
"examples": [
{
"image": { "b64": "aW1hZ2UgYnl0ZXM=" },
"caption": "seaside"
},
{
"image": { "b64": "YXdlc29tZSBpbWFnZSBieXRlcw==" },
"caption": "mountains"
}
]
}
JSON 符合性
许多特征或张量值是浮点数。除了有限值(例如 3.14、1.0 等)之外,它们还可以具有 NaN
和非有限值(Infinity
和 -Infinity
)。不幸的是,JSON 规范 (RFC 7159) *不* 识别这些值(尽管 JavaScript 规范确实识别)。
本页描述的 REST API 允许请求/响应 JSON 对象具有这些值。这意味着以下请求是有效的
{
"example": [
{
"sensor_readings": [ 1.0, -3.14, Nan, Infinity ]
}
]
}
一个(严格的)符合标准的 JSON 解析器将由于 NaN
和 Infinity
标记与实际数字混合而拒绝此请求并返回解析错误。为了在您的代码中正确处理请求/响应,请使用支持这些标记的 JSON 解析器。
NaN
、Infinity
、-Infinity
标记被 proto3、Python JSON 模块和 JavaScript 语言识别。
示例
我们可以使用玩具 half_plus_three 模型来查看 REST API 的实际应用。
使用 REST API 端点启动 ModelServer
从 git 仓库 下载 half_plus_three
模型
$ mkdir -p /tmp/tfserving
$ cd /tmp/tfserving
$ git clone --depth=1 https://github.com/tensorflow/serving
我们将使用 Docker 运行 ModelServer。如果您想在系统上本地安装 ModelServer,请按照 设置说明 进行安装,并使用 --rest_api_port
选项启动 ModelServer 以导出 REST API 端点(使用 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 ...
对 ModelServer 进行 REST API 调用
在另一个终端中,使用 curl
工具进行 REST API 调用。
获取模型的状态,如下所示
$ curl https://127.0.0.1:8501/v1/models/saved_model_half_plus_three
{
"model_version_status": [
{
"version": "123",
"state": "AVAILABLE",
"status": {
"error_code": "OK",
"error_message": ""
}
}
]
}
一个 predict
调用如下所示
$ curl -d '{"instances": [1.0,2.0,5.0]}' -X POST https://127.0.0.1:8501/v1/models/saved_model_half_plus_three:predict
{
"predictions": [3.5, 4.0, 5.5]
}
一个 regress
调用如下所示
$ curl -d '{"signature_name": "tensorflow/serving/regress", "examples": [{"x": 1.0}, {"x": 2.0}]}' \
-X POST https://127.0.0.1:8501/v1/models/saved_model_half_plus_three:regress
{
"results": [3.5, 4.0]
}
注意,regress
在非默认签名名称上可用,必须显式指定。不正确的请求 URL 或主体将返回 HTTP 错误状态。
$ curl -i -d '{"instances": [1.0,5.0]}' -X POST https://127.0.0.1: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)" }
$