体验Python极简并发推理Demo
概述
本教程提供了MindSpore Lite执行并发推理的示例程序。通过创建并发推理配置,并发Runner加载与编译,设置并发推理任务,执行并发推理的方式,演示了Python接口进行服务端并发推理的基本流程,用户能够快速了解MindSpore Lite执行并发推理相关API的使用。相关代码放置在mindspore/lite/examples/quick_start_server_inference_python目录。
本教程模拟的使用场景为:当服务器同时收到多台客户端请求的推理任务时,利用支持并发推理的ModelParallelRunner接口,同时进行多个推理任务的推理,并将推理结果返还给客户端。
下面以Ubuntu 18.04为例,介绍了在Linux X86操作系统配合CPU硬件平台下如何使用Python极简并发推理Demo:
一键安装
本环节以全新的Ubuntu 18.04为例,介绍在CPU环境的Linux-x86_64系统上,通过pip安装Python3.7版本的MindSpore Lite。
进入到mindspore/lite/examples/quick_start_server_inference_python目录下,以安装1.9.0版本的MindSpore Lite为例,执行lite-server-cpu-pip.sh
脚本进行一键式安装。安装脚本会下载并发推理所需的模型和输入数据文件、安装MindSpore_Lite所需的依赖,以及下载并安装支持并发推理功能的MindSpore Lite。
注:此命令可设置安装的MindSpore Lite版本,由于从MindSpore Lite 1.8.0版本开始支持Python接口,因此版本不能设置低于1.8.0,可设置的版本详情参见下载MindSpore Lite提供的版本。
MINDSPORE_LITE_VERSION=1.9.0 bash ./lite-server-cpu-pip.sh
若MobileNetV2模型下载失败,请手动下载相关模型文件mobilenetv2.ms,并将其拷贝到
mindspore/lite/examples/quick_start_server_inference_python/model
目录。若input.bin输入数据文件下载失败,请手动下载相关输入数据文件input.bin,并将其拷贝到
mindspore/lite/examples/quick_start_server_inference_python/model
目录。若使用脚本下载MindSpore Lite并发推理框架失败,请手动下载对应硬件平台为CPU、操作系统为Linux-x86_64的MindSpore Lite 并发模型推理框架或对应硬件平台为CPU、操作系统为Linux-aarch64的MindSpore Lite 并发模型推理框架,用户可以使用
uname -m
命令在终端上查询操作系统。并将其拷贝到mindspore/lite/examples/quick_start_server_inference_python
目录下。若需要使用Python3.7以上版本对应的MindSpore Lite,请在本地编译,注意Python API模块编译依赖:Python >= 3.7.0、NumPy >= 1.17.0、wheel >= 0.32.0。注意要生成支持并发推理的MindSpore Lite安装包,编译前需要配置环境变量:export MSLITE_ENABLE_SERVER_INFERENCE=on。编译成功后,将
output/
目录下生成的Whl安装包拷贝到mindspore/lite/examples/quick_start_server_inference_python
目录下。若
mindspore/lite/examples/quick_start_server_inference_python
目录下不存在MindSpore Lite安装包,则一键安装脚本将会卸载当前已安装的MindSpore Lite后,从华为镜像下载并安装MindSpore Lite。否则,若目录下存在MindSpore Lite安装包,则会优先安装该安装包。通过手动下载并且将文件放到指定位置后,需要再次执行lite-server-cpu-pip.sh脚本才能完成一键安装。
执行成功将会显示如下结果,模型文件和输入数据文件可在mindspore/lite/examples/quick_start_server_inference_python/model
目录下找到。
Successfully installed mindspore-lite-1.9.0
执行Demo
一键安装后,进入mindspore/lite/examples/quick_start_server_inference_python目录,并执行以下命令,体验MindSpore Lite并发推理MobileNetV2模型。
python quick_start_server_inferece_python.py
执行完成后将能得到如下结果。在多线程执行并发推理任务过程中,打印并发推理中的单次推理耗时和推理结果,结束线程后打印并发推理总耗时。
推理结果说明:
模拟5台客户端同时向服务器发送并发推理任务的请求,
parallel id
表示客户端id。模拟每个客户端向服务器发送2个推理任务的请求,
task index
表示任务的序号。run once time
表示每个客户端单次请求的推理任务的推理时长。接下来显示每个客户端单次请求的推理任务的推理结果,包括输出Tensor的名称、输出Tensor的数据大小,输出Tensor的元素数量以及前5个数据。
total run time
表示服务器完成所有并发推理任务的总耗时。parallel id: 0 | task index: 1 | run once time: 0.024082660675048828 s tensor name is:Softmax-65 tensor size is:4004 tensor elements num is:1001 output data is: 1.02271215e-05 9.92699e-06 1.6968432e-05 6.8573616e-05 9.731416e-05 parallel id: 2 | task index: 1 | run once time: 0.029989957809448242 s tensor name is:Softmax-65 tensor size is:4004 tensor elements num is:1001 output data is: 1.02271215e-05 9.92699e-06 1.6968432e-05 6.8573616e-05 9.731416e-05 parallel id: 1 | task index: 1 | run once time: 0.03409552574157715 s tensor name is:Softmax-65 tensor size is:4004 tensor elements num is:1001 output data is: 1.02271215e-05 9.92699e-06 1.6968432e-05 6.8573616e-05 9.731416e-05 parallel id: 3 | task index: 1 | run once time: 0.04005265235900879 s tensor name is:Softmax-65 tensor size is:4004 tensor elements num is:1001 output data is: 1.02271215e-05 9.92699e-06 1.6968432e-05 6.8573616e-05 9.731416e-05 parallel id: 4 | task index: 1 | run once time: 0.04981422424316406 s tensor name is:Softmax-65 tensor size is:4004 tensor elements num is:1001 output data is: 1.02271215e-05 9.92699e-06 1.6968432e-05 6.8573616e-05 9.731416e-05 parallel id: 0 | task index: 2 | run once time: 0.028667926788330078 s tensor name is:Softmax-65 tensor size is:4004 tensor elements num is:1001 output data is: 1.02271215e-05 9.92699e-06 1.6968432e-05 6.8573616e-05 9.731416e-05 parallel id: 2 | task index: 2 | run once time: 0.03010392189025879 s tensor name is:Softmax-65 tensor size is:4004 tensor elements num is:1001 output data is: 1.02271215e-05 9.92699e-06 1.6968432e-05 6.8573616e-05 9.731416e-05 parallel id: 3 | task index: 2 | run once time: 0.030695676803588867 s tensor name is:Softmax-65 tensor size is:4004 tensor elements num is:1001 output data is: 1.02271215e-05 9.92699e-06 1.6968432e-05 6.8573616e-05 9.731416e-05 parallel id: 1 | task index: 2 | run once time: 0.04117941856384277 s tensor name is:Softmax-65 tensor size is:4004 tensor elements num is:1001 output data is: 1.02271215e-05 9.92699e-06 1.6968432e-05 6.8573616e-05 9.731416e-05 parallel id: 4 | task index: 2 | run once time: 0.028671741485595703 s tensor name is:Softmax-65 tensor size is:4004 tensor elements num is:1001 output data is: 1.02271215e-05 9.92699e-06 1.6968432e-05 6.8573616e-05 9.731416e-05 total run time: 0.08787751197814941 s
Demo内容说明
使用MindSpore Lite执行并发推理主要包括以下步骤:
关键变量说明:说明并发推理中用到的关键变量。
创建并发推理配置:创建并发推理配置选项RunnerConfig,保存需要的一些基本配置参数,用于执行并发推理的初始化。
并发Runner加载与编译:执行并发推理之前,需要调用
ModelParallelRunner
的init接口进行并发Runner的初始化,主要进行模型读取,加载RunnerConfig
配置,创建并发,以及子图切分、算子选型调度。可将ModelParallelRunner
理解为支持并发推理的model
。该阶段会耗费较多时间,所以建议ModelParallelRunner
初始化一次,多次执行并发推理。设置并发推理任务:创建多线程,绑定并发推理任务。推理任务包括向
输入Tensor
中填充数据、使用ModelParallelRunner
的predict接口进行并发推理和通过输出Tensor
得到推理结果。执行并发推理:启动多线程,执行配置好的并发推理任务。执行过程中,打印并发推理中的单次推理耗时和推理结果,结束线程后打印并发推理总耗时。
更多Python接口的高级用法与示例,请参考Python API。
关键变量说明
THREAD_NUM
:单个worker的线程数量。WORKERS_NUM * THREAD_NUM
应该小于机器核心数量。WORKERS_NUM
:在服务器端,指定在一个ModelParallelRunner
中的workers的数量,即执行并发推理的单元。若想对比并发推理和非并发推理的推理时长差异,可以将WORKERS_NUM
设置为1进行对比。PARALLEL_NUM
:并发数量,即同时在发送推理任务请求的客户端数量。TASK_NUM
:任务数量,即单个客户端发送的推理任务请求的数量。import time from threading import Thread import numpy as np import mindspore_lite as mslite # the number of threads of one worker. # WORKERS_NUM * THREAD_NUM should not exceed the number of cores of the machine. THREAD_NUM = 1 # In parallel inference, the number of workers in one `ModelParallelRunner` in server. # If you prepare to compare the time difference between parallel inference and serial inference, # you can set WORKERS_NUM = 1 as serial inference. WORKERS_NUM = 3 # Simulate 5 clients, and each client sends 2 inference tasks to the server at the same time. PARALLEL_NUM = 5 TASK_NUM = 2
创建并发推理配置
创建并发推理配置RunnerConfig
。由于本教程演示的是在CPU设备上执行推理的场景,因此需要将创建的CPU设备硬件信息加入上下文Conterxt
中,再将Conterxt
加入RunnerConfig
中。
# Init RunnerConfig and context, and add CPU device info
cpu_device_info = mslite.CPUDeviceInfo(enable_fp16=False)
context = mslite.Context(thread_num=THREAD_NUM, inter_op_parallel_num=THREAD_NUM)
context.append_device_info(cpu_device_info)
parallel_runner_config = mslite.RunnerConfig(context=context, workers_num=WORKERS_NUM)
并发Runner加载与编译
调用ModelParallelRunner
的init接口进行并发Runner的初始化,主要进行模型读取,加载RunnerConfig
配置,创建并发,以及子图切分、算子选型调度。可将ModelParallelRunner
理解为支持并发推理的Model
。该阶段会耗费较多时间,所以建议ModelParallelRunner
初始化一次,多次执行并发推理。
# Build ModelParallelRunner from file
model_parallel_runner = mslite.ModelParallelRunner()
model_parallel_runner.init(model_path="./model/mobilenetv2.ms", runner_config=parallel_runner_config)
设置并发推理任务
创建多线程,绑定并发推理任务。推理任务包括向输入Tensor
中填充数据、使用ModelParallelRunner
的predict接口进行并发推理和通过输出Tensor
得到推理结果。
def parallel_runner_predict(parallel_runner, parallel_id):
"""
One Runner with 3 workers, set model input, execute inference and get output.
Args:
parallel_runner (mindspore_lite.ModelParallelRunner): Actuator Supporting Parallel inference.
parallel_id (int): Simulate which client's task to process
"""
task_index = 0
while True:
if task_index == TASK_NUM:
break
task_index += 1
# Set model input
inputs = parallel_runner.get_inputs()
in_data = np.fromfile("./model/input.bin", dtype=np.float32)
inputs[0].set_data_from_numpy(in_data)
once_start_time = time.time()
# Execute inference
outputs = []
parallel_runner.predict(inputs, outputs)
once_end_time = time.time()
print("parallel id: ", parallel_id, " | task index: ", task_index, " | run once time: ",
once_end_time - once_start_time, " s")
# Get output
for output in outputs:
tensor_name = output.get_tensor_name().rstrip()
data_size = output.get_data_size()
element_num = output.get_element_num()
print("tensor name is:%s tensor size is:%s tensor elements num is:%s" % (tensor_name, data_size,
element_num))
data = output.get_data_to_numpy()
data = data.flatten()
print("output data is:", end=" ")
for j in range(5):
print(data[j], end=" ")
print("")
# The server creates 5 threads to store the inference tasks of 5 clients.
threads = []
total_start_time = time.time()
for i in range(PARALLEL_NUM):
threads.append(Thread(target=parallel_runner_predict, args=(model_parallel_runner, i,)))
执行并发推理
启动多线程,执行配置好的并发推理任务。执行过程中,打印并发推理中的单次推理耗时和推理结果,结束线程后打印并发推理总耗时。
# Start threads to perform parallel inference.
for th in threads:
th.start()
for th in threads:
th.join()
total_end_time = time.time()
print("total run time: ", total_end_time - total_start_time, " s")