找回密码
 立即注册
查看: 321|回复: 0

ResNet18 :使用MindStudio进行MindX SDK应用开发

[复制链接]
发表于 2022-8-29 14:45 | 显示全部楼层 |阅读模式
第一次写专栏文章,内容比较长,大家可以参照自己的需求,通过目录快速定位想看的内容
<hr/>1 写在最前面

这是一篇基于昇腾众智“ResNet18 for pytorch”代码的MindX SDK应用开发经验帖。原始项目代码可以从Ascend/ModelZoo-PyTorch - Gitee.com仓库中找到,根据原始项目代码,我们可以在MindStudio上快速实现一个ImageNet2012数据集1000分类应用的开发。
整个过程中,MindStudio昇腾论坛上的教程基于MindStudio的MindX SDK应用开发全流程_MindStudio_昇腾_华为云论坛 (huaweicloud.com)也提供了很大的帮助。
2 ResNet18简介

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.
3 工具简介

3.1 MindX SDK介绍

MindX SDK是华为昇腾AI处理器加速的AI软件开发套件(SDK), 提供了大量丰富的API,可以快速完成AI应用的开发
3.2 MindStudio

MindStudio提供了一个AI开发所需的一站式开发环境,支持模型开发、算子开发以及应用开发三个主流程中的开发任务 依靠模型可视化、算力测试、IDE本地仿真调试等功能,MindStudio能够帮助开发者在一个工具上就能高效便捷地完成AI应用开发 MindStudio采用了插件化扩展机制,开发者可以通过开发插件来扩展已有功能
官网地址:MindStudio-昇腾社区 (hiascend.com)
<hr/>4 环境搭建与配置

因为我使用的Windows系统环境,所以这里提供Windows环境下MindStdio环境搭建和配置的全流程,其它系统可以查看安装方案-安装指南-MindStudio-文档首页-昇腾社区 (hiascend.com)
4.1 MindStudio 安装(Windows版本)


  • 安装前的说明
MindStudio可以单独安装在Windows上。在安装MindStudio前需要在Linux服务器上安装部署好Ascend-cann-toolkit开发套件包,之后在Windows上安装MindStudio,安装完成后通过配置远程连接的方式建立MindStudio所在的Windows服务器与Ascend-cann-toolkit开发套件包所在的Linux服务器的连接,实现全流程开发功能。
我所采用的是windows共部署的方法



img


  • 下载安装MindStudio
前往MindStudio下载-昇腾社区 (hiascend.com)页面进行MindStudio软件包的下载,并且参照安装MindStudio-安装操作(Windows)完成MindStudio的安装。
4.2 SSH连接

在进行共部署之前,先确认SSH服务器连接成功



image-20220729112740866



image-20220729112829999

按照上面图中所示,依次配置之后,点击Test Connection,显示下图就表示连接成功。



image-20220729112923000

4.3 MindX SDK安装


  • 远端环境MindX SDK安装
Windows 场景下基于 MindStuido 的 SDK 应用开发,请先确保远端环境上 MindX SDK 软件包已安装完成,安装方式请参见《mxManufacture 用户指南》和 《mxVision 用户指南》的“使用命令行方式开发”>“安装 MindX SDK 开发套件” 章节。

  • 本地 CANN 安装
①:在 Windows 本地打开 MindStudio,点击 Customize > Allsettings…,如下图所示:



image-20220729113540405

②:进入设置页面,点击 Appearance & Behavior > System Settings > CANN 进入 CANN 管理界面。



image-20220729113719466



image-20220729113824728



image-20220729113932486

完成后的状态如下图所示:



image-20220729115300714


  • 本地 MindX SDK安装
与步骤 2 开始一致,进入设置页面,点击 Appearance & Behavior > System  Settings > MindX SDK 进入 MindX SDK 管理界面。界面中 MindX SDK Location 为软件包的默认安装路径,默认安装路径为“C:\Users\用户名\Ascend\mindx_sdk”。 单击 Install SDK 进入 Installation settings 界面。



image-20220729120016477



image-20220729115519706

参数说明
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”




image-20220729120121530



image-20220729120226785

<hr/>5 推理开发运行流程

使用 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++版本的应用工程,则需要进行远端编译,远端编译时,会对工 程文件夹进行目录拷贝到远端连接的环境,编译成功后即可运行。
<hr/>6 业务开发

业务开发将按照python版和C++版分别进行介绍
6.1 Python版本开发

6.1.1 新建一个项目


  • 首先创建一个项目,选择一个自己喜欢的位置,比如图中所示,将在D:\Codes\python\Ascend\MyApp位置下创建自己的项目。选择好了后,点击下一步




新建一个项目


  • 选择MindX SDK Project(Python)




