diff --git "a/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\345\212\237\350\203\275\346\211\223\351\200\232/imgs/image-20210901203311059.png" "b/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\345\212\237\350\203\275\346\211\223\351\200\232/imgs/image-20210901203311059.png" new file mode 100644 index 0000000000000000000000000000000000000000..e0f6d222784b76b1244b11944c247c6f6654c02e Binary files /dev/null and "b/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\345\212\237\350\203\275\346\211\223\351\200\232/imgs/image-20210901203311059.png" differ diff --git "a/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\345\212\237\350\203\275\346\211\223\351\200\232/\345\212\250\346\200\201Shape\346\216\250\347\220\206\346\214\207\345\257\274(ResNet50).md" "b/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\345\212\237\350\203\275\346\211\223\351\200\232/\345\212\250\346\200\201Shape\346\216\250\347\220\206\346\214\207\345\257\274(ResNet50).md" new file mode 100644 index 0000000000000000000000000000000000000000..b38240d26344eb0d6ccda5b6af326a0c13e860ce --- /dev/null +++ "b/Ascend-PyTorch\347\246\273\347\272\277\346\216\250\347\220\206\346\214\207\345\257\274/\344\270\223\351\242\230\346\241\210\344\276\213/\345\212\237\350\203\275\346\211\223\351\200\232/\345\212\250\346\200\201Shape\346\216\250\347\220\206\346\214\207\345\257\274(ResNet50).md" @@ -0,0 +1,195 @@ +# 动态Shape推理指导 + + + +## 1. 前言 + +本文旨在说明如何在模型存在动态shape算子的场景下,进行模型转换和模型推理。 + +本文介绍的动态shape场景通过设定`shape_range`的方式,支持`shape_range`内的任意shape。 + +现有离线推理工具:benchmark/msame暂不支持动态shape的推理,需要基于pyACL库实现模型的动态shape推理,具体可参照:4. 模型推理 + +代码可参考:[Resnet50_dynamic]() + +## 2. 原理说明 + +本文主要基于pyACL(Python Ascend Computing Language)进行动态shape模型的推理: + +pyACL就是在ACL的基础上使用CPython封装得到的Python API库,使用户可以通过Python进行昇腾 AI 处理器的运行管理、资源管理等。 + +而AscendCL提供Device管理、Context管理、Stream管理、内存管理、模型加载与执行、算子加载与执行、媒体数据处理等C语言API库供用户开发深度神经网络应用,用于实现目标识别、图像分类等功能。 + +重要的基础概念: + +1. Host: Host指与Device相连接的X86服务器、ARM服务器,会利用Device提供的NN(Neural-Network )计算能力,完成业务。 + +2. Device:Device指安装了芯片的硬件设备,利用PCIe接口与Host侧连接,为Host提供NN计算能力。 + +3. Context:Context作为一个容器,管理了所有对象(包括Stream、Event、设备内存等)的生命周期。不同Context的Stream、不同Context的Event是完全隔离的,无法建立同步等待关系。 + +4. 动态Batch/动态分辨率:在某些场景下,模型每次输入的Batch数或分辨率是不固定的,如检测出目标后再执行目标识别网络,由于目标个数不固定导致目标识别网络输入BatchSize不固定。 + +基于AscendCL的开发流程如下: + +![image-20210901203311059](./imgs/image-20210901203311059.png) + +## 3. 模型转换 + +动态shape的模型转化可以直接与atc工具进行转化: + +```shell +/usr/local/Ascend/ascend-toolkit/latest/atc/bin/atc --model=${input_onnx_model} \ +--framework=5 --output=${output_name} --input_format=NCHW \ +--input_shape_range="actual_input_1:[1~6,3,224,224]" \ +--log=info --soc_version=Ascend310 +``` + +关键参数说明:`--input_shape_range`:可以设置动态shape的范围,如本例中的`1~6` + +## 4. 模型推理 + +动态shape模型的推理流程可以分为以下几个步骤: + +1. buffer申请:device侧的输入data buffer申请+host侧的输出dataset buffer申请; + +2. 输入数据搬运:host->device;data需要载入到dataset内,其中动态模型,输入需要通过`set_dataset_tensor_desc`添加动态描述 + +3. 启动模型推理 + +4. 输出数据搬运:device->host;根据输出shape大小得到最终输出的结果 + +5. 释放相关数据资源:包含data/dataset对象,释放内存等 + +关键代码示例: +buffer申请(data&&dataset): + +```python + def _gen_data_buffer(self, size, des, data=None): + # self.buffer_method: + # "in": acl.mdl.get_input_size_by_index, + # "out": acl.mdl.get_output_size_by_index, + # "outhost": acl.mdl.get_output_size_by_index + func = self.buffer_method[des] + for i in range(size): + if data is None: + temp_buffer_size = func(self.model_desc, i) + else: + if des == "in": + input_size = np.prod(np.array(data).shape) + # size * dtype.itemsize + temp_buffer_size = Net.gen_data_size(input_size, dtype=ACL_DTYPE.get(ACL_FLOAT32)) + elif des == "out": + batch_size = data[0].shape[0] + # 以当前的batchsize替换-1得到当前shape + out_shape = Net.fix_static_shape(self.out_shape, idx=0, value=batch_size) + out_size= np.prod(out_shape) + temp_buffer_size = Net.gen_data_size(out_size, dtype=ACL_DTYPE.get(ACL_FLOAT32)) + + temp_buffer, ret = acl.rt.malloc(temp_buffer_size, ACL_MEM_MALLOC_HUGE_FIRST) + check_ret("acl.rt.malloc", ret) + + if des == "in": + self.input_data.append({"buffer": temp_buffer, + "size": temp_buffer_size}) + elif des == "out": + self.output_data.append({"buffer": temp_buffer, + "size": temp_buffer_size}) +``` + +数据搬运: + +```python + # 数据拷贝 + def _data_interaction(self, dataset, policy=ACL_MEMCPY_HOST_TO_DEVICE): + temp_data_buffer = self.input_data \ + if policy == ACL_MEMCPY_HOST_TO_DEVICE \ + else self.output_data + + if len(dataset) == 0 and policy == ACL_MEMCPY_DEVICE_TO_HOST: + dataset = self.output_data_host + for i, item in enumerate(temp_data_buffer): + if policy == ACL_MEMCPY_HOST_TO_DEVICE: + ptr = acl.util.numpy_to_ptr(dataset[i]) + ret = acl.rt.memcpy(item["buffer"], item["size"], ptr, item["size"], policy) + check_ret("acl.rt.memcpy", ret) + + else: + ptr = dataset[i]["buffer"] + ret = acl.rt.memcpy(ptr, item["size"], item["buffer"], item["size"], policy) + check_ret("acl.rt.memcpy", ret) + + # 创建dataset对象,模型基于该对象进行后续推理 + # dataset实际存储的是一系列data对象 + def _gen_dataset(self, type_str="input", input_shapes=None): + dataset = acl.mdl.create_dataset() + temp_dataset = None + if type_str == "in": + self.load_input_dataset = dataset + temp_dataset = self.input_data + else: + self.load_output_dataset = dataset + temp_dataset = self.output_data + + for i, item in enumerate(temp_dataset): + data = acl.create_data_buffer(item["buffer"], item["size"]) + if data is None: + ret = acl.destroy_data_buffer(dataset) + check_ret("acl.destroy_data_buffer", ret) + + _, ret = acl.mdl.add_dataset_buffer(dataset, data) + if ret != ACL_ERROR_NONE: + ret = acl.destroy_data_buffer(dataset) + check_ret("acl.destroy_data_buffer", ret) + + # set dynamic dataset tensor desc + if type_str == "in": + input_shape = input_shapes[i] + input_desc = acl.create_tensor_desc(ACL_FLOAT32, input_shape, ACL_FORMAT_NCHW) + dataset, ret = acl.mdl.set_dataset_tensor_desc(dataset, input_desc, i) + if ret != ACL_ERROR_NONE: + ret = acl.destroy_data_buffer(dataset) + check_ret("acl.destroy_data_buffer", ret) + +``` + +数据释放: + +```python + # 销毁dataset/data对象:并不会释放内存,数据还在 + def _destroy_databuffer(self): + for dataset in [self.load_input_dataset, self.load_output_dataset]: + if not dataset: + continue + num = acl.mdl.get_dataset_num_buffers(dataset) + for i in range(num): + data_buf = acl.mdl.get_dataset_buffer(dataset, i) + if data_buf: + ret = acl.destroy_data_buffer(data_buf) + check_ret("acl.destroy_data_buffer", ret) + ret = acl.mdl.destroy_dataset(dataset) + check_ret("acl.mdl.destroy_dataset", ret) + + def _release_data_buffer(self): + while self.input_data: + item = self.input_data.pop() + ret = acl.rt.free(item["buffer"]) + check_ret("acl.rt.free", ret) + + while self.output_data: + item = self.output_data.pop() + ret = acl.rt.free(item["buffer"]) + check_ret("acl.rt.free", ret) + + while self.output_data_host: + item = self.output_data_host.pop() + ret = acl.rt.free_host(item["buffer"]) + check_ret("acl.rt.free_host", ret) +``` + + + +## 5. 参考资料 + +[CANN 5.0.2 应用软件开发指南 (Python, 推理)](https://support.huawei.com/enterprise/zh/doc/EDOC1100206683) +