5. 模型运行

Section 2.3, IPU推理方案架构 中提到,IPU推理方案分为模型编译和模型运行时两个阶段。本章将介绍在用户的模型通过模型转换并编译为 PopEF 之后,如何通过 PopRTTriton Inference ServerTensorFlow Serving 部署和运行。

备注

  • 本章示例的命令和代码较长,推荐使用HTML版本的文档进行拷贝

  • PDF格式下拷贝命令和代码请注意空格、换行以及分页

5.1. 通过PopRT Runtime运行

PopRT Runtime是PopRT中用于加载和运行PopEF的运行时环境。PopRT Runtime提供Python和C++ API,可以用于PopEF的快速验证,或者与机器学习框架以及模型服务框架集成。本节将以 Section 4.1.4, 模型转换和编译 中生成的 executable.popef 为例,讲述如何使用PopRT Runtime API加载和运行PopEF。

5.1.1. 环境准备

将当前目录切换到 Section 4.1.4, 模型转换和编译 中的 executable.popef 所在的目录,并通过 ls 检查目录是否正确。

$ ls `pwd -P` | grep bertsquad-12_fp16_bs_16.onnx

显示如下的信息则证明当前目录是正确的。

bertsquad-12_fp16_bs_16.onnx

使用以下的命令启动容器:

$ gc-docker -- --rm -it \
    -v `pwd -P`:/model_runtime_test \
    -w /model_runtime_test \
    --entrypoint /bin/bash \
    graphcorecn/poprt-staging:latest

5.1.2. 通过Python API运行

Listing 5.1 中的示例代码保存为 model_runner_quick_start.py

Listing 5.1 PopRT Runtime Python API 代码示例
# Copyright (c) 2022 Graphcore Ltd. All rights reserved.

import numpy as np
from poprt import runtime

# Load the popef
runner = runtime.ModelRunner('executable.popef')

# Get the input and output information of the model
inputs = runner.get_model_inputs()
outputs = runner.get_model_outputs()

# Create the random inputs and zero outputs
inputs_dict = {
    x.name: np.random.randint(2, size=x.shape).astype(x.numpy_data_type())
    for x in inputs
}
outputs_dict = {
    x.name: np.zeros(x.shape).astype(x.numpy_data_type()) for x in outputs
}

# Execute the inference
runner.execute(inputs_dict, outputs_dict)

# Check the output values
for name, value in outputs_dict.items():
    print(f'{name} : {value}')

model_runner_quick_start.py

运行保存的示例代码:

$ python3 model_runner_quick_start.py

成功运行将得到类似下面的输出:

unstack:1 : [[-0.9604 -1.379  -2.01   ... -1.814  -1.78   -1.626 ]
[-1.051  -1.977  -1.913  ... -1.435  -1.681  -1.251 ]
[-3.67   -2.71   -2.78   ... -3.951  -4.027  -3.959 ]
...
[-0.0919 -0.6445 -0.3125 ... -0.384  -0.54   -0.3152]
[-0.69   -1.071  -1.421  ... -1.533  -1.456  -1.389 ]
[-3.56   -2.99   -3.23   ... -4.05   -3.977  -3.955 ]]
unstack:0 : [[-1.437  -1.645  -2.17   ... -2.139  -2.379  -2.281 ]
[-1.259  -1.8545 -1.915  ... -1.804  -1.8955 -1.671 ]
[-2.832  -2.057  -2.104  ... -3.29   -3.34   -3.36  ]
...
[-0.4673 -0.8716 -0.8545 ... -1.253  -1.287  -1.289 ]
[-1.288  -1.481  -1.928  ... -2.158  -2.146  -2.129 ]
[-2.762  -2.43   -2.6    ... -3.418  -3.23   -3.324 ]]
unique_ids:0 : [1 0 1 1 1 0 0 1 1 0 1 1 1 1 0 1]

5.1.3. 通过C++ API运行

