多Batch模型推理
多Batch模型推理
多Batch推理的基本流程与单Batch类似,请参见模型推理基本场景。
多Batch推理与单Batch推理的不同点在于:
- 多Batch场景下,在构建模型时,使用ATC工具的input_shape参数需设置具体的Batch数,详细说明请参见《ATC工具使用指南》。
- 在推理前,需要编写一段代码逻辑:等输入数据满足多Batch(例如:8Batch)的要求,申请Device上的内存存放多Batch的数据,作为模型推理的输入。如果最后循环遍历所有的输入数据后,仍不满足多Batch的要求,则直接将剩余数据作为模型推理的输入。
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝运行,仅供参考,此处以8Batch为例:
batch_size = 8
device_num = 1
device_id = 0
## 获取模型第一个输入的大小。
model_input_size = acl.mdl.get_input_size_by_index(model_desc, 0)
## 获取每个Batch输入数据的大小。
single_buff_size = model_input_size / batch_size
## 定义该变量,用于累加Batch数是否达到8Batch。
cnt = 0
## 定义该变量,用于描述每个文件读入内存时的位置偏移。
pos = 0
infer_file_vec = []
for i in len(files_list):
# 每8个文件,申请一次Device上的内存,存放8Batch的输入数据。
if cnt % batch_size == 0:
pos = 0
infer_file_vec.clear()
# 申请Device上的内存。
p_batch_dst, ret = acl.rt.malloc(model_input_size, ACL_MEM_MALLOC_NORMAL_ONLY)
# TODO: 从某个目录下读入文件,计算文件大小file_size。
# 根据文件大小,申请Host上的内存,存放文件数据。
p_img_buf, ret = acl.rt.malloc_host(file_size)
# 将Host上的文件数据复制到Device的内存中。
ret = acl.rt.memcpy(p_batch_dst + pos, file_size, p_img_buf, file_size, ACL_MEMCPY_HOST_TO_DEVICE)
pos += file_size
# 释放Host上的内存。
acl.rt.free_host(p_img_buf)
# 将第i个文件存入vector中,同时cnt++。
infer_file_vec.append(files_list[i])
cnt++
# 每8Batch的输入数据送给模型推理进行推理。
if cnt % batch_size == 0:
# TODO: 创建aclmdlDataset、aclDataBuffer类型的数据,用于描述模型的输入、输出数据。
# TODO: 调用acl.mdl.execute接口执行模型推理。
# TODO: 推理结束后,调用acl.rt.free接口释放Device上的内存。
## 如果最后循环遍历所有的输入数据后,仍不满足多Batch的要求,则直接将剩余数据作为模型推理的输入。
if cnt % batch_size != 0:
# TODO: 创建aclmdlDataset、aclDataBuffer类型的数据,用于描述模型的输入、输出数据。
# TODO: 调用acl.mdl.execute接口执行模型推理。
# TODO: 推理结束后,调用acl.rt.free接口释放Device上的内存。
## ......
父主题: 扩展更多特性
接口调用流程
开发应用时,如果涉及异步场景下的同步等待,则应用程序中必须包含相关的代码逻辑,关于该场景的接口调用流程,请参见下图。
图1 同步等待流程_Callback场景

