昇腾 CANN 入门推理全流程 - 超分辨率 (PyTorch, ONNX, AscendCL)


开发板到手,当天就将 SD 卡插上进入并狠狠地体验了一把内置的 CANN 案例。但是对着现成的案例沾沾自喜有什么用!我现在就要体验昇腾模型推理全流程!

经过 CANN 训练营的学习,可以肯定的一点是昇腾的推理流程十分易于上手,即便是我这种鲲鹏昇腾小白也可以轻松掌握。

整个流程将分为以下几章节:

另,为开发板插网线时可首先尝试插入下面的端口,因为开启了 DHCP。

模型选择 - MAN 模型

我从研究记录中选择了 MAN 超分辨率模型进行体验。MAN 是一个较轻量的超分辨率模型,精度较好且性能开销少。代码开源较精简,有预训练模型

我们这次将专注于 MAN-light 模型。我们将代码仓库克隆并下载预训练模型,即可进行下一步。


将 SR 模型转换为中间格式 (MindIR, ONNX)

MAN 模型是基于 PyTorch 框架的模型,所以我们将转换为 ONNX 中间件格式。

载入 PyTorch 模型网络代码

MAN 的模型类型是通过调整网络的参数来实现切换的,参数说明在 options 文件中有定义。

*# ./options/*test_MAN.yml
# network structures
network_g:

  • type: MAN*
  • scale: 2 #or 3/4*
  • n_resblocks: 36 # 5 for MAN-tiny; 24 for MAN-light; 36 for MAN *
  • n_resgroups: 1*
  • n_feats: 180 # 48 for MAN-tiny; 60 for MAN-light; 180 for MAN *

导入相关程序库并输入我们要的参数,将仓库内的 MAN_arch.py 单独使用生成模型网络,并转换为推理模式。(内部注释掉 BasicSR 库的 Arch 代码)

使用 Python 载入模型

from MAN_arch import MAN  
model = MAN(n_resblocks=24, n_resgroups=1, n_feats=60, scale=2) # MAN-light  
model.eval() # 不转换精度会下降  

Python 会打印出模型网络:

MAN(

  • (sub_mean): MeanShift(3, 3, kernel_size=(1, 1), stride=(1, 1))*
  • (head): Conv2d(3, 60, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))*
  • (body): ModuleList(*
    ……

小贴士:由于不是训练,用于生成数据集以及优化器不需要生成,只关注模型网络,简化代码。

在 GitHub 仓库下载预训练模型,在这使用 PyTorch 的方法 load_state_dict 载入权重。

model.load_state_dict(torch.load("MAN-Light-x2.pth"), strict=True)

打印模型权重的载入情况。

for param_tensor in model.state_dict():`  
    print(param_tensor, "\t", model.state_dict()[param_tensor].size())

Model’s state_dict:
sub_mean.weight ** ** torch.Size([3, 3, 1, 1])
sub_mean.bias ** ** torch.Size([3])
……

生成一个测试张量,这个张量需要和之前训练模型的张量结构相等,你可以通过调试来获取以前的张量结构,一般是 NCHW 格式。MAN 超分辨率模型只对 H, W 参数不敏感。

testTensor = torch.tensor([[[[1.]],[[1.]],[[1.]]]])
# 或者使用 touch.ones 函数,两个相等
testTensor = torch.ones((1, 3, 1, 1), dtype=torch.float32)

小贴士:这里需要注意,张量数据类型 (dtype) 要与模型中偏置 (bias) 的数据类型一致。

使用一行代码将构建好的 PyTorch 模型导出至 ONNX,注意这里要使用 dynamic_axes 使输入图片大小任意

torch.onnx.export(  
        model,  
        testTensor,  
        ".\\test_onnx1.onnx",  
        verbose=False,  
        input_names=["image_in"],  
        output_names=["image_out"],  
        dynamic_axes={  
            "image_in": {0: 'batch_size', 2 : 'in_width', 3: 'in_height'},  
            "image_out": {0: 'batch_size', 2: 'out_width', 3:'out_height'}}  
    )  

导出完成,可以使用 Netron 可视化模型,真炫酷。

模型输入节点的名字不一定是 image_in ,可以任意设置。

至此我们就已经转换为了 ONNX 格式,让我们来到转换为昇腾模型的准备阶段。


将中间格式模型转换为昇腾 OM 模型

准备阶段

开始查阅昇腾文档所需的资料。首先我们需要将动态输入在转换为 OM 模型时实现。

设置输入图片的动态分辨率:适用于执行推理时,每次处理图片宽和高不固定的场景。
将解析的shape中的HW设置为-1,会在shape下方出现Dynamic Image Size参数。在其中的编辑框中输入具体的动态分辨率参数,最少输入两组,每一组参数通过英文分号分隔,组内参数使用英文逗号分隔。最多支持100档配置,例如输入112,112;224,224
来自 昇腾文档

为了加快模型转换速度,我们后续也会对图片做补边预处理,使一些长宽稍微偏离输入尺寸的图片也能正确进行推理。

我们也需要实现 MAN 模型在训练时的预处理方法。MAN 模型是用 BasicSR 库进行训练的,这是一个通用的超分辨率训练库,图片的预处理会定义在 util 文件夹内,可以从数据的加载方法中看到数据处理方法。

*# From *paired_image_dataset.py
# Load gt and lq images. Dimension order: HWC; channel order: BGR;
# image range: [0, 1], float32.
# BGR to RGB, HWC to CHW, numpy to tensor

看到了颜色通道排列方法是 R, G, B,而最终的张量排布是 NCHW,颜色需要均一化 (0~1),数据精度为32位浮点数,以上就是预处理方式。

还记得开头提到的 CANN 示例吗?里面有我们可以复用的轮子—— ACL 推理流程代码。所以我们只需要在示例的基础上修改部分操作,即可成功运行。
python/level2_simple_inference/1_classification/resnet50_imagenet_classification/src/acl_net.py · Ascend/samples - 码云 - 开源中国 (gitee.com)
上述的是 ResNet-50 分类网络模型,里面有完整的 AscendCL 流程。我们将修改:

  • 图片预处理函数
  • 显示 / 保存图片函数
  • 主函数

对于昇腾设备来说它们要做的流程并没有变,只是我们需要使用 PIL 进行 CPU 方式的图片补边,推理后再裁边。

转换流程

因为内存需求,我们将直接使用非昇腾设备 Ubuntu 系统完成转换。转换之前需要安装 CANN 环境。
安装步骤(Ubuntu 18.04)-安装依赖-安装开发环境-软件安装 (命令行)-环境准备-6.3.RC2.alpha003-CANN社区版-文档首页-昇腾社区 (hiascend.com)

首先使用命令迁移,命令按照官方文档来使用

atc --model=./MAN_light_x2.onnx \  
--framework=5 \  
--input_shape="image_in:1,3,-1,-1"  \  
--dynamic_image_size="416,416;832,832" \  
--output=$HOME/Model_Convert/MAN/MAN-light_x2 \  
--soc_version=Ascend310B1  

一行命令中的参数:

  • 模型路径
  • 模型类型(ONNX)
  • 输入尺寸(-1为预设动态分辨率)
  • 预设动态分辨率
  • 输出路径
  • 目标芯片

设置的预设越多,读条越久。但等到成功的那一刻还是很开心的。

我们现在已经有了昇腾 OM 模型,可以用于推理了。简化流程,我们下面使用静态分辨率模型。


使用 AscendCL 进行推理

改写示例

启动 A2 开发板,启动 Jupyter Lab 样例,找到“03-resnet”示例,打开即可看到代码。

但很多代码是可以复用的,我们只需修改以下部分:
图片预处理:

def preprocess_img(input_path, target_shape_list:tuple):  
   """图片预处理"""  
   input_path = os.path.abspath(input_path)  
   with Image.open(input_path) as image_file:  
       current_image_size = image_file.size  
       padding_image = image_file  
       padding_image.show()  
 
      # 归一化  
   img = np.array(padding_image,dtype=np.float32) / 255  
 
   # NCHW  
   img = np.expand_dims(np.transpose(img, (2,0,1)), axis=0)  
 
   result = np.frombuffer(img.tobytes(), np.float32)  
   return result, current_image_size  

打印推理结果:

def _print_result(self, result):  
        """打印推理结果  
        在 ResNet50 样例内这里是将 NumPy 转换为置信度  
        对于超分辨率直接 NumPy 数组转成图像输出好了"""  
  
        # NumPy 结果转 PIL 图片,NCHW -> HWC  
        for i in result:  
            i = np.transpose(i[0][0:3], (1,2,0))  
            i = np.clip(i, 0., 1.) * 255  
            i = i.astype(np.uint8)  
            i = np.clip(i,0,255)  
            i = Image.fromarray(i,"RGB")  
            i.show()  

主函数:

def main():  
    device = 0  
    model_path = 'MAN-light_x2_200x200.om'  
    images_path = './data'  
  
    targetList = ((200, 200),(300,300))  
    net = Net(device, model_path, targetList[0])  
  
    # 从文件夹读取图片进行流程  
    images_list = [os.path.join(images_path, img)  
                   for img in os.listdir(images_path)  
                   if os.path.splitext(img)[1] in IMG_EXT]  
  
    for image in images_list:  
        print("images:{}".format(image))  
        img, picSize = preprocess_img(image, targetList[0])  
        pred_dict = net.run([img])  
  
    print("*****run finish******")  
    net.release_resource()  

以及适配预设的动态分辨率。

修改完毕,点击开始运行,我们使用 Set5 数据集进行测试。

可以看到成功推理出大图片~

小贴士:如果对 AscendCL 代码全流程较难理解,可转而使用示例 “Unet++”,这个示例将 ACL 封装起来成 acllite_util ,大大简化步骤,更易上手。

(两行代码即可)


总结

简要说明流程:

  1. 从研究记录中选择一个模型作为实验(首选 SR 超分辨率模型)

    1. MAN 轻量
    2. 有预训练模型
    3. 开源
  2. 将 SR 模型转换为中间格式 (MindIR, ONNX)

    1. PyTorch 转换为 ONNX
    2. ONNX 要打开动态输入
  3. 将中间格式模型转换为昇腾 OM 模型

    1. OM 模型需要预设动态分辨率
    2. 动态分辨率越多转换越久
  4. 使用 AscendCL 进行推理

    1. 改写了昇腾原有示例

整个自身的体验流程就告一段落了。其中最有意思的肯定是平台的推理通过 ATC 将模型网络编译,大大缩短推理时间,算力开销大幅降低,才发现在模型边缘计算推理场景中性能需求可以压缩的如此低。将模型转换为昇腾 OM 模型所用的时间能接受,搭配上开发板的 GPIO 接口则有机会作为物联网的中枢。我自身也打算使用昇腾设备对开发模型进行推理测试,充分验证网络的实际应用性能;以及将昇腾开发板带到户外去,使用真实场景进行推理,所以求求官方赶快出一个外壳吧,把 PCB 螺丝孔设计图公开一下也可以啊(笑)。

感谢大家的观看,未来还会带来其他的进阶操作流程!

环境配置

昇腾环境

  • Atlas 200I DK A2 开发者套件
  • Ubuntu 20.04 LTS (aarch64)
  • CANN 6.3.RC1.alpha001

开发环境

  • Windows 11 Home 22H2
  • WSL 2 (Ubuntu 20.04)

链接

安装须知-软件安装 (命令行)-环境准备-6.3.RC2.alpha003-CANN社区版-文档首页-昇腾社区 (hiascend.com)
学习向导-ATC模型转换-推理应用开发-6.3.RC2.alpha003-CANN社区版-文档首页-昇腾社区 (hiascend.com)
导出ONNX模型-保存与导出模型-PyTorch 网络模型迁移和训练-模型开发(PyTorch)-6.3.RC1-CANN商用版-文档首页-昇腾社区 (hiascend.com)
samples: CANN Samples (gitee.com)

Logo

昇腾万里,让智能无所不及

更多推荐