5.3. 使用 Dynamic Batch Size

5.3.1. 背景

由于 IPU 仅支持静态图, 在模型编译阶段需要指定固定的 batch size. 而在实际推理过程中, 输入数据的 batch size 通常情况下是不固定的. PopRT 通过补 0 的方式支持任意 batch size 大小的输入数据. 例如, Fig. 5.3 中模型的 shape 大小为 [4, 2], 并且 batch size 的维度是 0, 即 batch size 为 4. 而输入的数据 shape 大小分别为 [1, 2] 和 [7, 2], 即 batch size 分别为 1 和 7.

../_images/dynamic_batch_size.png

Fig. 5.3 Dynamic Batch Size

PopRT 通过补 0 将数据扩展为最近的 N * 模型 batch size 大小的数据. 比如上述的 batch size 为 1 的数据会通过补 0 扩展到 batch size 为 4 的大小, 而 batch size 为 7 的数据会扩展为 batch size 为 8 的大小. 在 IPU 中数据会按照模型的 batch size 进行推理, 如上述扩展后 batch size 为 4 的数据一次推理得到结果后返回, 而 batch size 为 8 的数据会循环推理两次后返回结果.

动态 batch size 的功能对于用户程序来说是透明的, 用户无需关心当前 IPU 中加载模型的 batch size 大小, 按照应用的需求发送推理请求数据就可以了.

5.3.2. 示例

Listing 5.2 中是动态 batch size 的示例代码. 示例中创建一个输入 shape 为 [4, 2] 的模型, 其 batch size 为 4. 应用程序分别使用 batch size 为 1, 4, 7 的数据进行推理, 无需考虑加载模型的 batch size 大小.

Listing 5.2 dynamic_batch_size.py
 1# Copyright (c) 2023 Graphcore Ltd. All rights reserved.
 2import datetime
 3
 4import numpy as np
 5import numpy.testing as npt
 6import onnx
 7
 8from onnx import helper
 9
10from poprt import runtime
11from poprt.compiler import Compiler, CompilerOptions
12from poprt.converter import Converter
13from poprt.runtime import RuntimeConfig
14from poprt.utils import get_logger
15
16
17def default_model():
18    """Create a test model."""
19    TensorProto = onnx.TensorProto
20    add = helper.make_node("Add", ["X", "Y"], ["O"])
21    graph = helper.make_graph(
22        [add],
23        "test",
24        [
25            helper.make_tensor_value_info("X", TensorProto.FLOAT, (4, 2)),
26            helper.make_tensor_value_info("Y", TensorProto.FLOAT, (4, 2)),
27        ],
28        [helper.make_tensor_value_info("O", TensorProto.FLOAT, (4, 2))],
29    )
30    opset_imports = [helper.make_opsetid("", 11)]
31    original_model = helper.make_model(graph, opset_imports=opset_imports)
32    return original_model
33
34
35def compile(model: onnx.ModelProto):
36    """Compile ONNX to PopEF."""
37    model_bytes = model.SerializeToString()
38    outputs = [o.name for o in model.graph.output]
39    executable = Compiler.compile(model_bytes, outputs)
40    return executable
41
42
43def run(executable):
44    """Run PopEF."""
45    config = RuntimeConfig()
46    config.timeout_ns = datetime.timedelta(microseconds=300)
47    config.batching_dim = 0
48    model_runner = runtime.ModelRunner(executable, config)
49    batch_sizes = [1, 4, 7]
50    for batch_size in batch_sizes:
51        inputs = {}
52        inputs['X'] = np.random.uniform(0, 1, [batch_size, 2]).astype(np.float32)
53        inputs['Y'] = np.random.uniform(0, 1, [batch_size, 2]).astype(np.float32)
54
55        outputs = {}
56        outputs['O'] = np.zeros([batch_size, 2], dtype=np.float32)
57        model_runner.execute(inputs, outputs)
58        expected = inputs['X'] + inputs['Y']
59        npt.assert_array_equal(
60            outputs['O'],
61            expected,
62            f"Result: outputs['O'] not equal with expected: {expected}",
63        )
64        print(f'Successfully run with input data in batch size {batch_size}')
65
66
67if __name__ == '__main__':
68    model = default_model()
69    executable = compile(model)
70    run(executable)

Download dynamic_batch_size.py

运行示例得到如下的输出信息:

Successfully run with input data in batch size 1
Successfully run with input data in batch size 4
Successfully run with input data in batch size 7