关键接口说明如下:
-
回调函数需由用户提前创建,用于获取并处理模型推理或算子执行的结果。
-
线程需由用户提前创建,并自定义线程函数,在线程函数内调用acl.rt.process_report接口,设置超时时间,等待acl.rt.launch_callback接口下发的回调任务执行。
-
调用acl.rt.subscribe_report接口:指定处理Stream上回调函数的线程,线程与2中创建的线程保持一致。
-
异步推理时调用acl.mdl.execute_async接口。
对于异步接口,还需调用acl.rt.synchronize_stream接口阻塞应用程序运行,直到指定Stream中的所有任务都完成。
用户可以在acl.rt.synchronize_stream接口之后一次性获取所有图片的异步推理结果,但如果图片数据量较大的情况下,需要等待的时间比较长,这时可以使用Callback功能,每隔一段时间下发一次Callback任务,获取前一段时间内的异步推理结果。
-
调用acl.rt.launch_callback接口:在Stream的任务队列中下发一个函数回调任务,系统内部在执行到该回调任务时,会在Stream上订阅的线程(通过acl.rt.subscribe_report接口订阅的线程)中执行回调函数,回调函数与1中保持一致。
每调用一次acl.rt.launch_callback接口,就会下发一个回调函数任务。
-
调用acl.rt.unsubscribe_report接口:取消线程订阅(Stream上的回调函数不再由指定线程处理)。
父主题: 模型异步推理
示例代码
您可以从样例介绍中查看完整样例代码。
在acl_resnet50_async样例中:
- 运行可执行文件,不带参数时:
- 执行模型异步推理的次数默认为10次。
- device_id默认使用编号为0的设备。
- callback间隔默认为1,表示1次异步推理后,下发一次callback任务。
- 内存池中的内存块的个数默认为10个。
- 运行可执行文件,带参数时:
-
第一个参数表示使用设备ID。
-
第二个参数表示执行模型异步推理的次数。
-
第三个参数表示下发callback间隔,参数值为0时表示不下发callback任务,参数值为非0值(例如m)时表示m次异步推理后下发一次callback任务。
-
第四个参数表示内存池中内存块的个数,内存块个数需大于等于模型异步推理的次数,用户可根据输入图片数量,来调整内存块的个数。
- 例1:有2张输入图片、内存块个数为2时,则1张图片1个内存块。
- 例2:有3张输入图片、内存块个数为10时,则执行10次循环,每(10/3取整)个内存块对应同一张图片,剩下1个内存块随机对应1张图片。
- 内存块中存放是模型推理的输入数据、输出数据,若多个内存块对应的是同一张图片,则多个内存块中存放的是相同的输入数据、输出数据,用于输入图片少但又想模拟大量图片数据的场景。
-
第五个参数表示要加载执行的模型的路径。
-
第六个参数表示输入的图片路径。支持图片格式如下:
IMG_EXT = ['.jpg', '.JPG', '.png', '.PNG', '.bmp', '.BMP', '.jpeg', '.JPEG']
-
<br> 1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br>10<br>11<br>12<br>13<br>14<br>15<br>16<br>17<br>18<br>19<br>20<br>21<br>22<br>23<br>24<br>25<br>26<br>27<br>28<br>29<br>30<br>31<br>32<br>33<br>34<br>35<br>36<br>37<br>38<br>39<br>40<br>41<br>42<br>43<br>44<br>45<br>46<br>47<br>48<br>49<br>50<br>51<br>52<br>53<br>54<br>55<br>56<br>57<br>58<br>59<br>60<br>61<br>62<br>63<br>64<br>65<br>66<br>67<br>68<br>69<br>70<br>71<br>72<br>73<br>74<br>75<br>76<br>77<br>78<br>79<br>80<br>81<br>82<br>83<br>84<br>85<br>86<br>87<br>88<br>89<br>90<br>91<br>92<br>93<br>94<br>95<br>96<br> | python<br>import acl<br>import argparse<br># ......<br>images_list = [os.path.join(args.images_path, img) for img in os.listdir(args.images_path) \<br> if os.path.splitext(img)[1] in IMG_EXT]<br>data_list = []<br>for image in images_list:<br> # 自定义函数transfer_pic,完成以下功能:<br> # 加载图片到内存,并resize成 224x224尺寸。<br> transfer_pic(image)<br> dst_im = np.fromfile(image.replace(".jpg", ".bin"), dtype=np.byte)<br> data_list.append(dst_im)<br># 1.资源初始化: 样例中通过Net类实现资源初始化操作。<br># 1.1 pyACL初始化。<br>ret = acl.init()<br># 1.2 运行管理资源申请。<br>ret = acl.rt.set_device(self.device_id)<br>self.context, ret = acl.rt.create_context(self.device_id)<br>self.stream, ret = acl.rt.create_stream()<br># 获取当前昇腾AI软件栈的运行模式,根据不同的运行模式,后续的内存申请、内存复制等接口调用方式不同。<br>self.run_mode, ret = acl.rt.get_run_mode()<br># 1.3 申请模型推理资源。<br># 1.4 加载模型。<br># 加载离线模型文件,模型加载成功,返回标识模型的ID。<br>self.model_id, ret = acl.mdl.load_from_file(self.model_path)<br># 1.5 根据模型的ID,获取该模型的描述信息。<br>self.model_desc = acl.mdl.create_desc()<br>ret = acl.mdl.get_desc(self.model_desc, self.model_id)<br># 2.模型推理。<br># 2.1 根据输入参数内存池中内存块的个数申请内存,拷贝图片数据到device侧。<br>def _data_interaction(self, images_dataset_list):<br> for idx in range(self.memory_pool):<br> img_idx = idx % len(images_dataset_list)<br> img_input = self._load_input_data(images_dataset_list[img_idx])<br> infer_ouput = self._load_output_data()<br> self.dataset_list.append([img_input, infer_ouput])<br># 2.2 创建线程tid,并将该tid线程指定为处理Stream上回调函数的线程。<br># 其中ProcessCallback为线程函数,在该函数内调用acl.rt.process_report接口,等待指定时间后,触发回调函数处理。<br>tid, ret = acl.util.start_thread(self._process_callback, [self.context, 50])<br># 2.3 指定处理Stream上回调函数的线程。<br>ret = acl.rt.subscribe_report(tid, self.stream)<br># 2.4 创建回调函数,用户处理模型推理的结果,由用户自行定义。<br>def callback_func(self, delete_list):<br> for temp in delete_list:<br> _, infer_output = temp<br> # device to host<br> num = acl.mdl.get_dataset_num_buffers(infer_output)<br> for i in range(num):<br> temp_output_buf = acl.mdl.get_dataset_buffer(infer_output, i)<br> infer_output_ptr = acl.get_data_buffer_addr(temp_output_buf)<br> infer_output_size = acl.get_data_buffer_size(temp_output_buf)<br> output_host, ret = acl.rt.malloc_host(infer_output_size)<br> ret = acl.rt.memcpy(output_host,<br> infer_output_size,<br> infer_output_ptr,<br> infer_output_size,<br> ACL_MEMCPY_DEVICE_TO_HOST)<br> output_host_dict = ["buffer": output_host, "size": infer_output_size}]<br> result = self.get_result(output_host_dict)<br> st = struct.unpack("1000f", bytearray(result[0]))<br> vals = np.array(st).flatten()<br> top_k = vals.argsort()[-1:-6:-1]<br> print("\n======== top5 inference results: =============")<br> for n in top_k:<br> print("[%d]: %f" % (n, vals[n]))<br> ret = acl.rt.free_host(output_host)<br># 2.5 自定义函数forward,执行模型推理。<br>def forward(self):<br> self.excute_dataset = []<br> for idx in range(self.excute_times):<br> img_data, infer_output = self.dataset_list.pop(0)<br> ret = acl.mdl.execute_async(self.model_id,<br> img_data,<br> infer_output,<br> self.stream)<br> if self.is_callback:<br> self.excute_dataset.append([img_data, infer_output])<br> self._get_callback(idx)<br># 2.6 对于异步推理,需阻塞应用程序运行,直到指定Stream中的所有任务都完成。<br>ret = acl.rt.synchronize_stream(self.stream)<br># 2.7 取消线程注册,Stream上的回调函数不再由指定线程处理。<br>ret = acl.rt.unsubscribe_report(tid, self.stream)<br>self.is_exist = True<br>ret = acl.util.stop_thread(tid)<br># 3.释放运行管理资源。<br>ret = acl.rt.destroy_stream(self.stream)<br>ret = acl.rt.destroy_context(self.context)<br>ret = acl.rt.reset_device(self.model_id)<br># 4.pyACL去初始化。<br>ret = acl.finalize()<br># ......<br> |
|---|
父主题: 模型异步推理
动态Batch/动态分辨率/动态维度(设置多档维度值)
接口调用流程
动态Shape输入场景下模型推理与开发基础推理应用的流程类似,都涉及pyACL初始化与去初始化、运行管理资源申请与释放、模型构建、模型加载、模型执行、模型卸载等。
本节中重点描述动态Shape输入场景下模型推理与开发基础推理应用的不同之处:
-
构建模型时,需配置动态Batch、动态分辨率、动态维度(ND格式)相关的信息:
若模型推理时包含动态Batch特性,在模型推理时,需调用pyACL提供的接口设置模型推理时需使用的“batch size”,模型支持的“batch size”已提前在构建模型时配置(使用ATC工具的“dynamic_batch_size”参数)。
若模型推理时包含动态分辨率特性,在模型推理时,需调用pyACL提供的接口设置模型推理时需使用的分辨率,模型支持的分辨率已提前在构建模型时配置(使用ATC工具的“dynamic_image_size”参数)。
若模型推理时包含动态维度(ND格式)特性,在模型推理时,需调用pyACL提供的接口设置模型推理时需使用的维度值,模型支持哪些维度值已提前在构建模型时配置(使用ATC工具的“dynamic_dims”参数)。
构建模型成功后,在生成的om模型中,会新增相应的输入(下文简称动态Batch/动态分辨率/动态维度输入),在模型推理时通过该新增的输入提供具体的Batch值/分辨率/维度值。
例如,a输入的“batch size”是动态的,在om模型中,会新增与a对应的b输入来描述a的batch信息。在模型执行时,准备a输入的数据结构请参见准备模型执行的输入/输出数据结构,准备b输入的数据结构、设置b输入的数据请参见2。
ATC工具的参数说明请参见《ATC工具使用指南》。
-
在执行模型推理前:
-
需准备动态Batch/动态分辨率/动态维度输入的数据结构:
-
申请动态Batch/动态分辨率/动态维度输入对应的内存前,需要先调用acl.mdl.get_input_index_by_name接口根据输入名称(固定为“ascend_mbatch_shape_data”)获取模型中标识该输入的index。
-
调用acl.mdl.get_input_size_by_index根据index获取输入内存大小。
-
调用acl.rt.malloc接口根据2.b中的大小申请内存。
申请动态Batch/动态分辨率/动态AIPP/动态维度输入对应的内存后,无需用户设置该内存中的数据(否则可能会导致业务异常),用户调用2.b中的接口后,系统会自动向该内存中填入数据。
-
调用acl.create_data_buffer接口创建aclDataBuffer类型的数据,用于存放动态Batch/动态分辨率/动态维度输入数据的内存地址、内存大小。
-
调用acl.mdl.create_dataset接口创建aclmdlDataset类型的数据,并调用acl.mdl.add_dataset_buffer接口向aclmdlDataset类型的数据中增加aclDataBuffer类型的数据。
-
-
需设置动态Batch/动态分辨率/动态维度参数值:
图1 接口调用流程

