设置预测请求的格式

如果您要发出请求以响应应用输入,或者在其他需要及时推断(实时响应)的情况下,可以使用在线预测。

本页介绍了如何使用在线预测 API 为自定义训练模型设置在线预测请求的格式,并提供了请求和响应示例。格式化请求后,您可以获取在线预测结果

准备工作

在格式化请求以进行在线预测之前,请执行以下步骤:

  1. 导出模型制品以进行预测
  2. 将模型资源部署到端点

    此操作会将计算资源与模型相关联,以便以低延迟方式执行在线预测。

  3. 检查模型的 DeployedModel 自定义资源的状态,并确保该资源已准备好接受预测请求:

    kubectl --kubeconfig PREDICTION_CLUSTER_KUBECONFIG get -f DEPLOYED_MODEL_NAME.yaml -o jsonpath='{.status.primaryCondition}'
    

    替换以下内容:

    • PREDICTION_CLUSTER_KUBECONFIG:预测集群中 kubeconfig 文件的路径。
    • DEPLOYED_MODEL_NAMEDeployedModel 定义文件的名称。

    主要条件必须显示 DeployedModel 已就绪。

    以下输出显示了示例回答:

    {"firstObservedTime":"2024-01-19T01:18:30Z","lastUpdateTime":"2024-01-19T01:35:47Z","message":"DeployedModel is ready", "observedGeneration":1, "reason":"Ready", "resourceName":"my-tf-model","type":"DeployedModel"}
    
  4. 检查 Endpoint 自定义资源的状态,确保其已准备好接受预测请求:

    kubectl --kubeconfig PREDICTION_CLUSTER_KUBECONFIG get -f ENDPOINT_NAME.yaml -o jsonpath='{$.status.conditions[?(@.type == "EndpointReady")]}'
    

    替换以下内容:

    • PREDICTION_CLUSTER_KUBECONFIG:预测集群中 kubeconfig 文件的路径。
    • ENDPOINT_NAMEEndpoint 定义文件的名称。

    EndpointReady 条件的 status 字段必须显示 True 值。

    以下输出显示了示例回答:

    {"lastTransitionTime":"2024-01-19T05:12:26Z","message":"Endpoint Ready", "observedGeneration":1,"reason":"ResourceReady","status":"True","type":"EndpointReady"}%
    

格式化输入以进行在线预测

在线预测可通过以下两种方法发送请求:

  • 预测请求:向 predict 方法发送请求以获取在线预测结果。
  • 原始预测请求:向 rawPredict 方法发送请求,以便使用任意 HTTP 载荷,而不是遵循 JSON 格式。

如果您需要低延迟,请获取原始预测结果,因为 rawPredict 会跳过序列化步骤并直接将请求转发到预测容器。

本部分介绍了如何使用 JSON 设置预测输入实例的格式并进行编码,如果您使用的是 predict 方法,则需要执行此操作。如果您使用的是 rawPredict 方法,则无需提供此信息。

如果您使用 Python 版 Vertex AI SDK 发送预测请求,请指定不带 instances 字段的实例列表。例如,指定 [ ["the","quick","brown"], ... ] 而不是 { "instances": [ ["the","quick","brown"], ... ] }

将实例格式设置为 JSON 字符串

在线预测的基本格式是数据实例列表。这些列表可以是普通的值列表,也可以是 JSON 对象的成员,具体取决于您在训练应用中配置输入的方式。 TensorFlow 模型可以接受更复杂的输入。

以下示例显示了 TensorFlow 模型的输入张量和实例键:

 {"values": [1, 2, 3, 4], "key": 1}

只要遵循以下规则,JSON 字符串的构成可能会很复杂:

  • 顶级实例数据必须是 JSON 对象,即键值对的字典。

  • 实例对象中的各个值可以是字符串、数字或列表。 您无法嵌入 JSON 对象。

  • 列表必须仅包含相同类型的内容(包括其他列表)。 请勿混用字符串和数值。

您将在线预测的输入实例作为 predict 调用的消息正文进行传递。详细了解请求正文的格式要求

使每个实例成为 JSON 数组中的一项,并将该数组作为 JSON 对象的 instances 字段提供,如以下示例所示:

{"instances": [
  {"values": [1, 2, 3, 4], "key": 1},
  {"values": [5, 6, 7, 8], "key": 2}
]}

对二进制数据进行编码以进行预测预测

您无法将二进制数据格式化为 JSON 支持的 UTF-8 编码字符串。如果输入中包含二进制数据,请使用 base64 编码来表示。您需要使用以下特殊格式:

  • 将编码的字符串的格式设置为 JSON 对象,并包含名为 b64 的单个键。在 Python 3 中,base64 编码会输出一个字节序列。将此序列转换为字符串,使其能够进行 JSON 序列化:

    {'image_bytes': {'b64': base64.b64encode(jpeg_data).decode()}}
    
  • 在 TensorFlow 模型代码中,为二进制输入和输出张量提供别名,使这些名称以 _bytes 结尾。

请求和响应示例

本部分介绍在线预测请求和响应正文的格式,并提供 TensorFlow 和 PyTorch 的示例。

请求正文详情

TensorFlow

请求正文包含的数据采用以下结构(JSON 表示法):

{
  "instances": [
    <value>|<simple/nested list>|<object>,
    ...
  ]
}

instances[] 对象是必需的,而且必须包含待预测实例的列表。

