我的博客写的比较详细,大家可以根据目录查看自己需要的内容
这是一篇基于昇腾众智“ResNet18 for pytorch”代码的MindX SDK应用开发经验帖。原始项目代码可以从Ascend/ModelZoo-PyTorch - Gitee.com仓库中找到,根据原始项目代码,我们可以在MindStudio上快速实现一个ImageNet2012数据集1000分类应用的开发。
整个过程中,MindStudio昇腾论坛上的教程基于MindStudio的MindX SDK应用开发全流程_MindStudio_昇腾_华为云论坛 (huaweicloud.com)也提供了很大的帮助。
ResNet是ImageNet竞赛中分类问题效果较好的网络,它引入了残差学习的概念,通过增加直连通道来保护信息的完整性,解决信息丢失、梯度消失、梯度爆炸等问题,让很深的网络也得以训练。ResNet有不同的网络层数,常用的有18-layer、34-layer、50-layer、101-layer、152-layer。ResNet18的含义是指网络中有18-layer。
参考论文:He K, Zhang X, Ren S, et al. Deep residual learning for image recognition[C]//Proceedings of the IEEE conference on computer vision and pattern recognition. 2016: 770-778.
MindX SDK是华为昇腾AI处理器加速的AI软件开发套件(SDK), 提供了大量丰富的API,可以快速完成AI应用的开发
MindStudio提供了一个AI开发所需的一站式开发环境,支持模型开发、算子开发以及应用开发三个主流程中的开发任务
依靠模型可视化、算力测试、IDE本地仿真调试等功能,MindStudio能够帮助开发者在一个工具上就能高效便捷地完成AI应用开发
MindStudio采用了插件化扩展机制,开发者可以通过开发插件来扩展已有功能
官网地址:MindStudio-昇腾社区 (hiascend.com)
因为我使用的Windows系统环境,所以这里提供Windows环境下MindStdio环境搭建和配置的全流程,其它系统可以查看安装方案-安装指南-MindStudio-文档首页-昇腾社区 (hiascend.com)
安装前的说明
MindStudio可以单独安装在Windows上。在安装MindStudio前需要在Linux服务器上安装部署好Ascend-cann-toolkit开发套件包,之后在Windows上安装MindStudio,安装完成后通过配置远程连接的方式建立MindStudio所在的Windows服务器与Ascend-cann-toolkit开发套件包所在的Linux服务器的连接,实现全流程开发功能。
我所采用的是windows共部署的方法
下载安装MindStudio
前往MindStudio下载-昇腾社区 (hiascend.com)页面进行MindStudio软件包的下载,并且参照安装MindStudio-安装操作(Windows)完成MindStudio的安装。
在进行共部署之前,先确认SSH服务器连接成功
按照上面图中所示,依次配置之后,点击Test Connection,显示下图就表示连接成功。
远端环境MindX SDK安装
Windows 场景下基于 MindStuido 的 SDK 应用开发,请先确保远端环境上 MindX SDK 软件包已安装完成,安装方式请参见《mxManufacture 用户指南》和 《mxVision 用户指南》的“使用命令行方式开发”>“安装 MindX SDK 开发套件” 章节。
本地 CANN 安装
①:在 Windows 本地打开 MindStudio,点击 Customize > Allsettings…,如下图所示:
②:进入设置页面,点击 Appearance & Behavior > System Settings > CANN 进入 CANN 管理界面。
完成后的状态如下图所示:
本地 MindX SDK安装
与步骤 2 开始一致,进入设置页面,点击 Appearance & Behavior > System Settings > MindX SDK 进入 MindX SDK 管理界面。界面中 MindX SDK Location 为软件包的默认安装路径,默认安装路径为“C:\Users\用户名\Ascend\mindx_sdk”。 单击 Install SDK 进入 Installation settings 界面。
参数 | 说明 |
---|---|
Remote Connection | 远程连接的用户及 IP |
Remote CANN Location | 远端环境上 CANN 开发套件包的路 径,请配置到版本号一级 |
Remote SDK Location | 远端环境上 SDK 的路径,请配置到 版本号一级。IDE 将同步该层级下的 include、opensource、python、 samples 文件夹到本地 Windows 环境 |
Local SDK Location | 同步远端环境上 SDK 文件夹到本地 的路径。默认安装路径为“C:\Users\ 用户名\Ascend\mindx_sdk” |
使用 MindStudio 开发和运行推理业务步骤如下:
(1) 确定业务流程:根据业务功能如目标检测、图像分类、属性识别等,将 业务流程进行模块化。
(2) 寻找合适插件:根据已有 SDK 插件的功能描述和规格限制来匹配业务功 能,当 SDK 提供的插件无法满足功能需求时,用户可以开发自定义插件。
(3) 准备推理模型文件与数据:准备输入图片和下载模型,使用 Model Convertor 模型转换工具将模型转换为 om 格式。
(4) 流程编排:以可视化的方式,开发数据流图,生成 pipeline 文件供应用框 架使用,配置文件以 json 格式编写,必须指定业务流名称、元件名称和 插件名称,并根据需要,补充元件属性和下游元件名称信息。
(5) 业务集成:编写 C++程序或 Python 程序,调用业务流管理的 API ( MxStreamManager ), 先 进 行 初 始 化 , 再 加 载 业 务 流 配 置 文 件 (*.pipeline),然后根据 stream 配置文件中的 StreamName 往指定 Stream 获取输出数据,最后销毁 Stream。
(6) 编译与运行应用:若新建的工程为 Python 版本的应用工程,不需要执行 编译应用工程,配置 Python 环境后,即可在远端服务器运行;若新建工 程为 C++版本的应用工程,则需要进行远端编译,远端编译时,会对工 程文件夹进行目录拷贝到远端连接的环境,编译成功后即可运行。
业务开发将按照python版和C++版分别进行介绍
选择MindX SDK Project(Python)
如图所示,被圈出来的4个项目,上面两个是空模板,在这里面创建我们自己的工程项目,因为我们要创建Python版的应用,所以选箭头指的这个;下面两个是官方给出的样例项目,如果对目录结构和应该写哪些代码不太熟悉的话,也可以创建一个样例项目先学习一下。
选择完成后,点击Finish完成项目的创建
Python版本的SDK项目大概有哪些文件呢?我们其实可以先打开一个官方样例项目先看看
左边是我们自己创建的项目,右边是官方样例项目,对比之下我们可以发现,右边的样例项目主要有models(用于存储模型文件)、pipeline(流程编排文件)、python(项目python代码)和streamserver(实际开发中没用上)。
PS: 其实也不一定要按照这个目录结构存放代码,只是这样结构更加清晰一些。
我的工程目录文件是这样
其中data用来存放数据图片
下载模型
首先我们先在ModelZoo-昇腾社区 (hiascend.com)中下载ResNet18模型和代码
选Pytorch版
分别下载模型脚本和模型
其中模型脚本在“pipeline文件编排”和“main.py文件编写”章节得到应用
解压后,我们只要这个onnx模型,同时,我们将names标签数据也一起放进models文件夹
模型转换
点击这个工具,进入模型转换界面
选择待转换的模型路径
选好后点一下任意一个位置,MindStudio会进入模型解析状态
等待模型解析完成后,可以修改输出的模型名字,或者点击下一步
点击Finish,开始转换
转换完成
稍等片刻,出现这个就表明转换完成
如上图所示,在显示的路径中可以找到转化好的文件
我们将om文件移动到models文件夹中,就可以进行接下去的步骤了
pipeline文件编排是python版SDK最主要的推理开发步骤
作为一个图片分类任务,主要包括以下几个步骤:
初始化 → 图片数据传送 → 图片预处理 → 图片分类 → 序列化 → 结果传回
由于ResNet18模型采用的是PIL库进行图片预处理,而非opencv,因此我们不在pipeline中进行图片预处理步骤(包括图片解码、缩放、裁剪),而是通过在main.py文件中进行图片预处理,再将结果传给推理业务
因此pipeline流程编排为以下几个步骤
初始化 → 获取图片数据 → 图片分类 → 序列化 → 结果传回
可视化结果如下图所示:
实际上,我们可以通过对之前下载好的代码文件中已有的pipeline进行简单修改,就可以完成我们自己的pipeline文件编排
在下载好的ResNet18_for_PyTorch_{$version}__code中,路径./infer/sdk/data/config下可以找到resnet18.cfg和resnet8.pipeline两个文件,将这两个文件分别移动到models目录和pipeline目录下,如下图所示:
对resnet18.pipeline进行参数修改
我个人比较喜欢文本编辑的方式修改,如下图所示
修改完成后,就可以进行main.py文件的编写了,离成功运行越来越近了!
根据pipeline文件编排中的说明,我们在main.py中需要完成3件事——对图片进行预处理+将预处理好的结果传给推理业务+获取推理结果
实际上,我们依旧可以参考下载的代码文件中./infer/sdk/main.py文件中的做法,稍作修改就可以
(1) 图片预处理
主要用到resize、center_crop、preprocess和gen_protobuf(图片编码为推理业务需要的字节流)四个函数
参照下载代码中的main.py文件,很容易就能得到这四个函数
# resize and crop
def resize(img, size, interpolation=Image.BILINEAR):
return img.resize(size[::-1], interpolation)
def center_crop(img, out_height, out_width):
height, width, _ = img.shape
left = int((width - out_width) / 2)
right = int((width + out_width) / 2)
top = int((height - out_height) / 2)
bottom = int((height + out_height) / 2)
img = img[top:bottom, left:right]
return img
# preprocessor调用上面的两个函数进行图片缩放裁剪处理
def preprocess(in_file):
mean = [0.485, 0.456, 0.406]
std = [0.229, 0.224, 0.225]
img = Image.open(in_file).convert('RGB')
w = img.size[0]
h = img.size[1]
if w > h:
input_size = np.array([256, 256 * w / h])
else:
input_size = np.array([256 * h / w, 256])
input_size = input_size.astype(int)
print(input_size)
img = resize(img, input_size) # transforms.Resize(256)
img = np.array(img, dtype=np.float32)
img = center_crop(img, 224, 224) # transforms.CenterCrop(224)
img = img / 255. # transforms.ToTensor()
img[..., 0] = (img[..., 0] - mean[0]) / std[0]
img[..., 1] = (img[..., 1] - mean[1]) / std[1]
img[..., 2] = (img[..., 2] - mean[2]) / std[2]
img = img.transpose(2, 0, 1) # HWC -> CHW
return img
#generate protobuf调用preprocess生成传给推理业务的流数据
def gen_protobuf(in_file):
img_np = preprocess(in_file)
vision_list = MxpiDataType.MxpiVisionList()
vision_vec = vision_list.visionVec.add()
vision_vec.visionInfo.format = 0
vision_vec.visionInfo.width = 224
vision_vec.visionInfo.height = 224
vision_vec.visionInfo.widthAligned = 224
vision_vec.visionInfo.heightAligned = 224
vision_vec.visionData.memType = 0
vision_vec.visionData.dataStr = img_np.tobytes()
vision_vec.visionData.dataSize = len(img_np)
protobuf = MxProtobufIn()
protobuf.key = b"appsrc0"
protobuf.type = b'MxTools.MxpiVisionList'
protobuf.protobuf = vision_list.SerializeToString()
protobuf_vec = InProtobufVector()
protobuf_vec.push_back(protobuf)
return protobuf_vec
(2) main方法编写
main方法的编写,可以基于官方样例代码中的main方法,在那个基础上,调用我们自己的预处理函数再传值给推理业务即可
streamName: 业务流的名字,需要和pipeline文件中写的保持一致
前面的步骤完成之后,我们就可以进行代码的运行了。
(1) 选择一张测试用图片
我们在data目录中放入自己想要的测试图片,比如我放的这张
然后在main.py中设置好图片位置
全部完成后应该为这样
(2) 设置运行脚本
接着,我们点击上图中箭头指的地方,设置运行脚本为main.py
(3) 获取运行结果
点击运行获得推理结果
我们可以看到,推理结果为golden retriver 金毛寻回犬
上网搜搜看,发现和我们测试的狗狗图片是一样的品种
可以参见“Python版本开发-新建一个项目”章节,这次我们选择的是C++开发
空项目应该为这样
根据上图的说明,我们还需要建立一个models文件用于存放模型文件,完整的目录结构应该长这样
值得注意的是,这里多了一个preprocess.py文件,是用于预处理图片的,在下面“代码运行”章节会详细说明
和Python版本开发一样,可以对下载到的onnx模型进行转换,也因为转换好的模型其实就是下载的模型中的om模型,所以也可以直接拿来用。
(1) 将下载到的代码中,./infer/mxbase中的src代码和CMakeLists.txt文件全部移动到src目录下
(2) 修改src代码
①修改路径
首先修改main.cpp中的文件路径
再修改编译文件中的代码路径
②修改业务代码
因为我们下载到的代码中,推理结果只保留了推理的类别编号,并且需要传入测试图片所在的父目录。
而我们想要让它测试一张图片,并且将推理结果输出类别名称,所以需要对main.cpp和Resnet18Classify.cpp中部分代码进行修改
main.cpp
主要修改传入图片的方法,像上图中一样,改为传单张图片
Resnet18Classify.cpp
主要修改一处,将结果储存到文本文件中的方法,修改为打印输出
ShowResult函数(记得在.h头文件中也添上这个方法)
APP_ERROR Resnet18Classify::ShowResult(const std::string &imgPath, std::vector<std::vector<MxBase::ClassInfo>> \
&BatchClsInfos) {
uint32_t batchIndex = 0;
std::string fileName = imgPath.substr(imgPath.find_last_of("/") + 1);
for (const auto &clsInfos : BatchClsInfos) {
std::string resultStr;
for (const auto &clsInfo : clsInfos) {
resultStr += std::to_string(clsInfo.classId) + "," +
clsInfo.className + "," + "confidence: " + std::to_string(clsInfo.confidence) + ";";
}
LogInfo << fileName << "->Result: " << resultStr;
batchIndex++;
}
return APP_ERR_OK;
}
同时,在Process函数中,将SaveResult替换成ShowResult方法调用
Process函数
APP_ERROR Resnet18Classify::Process(const std::string &imgPath) {
std::vector<MxBase::TensorBase> inputs = {};
std::string inputIdsFile = imgPath;
APP_ERROR ret = ReadInputTensor(inputIdsFile, &inputs);
if (ret != APP_ERR_OK) {
LogError << "Read input ids failed, ret=" << ret << ".";
return ret;
}
std::vector<MxBase::TensorBase> outputs = {};
ret = Inference(inputs, outputs);
if (ret != APP_ERR_OK) {
LogError << "Inference failed, ret=" << ret << ".";
return ret;
}
std::vector<std::vector<MxBase::ClassInfo>> BatchClsInfos = {};
ret = PostProcess(outputs, BatchClsInfos);
if (ret != APP_ERR_OK) {
LogError << "PostProcess failed, ret=" << ret << ".";
return ret;
}
ret = ShowResult(imgPath, BatchClsInfos);
if (ret != APP_ERR_OK) {
LogError << "Show result failed, ret=" << ret << ".";
return ret;
}
return APP_ERR_OK;
}
Init函数中,注释掉生成txt文本的代码
全部完成后,就可以进行下一步了!
业务代码写好之后,我们需要编译整个文件
点击编译后,等待下方提示编译成功后,就可以运行代码了!
编译好的文件在out目录下
由于Resnet18用的预处理方法是PIL,C++中没有相应的图片处理方法,为了精度更高一些,我们需要稍微麻烦一下,使用python编写一个预处理方法prepreocess.py,将处理后的图片再传给C++代码运行
preprocess.py文件可以在下载好的代码中 ./infer/util中找到,根据实际需求稍作修改就可以使用。
因此我们先准备一张测试图片,比如下面这张,命名为test.jpg
①:先通过preprocess.py预处理生成test.bin
②:运行项目
先配置运行命令
点击运行
我已经将自己写好的项目上传到了GitHub上,有需要可以参考
Python版SDK应用开发:https://github.com/Swgj/Resnet18_SDK.git
C++版SDK应用开发:https://github.com/Swgj/Resnet18_Mxbase.git
在模型转换的时候,是否注意了选择FP32? 默认状态下是FP16的方式进行转换。
或者也可以直接将下载到的模型中的om模型文件直接拿来用
MindStudio官方文档非常详细,不一而足。所以在一开始查看的时候难免会有种觉得东西太多,不知道从哪里下手的感觉。但随着开发过程的推进,逐渐发现官方文档才是开发者最应该参考的内容。此外,遇到问题时,也可以在MindStudio 昇腾社区论坛中得到许多启发,相信大家在自己动手的过程中也能体会到这一点。
Now, it’s time to get your hands dirty!