小白将pytorch神经网络代码移植到MLU220上

1.任务背景

我的专业和计算机沾不上什么边,只在大一上过一学期C语言,没搞过深度学习,被安排了这个任务,经过三个星期左右的试探,终于要完成了,记录一下。

2.大致流程

1、在你的工作机上训练好网络,导出网络的模型参数文件(.pth);

2、在寒武纪官网上租用一个MLU270的容器;

3、编写离线模型生成程序,将本地保存的.pth文件逐步转成可以在MLU220部署的.cambricon文件;

4、编写测试代码,交叉编译测试程序,生成可执行文件ELF;

5、将可执行文件及编译出的build文件夹拷贝至MLU220,运行程序。

3.具体步骤

3.1 导出网络的模型参数文件(.pth)

这部分我不懂,问问别人。

3.2 租一个MLU270的容器

解释一下为什么要租MLU270的容器:我们最终要把模型移植到MLU220这个硬件平台,那么如何让这个硬件识别我们的模型呢,就需要利用工具将我们的.pth文件转化为MLU220可以识别的格式,这个工具MLU270有,220没有,因此我们要在网上租这个容器,一次可以租半个月,租吧,反正免费。)

进入寒武纪开发平台,点击这个链接寒武纪开发平台 ,点击添加开发容器,我选的是public-server/mlu270_ubuntu18.04.pytorch,最终的效果如图:

3.3 编写离线模型生成程序,将本地保存的.pth文件逐步转成可以在MLU220部署的.cambricon文件

这步是比较麻烦的,我的测试程序过一会在我的主页里更新吧。转化过程大致分为以下几个步骤:

0、进入MLU270容器;

1、在MLU270上将本地保存的.pth进行量化;

2、基于量化后的.pth文件进行在线推理;

3、生成适用于MLU270的.cambricon文件;

4、基于.cambricon文件进行离线推理,反复调试结果,查看是否与主机上运行结果相同(在可接受误差范围内满足要求);

5、重新生成适用于MLU220的.cambricon文件。

3.3.0 进入MLU270容器

因为后续生成.cambricon文件都是在MLU270上完成的,因此我们不可避免的要在MLU270上输入命令及进行文件上传下载,最朴实的方法是用Windows自带的命令行通过SSH远程连接,使用cp等命令进行上传下载,在进行多次尝试后,我找到了Mobaxterm这个软件(可免费下载),这个软件可以同时进行SSH连接和可视化文件交互,上传下载文件只是简单的拖拽即可,连接一次可以保存密码,下次开启后直接连接,软件界面如图所示:

小白将pytorch神经网络代码移植到MLU220上_第1张图片

左侧是我使用过的连接,新的连接可通过点击左侧的session得到下图:

小白将pytorch神经网络代码移植到MLU220上_第2张图片

点击ssh即创建了SSH连接,可对MLU270进行命令控制,点击SFTP即创建了某种协议,可实现可视化文件上传下载。

3.3.1 在MLU270上将本地保存的.pth进行量化

这步操作是将.pth文件的权重数值进行优化,一般由Float32量化到INT16或INT8,量化后的模型称为量化模型,此步骤的目的是减少模型占用的存储空间,以便在边缘设备(MLU220)上进行计算,关键代码如下:

qconfig={'firstconv':False} 
#一般只需设置firstconv(设置图像归一化),我在调试的时候将其设置为Fause后,最后的结果才没出错,不知道为什么

quantized_net = torch_mlu.core.mlu_quantize.quantize_dynamic_mlu(model.eval().float(),qconfig_spec=qconfig, dtype='int16', gen_quant=True)
#model为加载的模型,dtype为量化位数,可选'int16'、'int8',具体根据结果精度而定,注意gen_quant默认为False,而在生成量化模型时,需要设置为Ture,运行模型时不需要

torch.save(quantized_net.state_dict(), ‘resnet50_quant.pth’)
#保存量化后的模型

在CPU上基于量化后的模型进行推理,得到推理结果,如果结果和主机上跑的结果相差较大,可在量化时更改量化位数,重新生成量化模型。

3.3.2 基于量化后的.pth文件进行在线推理

调用生成的量化模型,对输入图像进行前处理,将图像加载到模型中,实现在线推理,关键代码如下:

net = resnet50() 
#获取resnet50网络文件

quantized_net = torch_mlu.core.mlu_quantize.quantize_dynamic_mlu(net)
#调用量化接口,推理时调用量化接口的作用是将网络中的可量化层比如 Conv2d 替换为 MLUConv2d

quantized_net.load_state_dict(torch.load(resnet50_quant.pth))
#加载量化权重文件,将量化后的.pth文件加载进网络中

transform = transforms.Compose([
    transforms.Resize(resize),
    transforms.CenterCrop(crop),
    transforms.ToTensor(),
    transforms.Normalize(mean=mean, std=std),
])
#模型推理首先要对图像进行前处理。该过程会调整输入图像的大小,并根据数据集的mean和std对图片做均值处理,上述代码定义了 transform 对象,该对象会对输入图像依次执行 Resize、CenterCrop、ToTensor、Normalize 操作,具体操作视情况而定。

