RESTful API

除了 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 的 ClassifyRegress 方法。

URL

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

包含 /versions/${VERSION}/labels/${LABEL} 是可选的。如果省略,则使用最新版本。

请求格式

classifyregress 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 的 ClassificationRequestRegressionRequest 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 的用户会注意到此格式与 ClassificationResponseRegressionResponse 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 值将是一个数字或其中一个特殊标记值 - NaNInfinity-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 解析器将由于 NaNInfinity 标记与实际数字混合而拒绝此请求并返回解析错误。为了在您的代码中正确处理请求/响应,请使用支持这些标记的 JSON 解析器。

NaNInfinity-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 http://localhost: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 http://localhost: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 http://localhost: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 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)" }
$