image-20220728210254779

如图所示,被圈出来的4个项目,上面两个是空模板,在这里面创建我们自己的工程项目,因为我们要创建Python版的应用,所以选箭头指的这个;下面两个是官方给出的样例项目,如果对目录结构和应该写哪些代码不太熟悉的话,也可以创建一个样例项目先学习一下。
选择完成后,点击Finish完成项目的创建



image-20220728210736449

6.1.2 代码目录结构

Python版本的SDK项目大概有哪些文件呢?我们其实可以先打开一个官方样例项目先看看



image-20220728211202984

左边是我们自己创建的项目,右边是官方样例项目,对比之下我们可以发现,右边的样例项目主要有models(用于存储模型文件)、pipeline(流程编排文件)、python(项目python代码)和streamserver(实际开发中没用上)。
PS: 其实也不一定要按照这个目录结构存放代码,只是这样结构更加清晰一些。
我的工程目录文件是这样



image-20220728212723792

其中data用来存放数据图片
6.1.3 模型转换


  • 下载模型
首先我们先在ModelZoo-昇腾社区 (hiascend.com)中下载ResNet18模型和代码



image-20220728213424790

选Pytorch版



image-20220728213500972

分别下载模型脚本和模型
其中模型脚本在“pipeline文件编排”和“main.py文件编写”章节得到应用



image-20220728213633039

解压后,我们只要这个onnx模型,同时,我们将names标签数据也一起放进models文件夹



image-20220728214048152


  • 模型转换
点击这个工具,进入模型转换界面



image-20220728214251343

选择待转换的模型路径



image-20220728214425647



image-20220728214644992

选好后点一下任意一个位置,MindStudio会进入模型解析状态



image-20220728214736312

等待模型解析完成后,可以修改输出的模型名字,或者点击下一步



image-20220728215024765



image-20220728215249628



image-20220728215341219

点击Finish,开始转换

  • 转换完成
稍等片刻,出现这个就表明转换完成



image-20220728215821103

如上图所示,在显示的路径中可以找到转化好的文件



image-20220728215947248

我们将om文件移动到models文件夹中,就可以进行接下去的步骤了
6.1.4 pipeline文件编排

pipeline文件编排是python版SDK最主要的推理开发步骤
作为一个图片分类任务,主要包括以下几个步骤:
初始化 → 图片数据传送 → 图片预处理 → 图片分类 → 序列化 → 结果传回
由于ResNet18模型采用的是PIL库进行图片预处理,而非opencv,因此我们不在pipeline中进行图片预处理步骤(包括图片解码、缩放、裁剪),而是通过在main.py文件中进行图片预处理,再将结果传给推理业务
因此pipeline流程编排为以下几个步骤
初始化 → 获取图片数据 → 图片分类 → 序列化 → 结果传回
可视化结果如下图所示:



image-20220728233832473

实际上,我们可以通过对之前下载好的代码文件中已有的pipeline进行简单修改,就可以完成我们自己的pipeline文件编排
在下载好的ResNet18_for_PyTorch_{$version}__code中,路径./infer/sdk/data/config下可以找到resnet18.cfg和resnet8.pipeline两个文件,将这两个文件分别移动到models目录和pipeline目录下,如下图所示:



image-20220728234522239

对resnet18.pipeline进行参数修改



image-20220809232235888

我个人比较喜欢文本编辑的方式修改,如下图所示



image-20220728235040831

修改完成后,就可以进行main.py文件的编写了,离成功运行越来越近了!
6.1.5 main.py文件编写

根据pipeline文件编排中的说明,我们在main.py中需要完成3件事——对图片进行预处理+将预处理好的结果传给推理业务+获取推理结果
实际上,我们依旧可以参考下载的代码文件中./infer/sdk/main.py文件中的做法,稍作修改就可以
(1) 图片预处理
主要用到resize、center_crop、preprocess和gen_protobuf(图片编码为推理业务需要的字节流)四个函数
参照下载代码中的main.py文件,很容易就能得到这四个函数



image-20220729091047207

# 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方法,在那个基础上,调用我们自己的预处理函数再传值给推理业务即可



image-20220809231758413

streamName: 业务流的名字,需要和pipeline文件中写的保持一致



image-20220729091943581

6.1.6 代码运行

前面的步骤完成之后,我们就可以进行代码的运行了。
(1) 选择一张测试用图片
我们在data目录中放入自己想要的测试图片,比如我放的这张



image-20220729092822142

然后在main.py中设置好图片位置
全部完成后应该为这样



image-20220729093037315

(2) 设置运行脚本
接着,我们点击上图中箭头指的地方,设置运行脚本为main.py



image-20220729093201889

(3) 获取运行结果
点击运行获得推理结果