PopRT Runtime也提供了C++ API,将 Listing 5.2 中的代码保存为 model_runner_quick_start.cpp

Listing 5.2 Sample PopRT Runtime code (C++ API)
// Copyright (c) 2022 Graphcore Ltd. All rights reserved.

#include <iostream>
#include "poprt/runtime/model_runner.hpp"

int main(int argc, char* argv[]) {
    // Load the PopEF file
    auto runner = poprt::runtime::ModelRunner("executable.popef");

    // Get the inputs and outputs information of model
    auto inputs = runner.getModelInputs();
    auto outputs = runner.getModelOutputs();

    // Create the inputs and ouputs
    poprt::runtime::InputMemoryView in;
    poprt::runtime::OutputMemoryView out;
    std::vector<std::shared_ptr<unsigned char[]>> memories;
    int i=0;
    for(const auto& input : inputs){
        memories.push_back(std::shared_ptr<unsigned char[]>(new unsigned char[input.sizeInBytes]));
        in.emplace(input.name, poprt::runtime::ConstTensorMemoryView(memories[i++].get(), input.sizeInBytes));
    }
    for(const auto& output : outputs){
        memories.push_back(std::shared_ptr<unsigned char[]>(new unsigned char[output.sizeInBytes]));
        out.emplace(output.name, poprt::runtime::TensorMemoryView(memories[i++].get(), output.sizeInBytes));
    }

    // Execute the inference
    runner.execute(in, out);

    // Print the result information
    std::cout << "Sucessfully executed. The outputs are: " << std::endl;
    for(const auto& output: outputs)
        std::cout << "name: " << output.name
                << ", dataType: " << output.dataType
                << ", sizeInBytes: " << output.sizeInBytes
                << std::endl;
}

model_runner_quick_start.cpp

编译代码:

$ apt-get update && \
  apt-get install g++ -y && \
  g++ model_runner_quick_start.cpp -o model_runner_quick_start \
    --std=c++14 -I/usr/local/lib/python3.8/dist-packages/poprt/include \
    -L/usr/local/lib/python3.8/dist-packages/poprt/lib \
    -lpoprt_runtime -lpoprt_compiler -lpopef

运行编译得到的程序:

$ LD_LIBRARY_PATH=/usr/local/lib/python3.8/dist-packages/poprt/lib:$LD_LIBRARY_PATH ./model_runner_quick_start

成功运行会得到如下的输出:

Sucessfully execute, the outputs:
name: unstack:1, dataType: F16, sizeInBytes: 8192
name: unstack:0, dataType: F16, sizeInBytes: 8192
name: unique_ids:0, dataType: S32, sizeInBytes: 64

备注

完成上述示例后,请退出当前容器,回到主机环境

5.2. 部署到Trition Inference server

Graphcore通过Poplar Model Runtime实现了 libtriton_poplar.so ,它作为Trition Inference Server的plugin包含在Poplar SDK中,负责加载和运行PopEF。 更多关于Poplar Triton Backend的信息,请参考 Poplar Triton Backend: User Guide

本节将以 Section 4.1.4, 模型转换和编译 中生成的 executable.popef 文件为例,讲解如何将编译出来的PopEF部署到Trition Inference Server。

5.2.1. 环境准备

首先将当前目录切换到 Section 4.1.4, 模型转换和编译 中生成的 executable.popef 所在的目录,通过以下命令检查目录是否正确。

$ ls `pwd -P` | grep bertsquad-12_fp16_bs_16.onnx

显示如下的信息则证明当前目录是正确的:

bertsquad-12_fp16_bs_16.onnx

创建一个 model_repository 的目录。

$ mkdir -p model_repository/bertsquad-12/1/ && \
  cp executable.popef model_repository/bertsquad-12/1/ && \
  touch model_repository/bertsquad-12/config.pbtxt && \
  cd model_repository

目录结构如下:

$ tree .
.
└── bertsquad-12
    ├── 1
    │  └── executable.popef
    └── config.pbtxt
  • bertsquad-12 是模型的名字

  • 1 表示模型的版本

  • executable.popef 是模型编译产生的PopEF文件

  • config.pbtxt 是在 生成模型的配置 中说明的Trition配置文件

备注

当模型的输入输出中包含Triton Inference Server不能处理的特殊时,可以使用Poplar Triton Backend的输入输出名称映射的功能。请参考 Poplar Triton Backend: User GuideInput/Output Name Mapping 的信息。

5.2.2. 生成模型的配置

部署模型到Triton Inference Server需要为这个模型创建一个配置文件 config.pbtxt ,主要包含模型的名称、使用的backend、batching的信息和输入输出等信息,更多关于模型配置的内容请参考 Triton Model Configuration 文档。

本例中使用的 config.pbtxt 配置如下,需要将内容拷贝到上述产生的空的 config.pbtxt 中。

Listing 5.3 配置文件 config.pbtxt 示例
name: "bertsquad-12"
backend: "poplar"
max_batch_size: 16
dynamic_batching {
   preferred_batch_size: [16]
   max_queue_delay_microseconds: 5000
}
input [
   {
      name: "input_ids:0"
      data_type: TYPE_INT32
      dims: [ 256 ]
   },
   {
      name: "input_mask:0"
      data_type: TYPE_INT32
      dims: [ 256 ]
   },
   {
      name: "segment_ids:0"
      data_type: TYPE_INT32
      dims: [ 256 ]
   },
   {
      name: "unique_ids_raw_output___9:0"
      data_type: TYPE_INT32
      dims: [ 1 ]
      reshape: { shape: [ ] }
   }
]
output [
   {
      name: "unique_ids:0"
      data_type: TYPE_INT32
      dims: [ 1 ]
      reshape: { shape: [ ] }
   },
   {
      name: "unstack:0"
      data_type: TYPE_FP16
      dims: [ 256 ]
   },
   {
      name: "unstack:1"
      data_type: TYPE_FP16
      dims: [ 256 ]
   }
]
parameters [
   {
      key: "synchronous_execution"
      value:{string_value: "1"}
   },
   {
      key: "timeout_ns"
      value:{string_value: "500000"}
   }
]

模型名称

bertsquad-12,通常与模型所在的目录名一致

Backend

poplar,指明使用Poplar Triton Backend

Batching

Poplar Triton Backend支持动态batch,建议 max_batch_sizepreferred_batch_size 的值设置为模型batch size的整数倍。本例中模型的batch size为16,简单起见直接设置两个参数值为batch size的大小。

输入和输出

输入和输出的名字、类型和维度信息,可以通过 popef_dump 命令获取。更多关于 popef_dump 的信息,请参考 PopEF: User Guide 中的 PopEF file analysis

$ gc-docker -- --rm \
    -v `pwd -P`:/models \
    --entrypoint popef_dump \
    graphcorecn/toolkit-triton-staging:latest \
    /models/bertsquad-12/1/executable.popef

popef_dump 的输出信息节选如下:

PopEF file: executable.popef
Metadata:
...
Anchors:
Inputs (User-provided):
Name: "input_ids:0":
   TensorInfo: { dtype: S32, sizeInBytes: 16384, shape [16, 256] }
   Programs: [5]
   Handle: h2d_input_ids:0
   IsPerReplica: **False**
...
Outputs (User-provided):
Name: "unique_ids:0":
   TensorInfo: { dtype: S32, sizeInBytes: 64, shape [16] }
   Programs: [5]
   Handle: anchor_d2h_unique_ids:0
   IsPerReplica: **False**
...

从上述节选的 popef_dump 的部分输出,可以看到PopEF的模型输入输出与模型配置文件中输入输出的对应关系。其中 dtype 的数据类型,可以参考 PopEF Tensor and Feed DataTypes supported by Poplar Triton Backend