实例列表中每个元素的结构由模型的输入定义决定。实例可包含命名输入(作为对象),或者仅包含未加标签的值。

并非所有数据都包含命名输入。有些实例是 JSON 值(布尔值、数字或字符串)。不过,实例通常是值列表或复杂的嵌套列表。

以下是一些请求正文示例:

  • CSV 数据,其中每行编码为字符串值
{"instances": ["1.0,true,\\"x\\"", "-2.0,false,\\"y\\""]}
  • 纯文本
{"instances": ["the quick brown fox", "the lazy dog"]}
  • 编码为字词列表的句子(字符串矢量)
{
  "instances": [
    ["the","quick","brown"],
    ["the","lazy","dog"],
    ...
  ]
}
  • 浮点标量值
{"instances": [0.0, 1.1, 2.2]}
  • 整数向量
{
  "instances": [
    [0, 1, 2],
    [3, 4, 5],
    ...
  ]
}
  • 张量(在本示例中为二维张量)
{
  "instances": [
    [
      [0, 1, 2],
      [3, 4, 5]
    ],
    ...
  ]
}
  • 图片,可通过不同方式来表示

在本编码方案中,前两个维度表示图片的行和列,第三个维度包含每个像素的 R、G 和 B 值的列表(矢量):

{
  "instances": [
    [
      [
        [138, 30, 66],
        [130, 20, 56],
        ...
      ],
      [
        [126, 38, 61],
        [122, 24, 57],
        ...
      ],
      ...
    ],
    ...
  ]
}

数据编码

JSON 字符串必须采用 UTF-8 编码。如要发送二进制数据,您必须对数据进行 base64 编码并将数据标记为二进制。如要将 JSON 字符串标记为二进制,请将其替换为具有单一 b64 特性的 JSON 对象:

{"b64": "..."}

以下示例展示了两个需要 base64 编码的序列化 tf.Examples 实例(虚构的数据,仅作说明之用):

{"instances": [{"b64": "X5ad6u"}, {"b64": "IA9j4nx"}]}

以下示例显示两个需要 base64 编码的 JPEG 图片字节字符串(虚构的数据,仅作说明之用):

{"instances": [{"b64": "ASa8asdf"}, {"b64": "JLK7ljk3"}]}

多个输入张量

一些模型具有可接受多个输入张量的底层 TensorFlow 图。在这种情况下,请使用 JSON 键值对的名称来识别输入张量。

对于带有输入张量别名 tag(字符串)和 image(以 base64 编码的字符串)的图,请使用以下代码:

{
  "instances": [
    {
      "tag": "beach",
      "image": {"b64": "ASa8asdf"}
    },
    {
      "tag": "car",
      "image": {"b64": "JLK7ljk3"}
    }
  ]
}

对于带有输入张量别名 tag(字符串)和 image(8 位整数的三维数组)的图,请使用以下代码:

{
  "instances": [
    {
      "tag": "beach",
      "image": [
        [
          [138, 30, 66],
          [130, 20, 56],
          ...
        ],
        [
          [126, 38, 61],
          [122, 24, 57],
          ...
        ],
        ...
      ]
    },
    {
      "tag": "car",
      "image": [
        [
          [255, 0, 102],
          [255, 0, 97],
          ...
        ],
        [
          [254, 1, 101],
          [254, 2, 93],
          ...
        ],
        ...
      ]
    },
    ...
  ]
}

PyTorch

如果您的模型使用 PyTorch 预构建容器,则 TorchServe 的默认处理程序预期将每个实例封装在 data 字段中。例如:

{
  "instances": [
    { "data": , <value> },
    { "data": , <value> }
  ]
}

响应正文详情

如果调用成功,则对于请求正文中的每个实例,响应正文都包含一个预测条目,并按相同顺序列出:

{
  "predictions": [
    {
      object
    }
  ],
  "deployedModelId": string
}

如有任何实例的预测失败,则响应正文不会包含任何预测条目, 而是包含一个错误条目:

{
  "error": string
}

predictions[] 对象包含预测结果列表,其中每个预测结果对应请求中的一个实例。

如果发生错误,error 字符串会包含一条描述问题的消息。如果在处理任何实例时发生错误,则系统会返回错误,而非预测结果列表。

即使每个实例只有一个预测结果,预测结果的格式也与实例的格式没有直接关联。预测结果的格式是由模型中定义的输出集合所指定。预测结果集合会以 JSON 列表的形式返回。列表的每个成员可以是值、列表或任何复杂度的 JSON 对象。如果模型具有多个输出张量,则每个预测结果都是一个 JSON 对象,其中包含每个输出的键值对。这些键可标识图中的输出别名。

响应正文示例

以下示例展示了 TensorFlow 的一些可能响应:

  • 针对三个输入实例产生的一组预测结果,其中每个预测结果均为一个整数值:

    {"predictions":
      [5, 4, 3],
      "deployedModelId": 123456789012345678
    }
    
  • 一组较复杂的预测结果,各包含两个与输出张量相对应的命名值,分别名为 labelscoreslabel 的值是预测的类别(carbeach),scores 包含该实例在各可能类别之间的概率列表:

    {
      "predictions": [
        {
          "label": "beach",
          "scores": [0.1, 0.9]
        },
        {
          "label": "car",
          "scores": [0.75, 0.25]
        }
      ],
      "deployedModelId": 123456789012345678
    }
    
  • 处理输入实例出错时的响应:

    {"error": "Divide by zero"}