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

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

我的博客写的比较详细,大家可以根据目录查看自己需要的内容😁

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)

4 环境搭建与配置

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

4.1 MindStudio 安装(Windows版本)

  1. 安装前的说明

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

    我所采用的是windows共部署的方法

    img
  2. 下载安装MindStudio

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

4.2 SSH连接

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

image-20220729112740866 image-20220729112829999

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

image-20220729112923000

4.3 MindX SDK安装

  1. 远端环境MindX SDK安装

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

  2. 本地 CANN 安装

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

    image-20220729113540405

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

    image-20220729113719466 image-20220729113824728 image-20220729113932486

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

    image-20220729115300714
  3. 本地 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

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

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

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

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
  • 注意这两个地方要选子路径,选择父路径会找不到版本号

9 结语

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

Now, it’s time to get your hands dirty!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值