max_batch_size 的设置不为0的情况下, 模型配置文件中 dims 的维度信息是不包含batch size这一维的,比如 input_ids:0 的维度是 [ 16, 256 ] ,在模型配置文件中配置为 dims: [ 256 ] 。 对于仅有batch size这一个维度的输入输出,如 unique_ids:0 ,需要设置 dims: [ 1 ] 并使用 reshape: { shape: [ ] } 将维度转换成一维。更多关于维度设置相关的内容,请参考 Triton Model Configuration 文档。

更多关于 config.pbtxt 配置文件中的字段信息,请参考 Poplar Triton Backend 文档中的 Triton model configuration

5.2.3. 启动模型服务

通过 gc-docker 启动容器,如果主机上没有安装Poplar SDK请参考 Section 3.7.2, 使用docker run启动容器

$ gc-docker -- --rm \
    --network=host \
    -v `pwd -P`:/models \
    graphcorecn/toolkit-triton-staging:latest

备注

如果在IPU-M2000或Bow-2000的环境中测试,运行 gc-docker 时请删除 --network=host 参数

打印出如下信息,说明服务已经启动,可以接受 gRPCHTTP 的请求。

Started GRPCInferenceService at 0.0.0.0:8001
Started HTTPService at 0.0.0.0:8000

通过gRPC验证服务

以下是通过 Triton Client gRPC API 测试部署模型的例子,更详细的API信息请参考其文档和代码示例。

import numpy as np
import tritonclient.grpc as gc

# create the triton client
triton_client = gc.InferenceServerClient(
    url = "localhost:8001")

model_name = 'bertsquad-12'

inputs = []
outputs = []

inputs.append(gc.InferInput('input_ids:0', [16,256], "INT32"))
inputs.append(gc.InferInput('input_mask:0', [16,256], "INT32"))
inputs.append(gc.InferInput('segment_ids:0', [16,256], "INT32"))
inputs.append(gc.InferInput('unique_ids_raw_output___9:0', [ 16,1 ], "INT32"))

# create data
input0_data = np.random.randint(0, 1000, size=(16,256)).astype(np.int32)
input1_data = np.random.randint(0, 1, size=(16,1)).astype(np.int32)

for i in range(3):
    inputs[i].set_data_from_numpy(input0_data)
inputs[3].set_data_from_numpy(input1_data)

outputs_names = ['unique_ids:0', 'unstack:0', 'unstack:1']

for name in outputs_names:
    outputs.append(gc.InferRequestedOutput(name))

results = triton_client.infer(
model_name = model_name,
inputs = inputs,
outputs = outputs)

statistics = triton_client.get_inference_statistics(model_name=model_name)
if len(statistics.model_stats) != 1:
    print("FAILED: Inference Statistics")
    sys.exit(1)
print(statistics)

for name in outputs_names:
    print(f'{name} = {results.as_numpy(name)}')

grpc_test.py

打开新的终端连接到主机,将以上代码保存到 grpc_test.py,创建python虚拟环境并测试。

$ virtualenv -p python3 venv && \
  source venv/bin/activate && \
  pip install tritonclient[all] && \
  python grpc_test.py && \
  deactivate

正确执行,则返回模型统计信息和推理结果。