image-20220729093241568



image-20220729093350908

我们可以看到,推理结果为golden retriver 金毛寻回犬
上网搜搜看,发现和我们测试的狗狗图片是一样的品种



image-20220729093502926

<hr/>6.2 C++版本开发

6.2.1 新建一个项目

可以参见“Python版本开发-新建一个项目”章节,这次我们选择的是C++开发
空项目应该为这样



image-20220809230835243

6.2.2 代码目录结构

根据上图的说明,我们还需要建立一个models文件用于存放模型文件,完整的目录结构应该长这样



image-20220729095359486

值得注意的是,这里多了一个preprocess.py文件,是用于预处理图片的,在下面“代码运行”章节会详细说明
6.2.3 模型转换(或者不转换)

和Python版本开发一样,可以对下载到的onnx模型进行转换,也因为转换好的模型其实就是下载的模型中的om模型,所以也可以直接拿来用。



image-20220729100253917

6.2.4 业务代码书写(修改)

(1) 将下载到的代码中,./infer/mxbase中的src代码和CMakeLists.txt文件全部移动到src目录下
(2) 修改src代码
①修改路径
首先修改main.cpp中的文件路径



image-20220729102429308

再修改编译文件中的代码路径



image-20220729102551178

②修改业务代码
因为我们下载到的代码中,推理结果只保留了推理的类别编号,并且需要传入测试图片所在的父目录。
而我们想要让它测试一张图片,并且将推理结果输出类别名称,所以需要对main.cpp和Resnet18Classify.cpp中部分代码进行修改
main.cpp



image-20220729102722911

主要修改传入图片的方法,像上图中一样,改为传单张图片
Resnet18Classify.cpp
主要修改一处,将结果储存到文本文件中的方法,修改为打印输出



image-20220729103213389

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方法调用



image-20220729103344316

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文本的代码



image-20220729124905591

全部完成后,就可以进行下一步了!
6.2.5 项目编译

业务代码写好之后,我们需要编译整个文件



image-20220729104145260



image-20220729104313835



image-20220729104347023

点击编译后,等待下方提示编译成功后,就可以运行代码了!



image-20220729104549773

编译好的文件在out目录下
6.2.6 代码运行

由于Resnet18用的预处理方法是PIL,C++中没有相应的图片处理方法,为了精度更高一些,我们需要稍微麻烦一下,使用python编写一个预处理方法prepreocess.py,将处理后的图片再传给C++代码运行
preprocess.py文件可以在下载好的代码中 ./infer/util中找到,根据实际需求稍作修改就可以使用。
因此我们先准备一张测试图片,比如下面这张,命名为test.jpg



image-20220729103839769

①:先通过preprocess.py预处理生成test.bin



image-20220729103952425

②:运行项目
先配置运行命令



image-20220729104736863

点击运行



image-20220729104914645

<hr/>7 可参考的代码

我已经将自己写好的项目上传到了GitHub上,有需要可以参考
Python版SDK应用开发:https://github.com/Swgj/Resnet18_SDK.git
C++版SDK应用开发:https://github.com/Swgj/Resnet18_Mxbase.git
8 FAQ

8.1 模型运行报错?


  • 在模型转换的时候,是否注意了选择FP32?  默认状态下是FP16的方式进行转换。
  • 或者也可以直接将下载到的模型中的om模型文件直接拿来用




image-20220729095909438

8.2 如果想换个数据集怎么办?


  • 可以参考modelzoo中,下载ResNet18模型的地方的说明。可以通过ModelArts训练快速进行迁移学习。
8.3 Python找不到模型路径?


  • 注意在pipeline流程编排中,模型路径若使用的是相对路径,则是相对main.py文件的位置的相对路径,而不是相对于pipeline文件的相对路径。因此注意根据自己的main.py文件位置修改模型的路径
8.4 C++找不到模型路径?


  • 检查自己此时代码中的模型路径是否和已经编译好的代码路径一致,C++已经编译完成后再修改代码,则需要重新编译才能运行修改后结果。
8.5 No Python interpreter configured for the module?




image-20220729092520781



image-20220729092602699

8.6 SDK版本获取失败?




image-20220729120333851


  • 注意这两个地方要选子路径,选择父路径会找不到版本号
<hr/>9 结语

MindStudio官方文档非常详细,不一而足。所以在一开始查看的时候难免会有种觉得东西太多,不知道从哪里下手的感觉。但随着开发过程的推进,逐渐发现官方文档才是开发者最应该参考的内容。此外,遇到问题时,也可以在MindStudio 昇腾社区论坛中得到许多启发,相信大家在自己动手的过程中也能体会到这一点。
Now, it's time to get your hands dirty!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-25 03:58 , Processed in 0.092994 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表