- 调用acl.mdl.get_input_index_by_name接口根据输入名称(固定为“ascend_mbatch_shape_data”)获取模型中标识该输入的index。
- 设置动态Batch/动态分辨率/动态维度参数值。
-
调用acl.mdl.set_dynamic_batch_size接口设置动态Batch。
此处设置的“batch size”只能是构建模型时设置的Batch档位中的某一个。
也可以调用acl.mdl.get_dynamic_batch接口获取指定模型支持的Batch档位数以及每一档中的“batch size”。
-
调用acl.mdl.set_dynamic_hw_size接口设置动态分辨率。
此处设置的分辨率只能是构建模型时设置的分辨率档位中的某一个。
也可以调用acl.mdl.get_dynamic_hw接口获取指定模型支持的分辨率档位数以及每一档中的宽、高。
-
调用acl.mdl.set_input_dynamic_dims接口设置动态维度的维度值。
此处设置的动态维度的值只能是构建模型时设置的档位中的某一档。
也可以调用acl.mdl.get_input_dynamic_dims接口获取指定模型支持的动态维度档位数以及每一档中的值。
-
-
须知
-
对同一个模型,不能同时调用acl.mdl.set_dynamic_batch_size接口设置动态Batch、调用acl.mdl.set_dynamic_hw_size接口设置动态分辨率、调用acl.mdl.set_input_dynamic_dims接口设置动态维度的维度值,只能调用其中一种。
-
申请模型推理的输出内存时,可以按照各档位的实际大小申请内存,也可以调用acl.mdl.get_output_size_by_index接口获取内存大小后再申请内存(建议使用该方式,确保内存足够)。
-
动态AIPP和动态Batch同时使用时:
- 调用acl.mdl.create_aipp接口设置“batchSize”时,“batchSize”要设置为最大Batch数。
- 模型中需要进行动态AIPP处理的data节点,其对应的输入内存大小需按照最大Batch来申请。
-
动态AIPP和动态分辨率同时使用时:
- 若在设置动态AIPP参数时,开启了抠图或缩放或补边功能,则不能与动态分辨率同时使用。
- 若在设置动态AIPP参数时,未开启抠图或缩放或补边功能,在与动态分辨率同时使用时,需确保通过acl.mdl.set_aipp_src_image_size接口设置的宽和高,与通过acl.mdl.set_dynamic_hw_size接口设置的宽和高相等,都必须设置成模型转换时动态分辨率最大档位的宽和高。
- 模型中需要进行动态AIPP处理的data节点,其对应的输入内存大小需按照最大分辨率(宽、高)来申请。
-
静态AIPP和动态分辨率同时使用时,由于动态分辨率场景下输入图片的宽和高不确定,因此在使用ATC工具的“insert_op_conf”参数传入AIPP配置文件时,AIPP配置文件中不能开启Crop和Padding功能,并且需要将配置文件中的“src_image_size_w”和“src_image_size_h”取值设置为0。
-
对同一个模型,AIPP(包括静态AIPP和动态AIPP)与动态维度(ND格式)不能同时使用。
动态Batch示例代码
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝运行,仅供参考。
<br> 1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br>10<br>11<br>12<br>13<br>14<br>15<br>16<br>17<br>18<br>19<br>20<br>21<br>22<br>23<br>24<br>25<br>26<br> | python<br># 1.模型加载,加载成功后,再设置动态Batch。<br># ......<br># 2.创建aclmdlDataset类型的数据,用于描述模型的输入数据input、输出数据output。<br># ......<br># 3.自定义函数,设置动态Batch。<br>def model_set_dynamicinfo():<br> # 3.1 获取动态Batch输入的index,标识动态Batch输入的输入名称固定为“ascend_mbatch_shape_data”。<br> index, ret = acl.mdl.get_input_index_by_name(model_desc, "ascend_mbatch_shape_data")<br> # 3.2 设置Batch,model_id表示加载成功的模型的ID,input表示aclmdlDataset类型的数据,index表示标识动态Batch输入的输入index。<br> batchSize = 8<br> ret = acl.mdl.set_dynamic_batch_size(model_id_, input, index, batch_size)<br> # ......<br># 4.自定义函数,执行模型。<br>def model_execute(index):<br> # 4.1 调用自定义函数,设置动态Batch。<br> ret = model_set_dynamicinfo()<br> # 4.2 执行模型,model_id表示加载成功的模型的ID,input和output分别表示模型的输入和输出。<br> ret = acl.mdl.execute(model_id, input, output)<br> # ......<br>}<br># 5.处理模型推理结果。<br># ......<br> |
|---|
动态分辨率示例代码
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝运行,仅供参考。
<br> 1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br>10<br>11<br>12<br>13<br>14<br>15<br>16<br>17<br>18<br>19<br>20<br>21<br>22<br>23<br>24<br>25<br>26<br> | python<br># 1.模型加载,加载成功后,再设置动态分辨率。<br># ......<br># 2.创建aclmdlDataset类型的数据,用于描述模型的输入数据input、输出数据output。<br># ......<br># 3.自定义函数,设置动态分辨率。<br>def model_set_dynamicInfo():<br> # 3.1 获取动态分辨率输入的index,标识动态分辨率输入的输入名称固定为“ascend_mbatch_shape_data”。<br> index, ret = acl.mdl.get_input_index_by_name(modelDesc, "ascend_mbatch_shape_data")<br> # 3.2 设置输入图片分辨率,model_id表示加载成功的模型的ID,input表示aclmdlDataset类型的数据,index表示标识动态分辨率输入的输入index。<br> height = 224<br> width = 224<br> ret = acl.mdl.set_dynamic_hw_size(model_id, input, index, height, width)<br> # ......<br># 4.自定义函数,执行模型。<br>def model_execute(index):<br> # 4.1 调用自定义函数,设置动态分辨率。<br> ret = model_set_dynamicInfo()<br> # 4.2 执行模型,model_id表示加载成功的模型的ID,input和output分别表示模型的输入和输出。<br> ret = acl.mdl.execute(model_id, input, output)<br> # ......<br># 5.处理模型推理结果。<br># ......<br> |
|---|
ND格式,动态维度示例代码
调用接口后,需增加异常处理的分支,示例代码中不一一列举。以下是关键步骤的代码示例,不可以直接拷贝运行,仅供参考。
<br> 1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br>10<br>11<br>12<br>13<br>14<br>15<br>16<br>17<br>18<br>19<br>20<br>21<br>22<br>23<br>24<br>25<br>26<br>27<br>28<br> | python<br>import acl<br># ......<br># 1.模型加载,加载成功后,再设置动态维度。<br># ......<br># 2.准备模型描述信息model_desc,准备模型的输入数据input_dataset和模型的输出数据output_dataset。<br># ......<br># 3.自定义函数,设置动态维度。<br>def model_set_dynamic_info():<br> # 3.1 获取动态维度输入的index,标识动态维度输入的输入名称固定为“ascend_mbatch_shape_data”。<br> index, ret = acl.mdl.get_input_index_by_name(model_desc, "ascend_mbatch_shape_data")<br> # 3.2 设置具体档位信息,包括维度数dimCount和各个维度的数值,model_id表示加载成功的模型的ID,input_dataset表示aclmdlDataset类型的数据,index表示标识动态维度输入的输入index。<br> current_dims = {'name': '', 'dimCount': 4, 'dims': [8, 3, 224, 224]}<br> ret = acl.mdl.set_input_dynamic_dims(mode_id, input_dataset, index, current_dims)<br> # ......<br># 4.自定义函数,执行模型。<br>def ModelExecute(int index):<br> # 4.1 调用自定义函数,设置动态维度。<br> ret = model_set_dynamic_info()<br> # 4.2 执行模型,model_id表示加载成功的模型的ID,input_dataset和output_dataset分别表示模型的输入和输出。<br> ret = acl.mdl.execute(model_id, input_dataset, output_dataset)<br> # ......<br># 5.处理模型推理结果。<br># ......<br> |
|---|
父主题: 模型动态推理
在线提单