im_tensor = torch.cat(imgs)
#对于 batchsize 不为 1 的情况,利用上语句可将imgs中多张处理后的图片,拼接成一个符合要求的输入tensor。

torch_mlu.core.mlu_model.set_core_number(args.core_number) # 设置MLU核心数,可选 1、4、16
torch_mlu.core.mlu_model.set_core_version(MLU270) # 设置MLU版本
net_mlu = quantized_net.to(torch_mlu.core.mlu_model.mlu_device()) #模型加载到设备中
output = net_mlu(img.to(torch_mlu.core.mlu_model.mlu_device()) #得到推理结果

在对图像进行前处理时,注意img转为tensor时,pytorch会自动将图像的灰度除以255,因为这块不知道,debug了两三天,哎。

得到推理结果与主机推理得到的结果进行对比,反复调试即可。

3.3.3 生成适用于MLU270的.cambricon文件

在前面步骤很顺利的前提下,可以基于量化后的模型生成离线模型(.cambricon)了,关键代码如下:

torch_mlu.core.mlu_model .set_core_number(args.core_number) # 设置 MLU core number,可选 1、4、16
torch_mlu.core.mlu_model .save_as_cambricon(model_name) #保存为.cambricon文件
torch_mlu.core.mlu_model .set_core_version(MLU270) # 设置 MLU版本,先生成适用于MLU270的离线模型
net_mlu = quantized_net.to(torch_mlu.core.mlu_model.mlu_device()) 
example_mlu = torch.randn(1, 3, in_h, in_w, dtype=torch.float)#指定推理时的batch_size值
traced_model=torch.jit.trace(net_mlu,example_mlu.to(ct.mlu_device()),check_trace=False)
output = trace_model(im_tensor.to(ct.mlu_device())) #生成静态图,得到推理结果
ct.save_as_cambricon("") #运行结束后调用接口关闭.cambricon 离线生成 
3.3.4 基于.cambricon文件进行离线推理

为了验证生成的.cambricon文件是否正确,在MLU270上编写测试程序,基于离线模型进行离线推理。这部分直接在主页里粘贴代码,反复调试程序即可。

3.3.5 重新生成适用于MLU220的.cambricon文件

在270版本的离线模型验证成功后,重新生成离线模型,只需更改一处代码,将MLU版本改为MLU220,代码如下:

torch_mlu.core.mlu_model .set_core_version(MLU220) # 设置 MLU版本,先生成适用于MLU220的离线模

适用于MLU220的离线模型已经生成 。

3.4 编写测试程序

这部分主要就是用C++完成模型的导入,图像的读取、前处理、离线推理及后处理,这部分应该用交叉编译的,因为MLU270的架构是X86_64的,而MLU220的架构是aarch64的,用MLU270的编译器编译程序只能在X86_64上运行,但是当时我不懂,就先没有交叉编译,由于是否交叉编译对测试程序编写基本没影响,所以先按照正常编译的思路写了。

由于框架有人帮忙提供了,我做的工作只是简单进行前处理、后处理、更改Cmakelist文件中库的位置及添加模型路径,具体项目文件夹会在主页更新,这里简单提一嘴在改Cmakelist文件中遇到的低级问题,当时把动态链接库路径和include函数库路径正确填写后,始终报下列错误:

说没有imwrite及imread这个函数,查来查去,说是没有成功配置opencv的环境,我把那链接库、包含库路径搞了好多,甚至重新下载了opencv,还是不管用,花了一天多的时间,最后排来排去,注意到我的CMakelist中链接具体函数时出现了下图所示的情况

小白将pytorch神经网络代码移植到MLU220上_第3张图片

没错,注释掉的就是imread和imwrite函数的库 ,把注释去掉后,编译成功了,成功在MLU270上跑通了。

没错,我这就是典型的猪脑子支配了两只猪眼睛!!!

最后在说说交叉编译,因为配置交叉编译的环境(比如cnrt,glog,opencv)对我来说有点过于复杂,并且会耽误很长时间,甲方干脆给我发过来一个镜像,这里面据说环境都配置好了,我只需要加载镜像即可,浅谈我加载镜像的流程。

拿到镜像后,怕出现一些奇奇怪怪的问题,我直接在我的Ubuntu系统下弄的,首先下载docker工具,流程参考Ubuntu18.04安装Docker完整教程 - 时光如你般美好 - 博客园 (cnblogs.com)吧。

docker 工具安装好后,将镜像拷贝至主机目录,加载镜像:

docker load 

输入docker images查看镜像,并根据repository:tag创建容器并进入容器(docker run -it --name yy mlu_docker:v1.6.0c /bin/bash,,,,,yy为容器名,mlu_docker为repository,v1.6.0c为tag),进入容器的常见方法为:

docker exec -it yy /bin/bash;

可以选择挂载目录的方式共享主机的特定目录与镜像中的目录,到这里也就基本具备交叉编译的环境了,由于我在编译的时候没有找到glog与gflgs的动态链接,因此还没完成编译,后续继续更新。

由于本人非相应专业出身,只是记录一下自己完成任务过程中不断摸索的足迹,故上述过程一定有啰嗦甚至不对的地方,欢迎各位大佬及时斧正。

你可能感兴趣的:(pytorch,神经网络,人工智能)