model_stats {
name: "bertsquad-12"
version: "1"
last_inference: 1667439772895
inference_count: 64
execution_count: 4
inference_stats {
   success {
   count: 4
   ns: 170377440
   }
...
unique_ids:0 = [[0]
...
unstack:0 = [[-0.991 -1.472 -1.571 ... -1.738 -1.77 -1.803]
...
unstack:1 = [[-0.9023 -1.285 -1.325 ... -1.419 -1.441 -1.452 ]
...

通过HTTP验证服务

以下是通过 Triton Client HTTP API 测试部署模型的例子,更详细的API信息请参考其文档和代码示例。

import numpy as np
import tritonclient.http as hc

# create the triton client
triton_client = hc.InferenceServerClient(
    url = "localhost:8000")

model_name = 'bertsquad-12'

inputs = []
outputs = []

inputs.append(hc.InferInput('input_ids:0', [16,256], "INT32"))
inputs.append(hc.InferInput('input_mask:0', [16,256], "INT32"))
inputs.append(hc.InferInput('segment_ids:0', [16,256], "INT32"))
inputs.append(hc.InferInput('unique_ids_raw_output___9:0', [ 16,1 ], "INT32"))

# create data
input0_data = np.random.randint(0, 1000, size=(16,256)).astype(np.int32)
input1_data = np.random.randint(0, 1, size=(16,1)).astype(np.int32)

for i in range(3):
    inputs[i].set_data_from_numpy(input0_data)
inputs[3].set_data_from_numpy(input1_data)

outputs_names = ['unique_ids:0', 'unstack:0', 'unstack:1']

for name in outputs_names:
    outputs.append(hc.InferRequestedOutput(name, binary_data=True))

results = triton_client.infer(
model_name = model_name,
inputs = inputs,
outputs = outputs)

statistics = triton_client.get_inference_statistics(model_name=model_name, headers=None)
if len(statistics['model_stats']) != 1:
    print("FAILED: Inference Statistics")
    sys.exit(1)
print(statistics)

for name in outputs_names:
    print(f'{name} = {results.as_numpy(name)}')

triton_client_http_test.py (重命名为 http_test.py)

打开新的终端连接到主机,将以上代码保存到 http_test.py,创建Python虚拟环境并测试。

# Execute the following virtualenv command if the python virtual environment has not been created
# virtualenv -p python3 venv

$ source venv/bin/activate && \
  pip install tritonclient[all] && \
  python http_test.py && \
  deactivate

正确执行,则返回模型统计信息和推理结果。

{'model_stats': [{'name': 'bertsquad-12', 'version': '1', 'last_inference': 1667440001420, 'inference_count': 80, ... {'count': 5, 'ns': 462978}}]}]}
unique_ids:0 = [[0]
...
unstack:0 = [[-0.753  -1.183  -1.296  ... -1.595  -1.599  -1.65  ]
...
unstack:1 = [[-0.6206 -0.9683 -1.031  ... -1.222  -1.221  -1.241 ]
...

备注

本示例结束,请按<ctrl+C>退出Triton容器,回到主机环境

5.3. 部署到TensorFlow Serving

本节将以 Section 4.2.2, 模型转换和编译 中生成的 executable.popef 文件为例,讲解如何将编译出来的PopEF部署到 TensorFlow Serving

5.3.1. 环境准备

通过 gc-docker 启动容器,如果主机上没有安装Poplar SDK请参考 Section 3.7.2, 使用docker run启动容器 。 启动docker前,将 MODEL_PATH 指向包含 resnet_v2_50_optimized.onnx 的路径:

$ export MODEL_PATH=/path/to/your/models
$ ls $MODEL_PATH

如果 MODEL_PATH 指定的正确,将会看到如下信息:

resnet_v2_50_optimized.onnx executable.popef ...

5.3.2. 生成SavedModel模型

TensorFlow Serving的输入模型格式是SavedModel,所以我们需要将PopEF文件封装在一个model_runtime自定义算子中,使得TensorFlow在运行该算子时,可以通过调用 Poplar Model Runtime API来运行PopEF模型文件。

$ gc-docker -- --rm \
   -v $MODEL_PATH:/model_path \
   graphcorecn/toolkit-tfserving-staging:latest \
   /bin/bash -c "python3 -m popef2tf.convert \
   --model /model_path/executable.popef \
   --name resnet_v2_50_serving \
   --version 001 \
   --output /model_path"

备注

该镜像使用的TensorFlow为通过官方源代码编译得到的TensorFlow-2.6.5,并非Poplar SDK中提供的TensorFlow。

因此,这里使用 popef2tf 生成TensorFlow的SavedModel模型,其中包含了 model_runtime 自定义算子以执行指定的PopEF模型。其中, popef2tf 工具的输入参数 modeloutput 分别指定了输入PopEF文件的路径和输出SavedModel的路径。

通过 tree 可以观察生成的SavedModel文件目录如下:

$ tree $MODEL_PATH/resnet_v2_50_serving
resnet_v2_50_serving
└── 001
    ├── assets
    │   └── executable.popef
    ├── saved_model.pb
    └── variables
        ├── variables.data-00000-of-00001
        └── variables.index

5.3.3. 启动模型服务

TensorFlow Serving提供了batching功能,而IPU使用静态图,需要确定 batch_sizeinput_shape。对于该模型,我们使用固定shape为[4,3,224,224]的输入tensor尺寸,其所对应的 batch_size 为4。

因此,我们需要在部署时,通过配置文件设置 allowed_batch_sizesmax_batch_size 为4,使客户端可以向服务器发送batch_size为[1-4]间任意整数的数据。更多关于 allowed_batch_sizes 的信息请参考 batching_effect

MODEL_PATH 目录下创建配置文件并编辑:

$ touch $MODEL_PATH/resnet_v2_50_serving/001/resnet_bs4_3_224_224.conf
$ vim $MODEL_PATH/resnet_v2_50_serving/001/resnet_bs4_3_224_224.conf

在该文件中添加:

allowed_batch_sizes: 4
max_batch_size {value: 4}

开启Serving服务:

$ gc-docker -- --rm \
   -v $MODEL_PATH:/model_path \
   --network=host \
   graphcorecn/toolkit-tfserving-staging:latest \
   /bin/bash -c "tensorflow_model_server \
   --rest_api_port=8501 \
   --model_name=resnet_v2_50 \
   --enable_batching \
   --batching_parameters_file=/model_path/resnet_v2_50_serving/001/resnet_bs4_3_224_224.conf \
   --model_base_path=/model_path/resnet_v2_50_serving"

其中,参数 --enable_batching 用于开启 batching 功能, --batching_parameters_file 指定了配置文件的路径。

备注

如果在IPU-M2000或Bow-2000上测试,运行 gc-docker 时请去掉 --network=host 参数

备注

该镜像使用的 TensorFlow Serving 是通过官方源代码编译得到的TensorFlow Serving-2.6.5 (TensorFlow-2.6.5),并支持 model_runtime 自定义算子。

如果无需开启batching功能,在启动该容器时去除 --enable_batching--batching_parameters_file 2个参数即可。此时,客户端仅能处理 batch_size=4 的输入数据。

容器启动后,输出log如下:

Building single TensorFlow model file config: model_name: resnet_v2_50 model_base_path: /model_path/resnet_v2_50_serving
Adding/updating models.
**(Re-)**\ adding model: resnet_v2_50
Successfully reserved resources to load servable {name: resnet_v2_50 version: 1}
Approving load for servable version {name: resnet_v2_50 version: 1}
Loading servable version {name: resnet_v2_50 version: 1}
Reading SavedModel from: /model_path/resnet_v2_50_serving/001
Reading meta graph with tags { serve }
Reading SavedModel debug info (if present) from: /model_path/resnet_v2_50_serving/001
...
Successfully loaded servable version {name: resnet_v2_50 version: 1}
...
Exporting HTTP/REST API at:localhost:8501 ...

resnet_v2_50 模型服务已发布到8501端口,客户端可以通过RESTful API来调用该服务。

开启或关闭batching功能

在运行TensorFlow Serving可以开启或者关闭batching功能。本节将讨论关于 allowed_batch_sizes 在batching开启和关闭时的作用。

  • 开启batching并设置 allowed_batch_sizes 的值为4

    客户端可以发送 batch size 大小为[1-4]之间的数据,如果batch size小于4,服务器端将会填充数据到 allowed_batch_sizesmax_batch_size ,即4这个大小。例如,客户端发送的数据大小为[1, 3, 224, 224],服务器端会通过空数据将其填充到[4, 3, 224, 224]的大小。

  • 开启batching但不设置 allowed_batch_sizes

    服务器端将接受 batch size 为[1-4]的数据,但PopEF只能接受 batch size 为4的数据。例如,客户端发送大小为[2, 3, 224, 224]的数据,服务器只检查大小是否超过 max_batch_size,而并不会填充数据。由于数据的大小并没有超过设置的最大阈值,服务器会将其发送给TensorFlow后端,这将引发数据大小不匹配的错误。

    而如果发送大小为[5, 3, 224, 224]的数据时,因为超出了 max_batch_size 设置的阈值,会引发超出最大batch size的错误。

  • 禁用Batching

    客户端仅允许发送 batch_size==4 的数据。例如,当客户端发送大小为[5, 3, 224, 224]的请求时,同样会导致数据大小不匹配的错误。

5.3.4. 通过HTTP验证服务

本例会下载一组图片作为运行示例,演示如何通过RESTful API调用模型服务:

import numpy as np
import json, urllib3
import tempfile, wget
from PIL import Image

def read_labels_file(fname):
    outs = []
    with open(fname, 'r') as fin:
        for line in fin.readlines():
            outs.append(line.strip())
    return outs

def read_images(files, img_h, img_w):
    images = []
    for file in files:
        image = Image.open(file)
        image = image.resize((img_h, img_w))
        image = (np.array(image) / 255).astype(np.float32)
        image = image.transpose((2, 0, 1))
        images.append(image[np.newaxis, :])
    return images

def main():
    # image urls
    urls = [
            'http://images.cocodataset.org/test-stuff2017/000000024309.jpg',
            'http://images.cocodataset.org/test-stuff2017/000000028117.jpg',
            'http://images.cocodataset.org/test-stuff2017/000000006149.jpg',
            'http://images.cocodataset.org/test-stuff2017/000000004954.jpg',
        ]

    # download images
    image_files = []
    with tempfile.TemporaryDirectory() as tmpdir:
        for url in urls:
            image_files.append(wget.download(url, tmpdir))
        images = read_images(image_files, 224, 224)

        label_file = wget.download(
            "https://storage.googleapis.com/download.tensorflow.org/data/ImageNetLabels.txt", tmpdir)
        labels = read_labels_file(label_file)

    # get input json
    input_data = np.concatenate(images, axis=0)
    input_data_list = input_data.tolist()
    postData = {'inputs':input_data_list}
    jPostData = json.dumps(postData)

    http = urllib3.PoolManager()
    r = http.request('POST','http://localhost:8501/v1/models/resnet_v2_50:predict',body=jPostData)

    return_data = json.loads(r.data)
    output = np.array(list(return_data.values()))

    # get real images top5
    k = 5
    idx = output.argsort()[:,:,-1:-k-1:-1]
    for b,idx_bs in enumerate(idx[0]):
        print(f'\nimage{b}:')
        for i in idx_bs:
            print(labels[i], output[0, b, i])

if __name__ == "__main__":
    main()

restful_http_test.py

打开新的终端连接到主机,将以上代码保存到 restful_http_test.py,创建Python虚拟环境并测试。

virtualenv -p python3 venv
source venv/bin/activate
pip install pillow numpy urllib3 wget
python restful_http_test.py
deactivate

正确执行,则返回结果示例如下:

image0:
laptop 18.2867374
notebook 15.9842319
desk 13.6191549
web site 11.1951714
mouse 11.1430635

image1:
mashed potato 14.4165163
guacamole 11.8748741
meat loaf 10.7978802
cheeseburger 9.09055805
plate 8.93529892

image2:
knee pad 8.52720547
volleyball 8.52627659
racket 8.31885242
ski 7.84297752
horizontal bar 7.11924124

image3:
hare 17.0646706
fountain 15.7413292
tennis ball 13.2553062
wallaby 12.6554518
wood rabbit 11.5797682