【CANN训练营第四期】如何在AI1S云服务上基于ACL实现单算子调用
617
2022-05-29
CANN训练营第二期 应用营 高玩赛作业链接:
https://bbs.huaweicloud.com/forum/forum.php?mod=viewthread&tid=143438&extra=page=1
实操作业为:
学习路径: 1、视频课程:MindX应用使能组件介绍 https://www.hiascend.com/zh/activities/cloud2021/live/8120 2、实验课程:使用MindX SDK开发智能质检应用 https://lab.huaweicloud.com/testdetail_531 3、实验课程:MindX SDK + Pytorch yolov5 应用案例详解 https://bbs.huaweicloud.com/forum/thread-118598-1-1.html 课程作业: 在MindStudio中创建MindX SDK模板工程,并完成检测(yolov5)+分类(resnet50,从生昇腾社区ModelZoo获取,TensorFlow和Pytorch的均可)。 MindX SDK模板工程可以放到gitee上,参考附件。 评分规则: 在MindStudio中创建MindX SDK模板工程:10分 并完成检测:30分 分类:15分 推理应用开发:15分
解题思路分析:
作业中“学习路径”中的第二个链接的实验,张小白以前就做过了:
https://bbs.huaweicloud.com/forum/thread-137435-1-1.html
第一个链接的直播,好像讲的也是 MindX,并且也含了第二个链接的实验:
https://www.hiascend.com/zh/activities/cloud2021/live/8120
第三个链接,好像就可以指导作业了:
https://bbs.huaweicloud.com/forum/thread-118598-1-1.html
所以打开第三个链接,咱们就开始做作业吧!
听这次的 高玩赛 老师 @Fate丶SSS 的介绍,高玩赛的镜像跟 新手营的镜像一样,都是image-for-MindX。那么简单了,只需要将 新手营没有删除的 AI1S服务器开机,就可以开始做作业了。
如果你不小心删了,也不要紧请移驾 https://bbs.huaweicloud.com/blogs/285668 完成服务器购买等操作:
并根据 https://bbs.huaweicloud.com/blogs/285675 步骤,完成ResNet50网络的离线模型生成:
下面的操作是基于上述步骤执行完毕的情况下进行。
MindStudio的启动、创建工程和MindStudio配置:
开机-》启动 MindStudio
New Project
Next,选择 选择Sample(detection and classfication)(下图中的箭头请忽略,以文字为准)
点击Finish:
当点击工程中pipeline目录下的Sample.pipeline时,系统会提示,“There is no activated MindX SDK”,因此要配置下MindX SDK的位置:
在MindStudio菜单栏依次点击"Ascend"->"MindX SDK Manager"
在弹出的窗口点击 Install SDK
选择 SDK的路径:
选择online试试:(这是一个坑。。。)
它居然在装aarch64的版本,估计是对自己是X86的身份存在怀疑:
点击Finish
明显X86的SDK是不能用的。
还是通过local模式装SDK吧:
检查mindxsdk安装盘的位置:/home/HwHiAiUser/MindX
选择该目录下的run文件:
Next:
Next:
耐心等待安装完毕,点击Finish:
点击OK,这回pipeline的图形化界面终于出现了:
仔细查看pipleline的图形,以及对应的源码(Text):
这个sample。前面是YoLoV3,后面是 ResNet50.
而我们的高玩题目呢?要求前面是YoLoV5,后面是 ResNet50。
所以解题思路很简单:
1.要么一个一个做,比如先只做YoLoV5, 再开个MindX SDK项目只做ResNet50
2.或者把现在的pipeline改一下,前面改为YoLoV5。后面应该不用改了。
跑通YoLoV3+ResNet50:
先把现在的代码编译一下:
菜单栏点击”Build“->"Edit Build Configuration..."
点击build,会生成out目录下的main可执行代码:
运行下试试:选择local Run,按以下内容填写:
点击Run:会告诉你om模型不存在:
需要把 第三周作业( https://bbs.huaweicloud.com/blogs/285675 )转换好的ResNet50的离线模型 resnet50_aipp_tf.om 拷贝过来:
找到本项目所在的目录:
cd ~/AscendProjects/Resnet50
cd models/resnet50
cp /home/HwHiAiUser/MindX/resnet50/model/resnet50_aipp_tf.om .
顺便也把第二周作业( https://bbs.huaweicloud.com/blogs/285668 )生成的YoLoV3离线模型也拷过来:
cd ../yolov3
cp /home/HwHiAiUser/MindX/yolov3/yolov3_tf_bs1_fp16.om .
再重新运行:
运行结果如下:
具体内容看不清,贴出来一下:
2021-07-31 18:40:38 - [INFO] Resnet50 start running... 2021-07-31 18:40:38 - [INFO] Execute command on local: export LD_LIBRARY_PATH=/usr/local/Ascend/mindx_sdk/mxVision_2.0.1/linux-x86_64/mxVision-2.0.1/lib:/usr/local/Ascend/mindx_sdk/mxVision_2.0.1/linux-x86_64/mxVision-2.0.1/lib/plugins:/usr/local/Ascend/mindx_sdk/mxVision_2.0.1/linux-x86_64/mxVision-2.0.1/opensource/lib:/usr/local/Ascend/mindx_sdk/mxVision_2.0.1/linux-x86_64/mxVision-2.0.1/opensource/lib64:/root/AscendProjects/Resnet50/lib:/root/AscendProjects/Resnet50/lib/plugins:${LD_LIBRARY_PATH} && export GST_PLUGIN_SCANNER=/usr/local/Ascend/mindx_sdk/mxVision_2.0.1/linux-x86_64/mxVision-2.0.1/opensource/libexec/gstreamer-1.0/gst-plugin-scanner && export GST_PLUGIN_PATH=/usr/local/Ascend/mindx_sdk/mxVision_2.0.1/linux-x86_64/mxVision-2.0.1/opensource/lib/gstreamer-1.0:/usr/local/Ascend/mindx_sdk/mxVision_2.0.1/linux-x86_64/mxVision-2.0.1/lib/plugins:/root/AscendProjects/Resnet50/lib/plugins && export MX_SDK_HOME=/usr/local/Ascend/mindx_sdk/mxVision_2.0.1/linux-x86_64/mxVision-2.0.1 && cd /root/AscendProjects/Resnet50/out && ./main Begin to initialize Log. The output directory(./logs) of logs file exist. Save logs information to specified directory(./logs). W0731 18:40:40.195328 5423 main.cpp:130] Results:{"MxpiObject":[{"MxpiClass":[{"classId":163,"className":"beagle","confidence":0.87109375}],"classVec":[{"classId":16,"className":"dog","confidence":0.99641436299999997,"headerVec":[]}],"x0":125.63256800000001,"x1":918.29089399999998,"y0":116.434044,"y1":597.21276899999998}]} 2021-07-31 18:40:41 - [INFO] Run finished, exit status: 0
JSON格式化后的结果如下:
{ "MxpiObject": [{ "MxpiClass": [{ "classId": 163, "className": "beagle", "confidence": 0.87109375 }], "classVec": [{ "classId": 16, "className": "dog", "confidence": 0.99641436299999997, "headerVec": [] }], "x0": 125.63256800000001, "x1": 918.29089399999998, "y0": 116.434044, "y1": 597.21276899999998 }] }
结果分析:
先进行了 YoLoV3的检测(检测结果放入ClassVec和x0,y0、x1、y1的坐标),然后进行了ResNet50的分类(分类结果放入MxpiClass)。
检测结果是:
在 "x0":125.63256800000001,"x1":918.29089399999998,"y0":116.434044,"y1":597.21276899999998}
这个框内,YoLoV3认为它是dog,ResNet认为它属于beagle(猎兔犬)。
当然,Sample代码并没有画出结果。
所以增加以下代码,画个结果的框吧。
参考 https://bbs.huaweicloud.com/forum/thread-137435-1-1.html 的内容增加OpenCV2的代码。
CMakeLists.txt:
Header path include_directories( ${MX_SDK_HOME}/include/ ${MX_SDK_HOME}/opensource/include/ ${MX_SDK_HOME}/opensource/include/opencv4/ ) # add host lib path link_directories( ${MX_SDK_HOME}/lib/ ${MX_SDK_HOME}/opensource/lib/ ${MX_SDK_HOME}/opensource/lib64/ ${MX_SDK_HOME}/opensource/include/opencv4/ ) add_executable(main main.cpp) target_link_libraries(main glog mxbase streammanager cpprest mindxsdk_protobuf opencv_world)
main.cpp
#include "opencv4/opencv2/opencv.hpp"
还有:
web::json::value jsonText = web::json::value::parse(result); if (jsonText.is_object()) { web::json::object textObject = jsonText.as_object(); auto itInferObject = textObject.find("MxpiObject"); if (itInferObject == textObject.end() || (!itInferObject->second.is_array())) { return 0; } auto iter = itInferObject->second.as_array().begin(); cv::Mat src = cv::imread("../data/test.jpg"); for (; iter != itInferObject->second.as_array().end(); iter++) { if (iter->is_object()) { auto modelInferObject = iter->as_object(); float x0 = 0; float x1 = 0; float y0 = 0; float y1 = 0; auto it = modelInferObject.find("x0"); if (it != modelInferObject.end()) { x0 = float(it->second.as_double()); } it = modelInferObject.find("x1"); if (it != modelInferObject.end()) { x1 = float(it->second.as_double()); } it = modelInferObject.find("y0"); if (it != modelInferObject.end()) { y0 = float(it->second.as_double()); } it = modelInferObject.find("y1"); if (it != modelInferObject.end()) { y1 = float(it->second.as_double()); } cv::Rect rect(x0, y0, x1 - x0, y1 - y0); cv::rectangle(src, rect, cv::Scalar(0, 255, 0),5, cv::LINE_8,0); } } cv::imwrite("./result_test.jpg", src); std::cout << "result_test.jpg produced in the directory:/home/user/AscendProjects/MyApp/out." << std::endl; }
重新编译:
重新执行:
上图绿色的就是YoLoV3标记出来的框。
PipeLine:YoLoV3替换为YoLoV5:
既然 YoLoV3和ResNet50都跑通了。那么把 YoLoV5替换掉 YoLoV3就可以。
参考 https://bbs.huaweicloud.com/forum/thread-118598-1-1.html
类似 YoLoV3 的编排,对比链接中 YoLoV5的pipeline。我们做个修改:
可以先把 YoLoV3 的pipeline备份下
检视一下,
YoLoV3有aippconfig、coco.names、yolov3_tf_bs1_fp16.cfg还有一个om,另外还有一个libMpYOLOv3PostProcessor.so
看看对应的yolov5是否齐全:
aipp_yolov5.cfg
那就先生成YoLoV5的离线模型。
YoLoV5的离线模型的生成:
mkdir /root/yolov5s_convert
cd /root/yolov5s_convert
wget https://github.com/ultralytics/yolov5/archive/v2.0.tar.gz --no-check-certificate
tar -xzf v2.0.tar.gz
vi yolov5-2.0/models/export.py
修改第48行opset_version为11
下载权重文件 yolov5s.pt
cd yolov5-2.0
wget https://github.com/ultralytics/yolov5/releases/download/v2.0/yolov5s.pt --no-check-certificate
python models/export.py --weights ./yolov5s.pt --img 640 --batch 1
好像还得装个pytorch。
去PyPorch官网下载 Pytorch 1.8.1:
pip3 install torch==1.8.1+cpu torchvision==0.9.1+cpu torchaudio==0.8.1 -f https://download.pytorch.org/whl/lts/1.8/torch_lts.html
重新执行:
python models/export.py --weights ./yolov5s.pt --img 640 --batch 1
报缺_lzma模块:
查资料找解决方案:https://blog.csdn.net/moxiao1995071310/article/details/97399809/
照此办理:
apt-get install libbz2-dev
apt-get install lzma
apt-get install liblzma-dev
重新编译python3.7.5:
cd /root/Python-3.7.5
./configure --prefix=/usr/local/python3.7.5 --enable-optimizations --enable-shared
...
make
...
需要耐心等待416个test走完。。这是一个漫长的过程:
make install
...
验证下:
下面反复执行:
python models/export.py --weights ./yolov5s.pt --img 640 --batch 1
系统会报缺什么模块,然后我们就按照要求装什么模块:
pip install pyyaml
pip install tqdm
pip install onnx
pip install coremltools -i https://pypi.tuna.tsinghua.edu.cn/simple/
这次再执行:
python models/export.py --weights ./yolov5s.pt --img 640 --batch 1
这到底是成功了还是失败了?
检查了一下,并没有生成onnx文件。应该还是有问题的:
度娘搜索:'google.protobuf.descriptor' has no attribute '_internal_create_key'
https://blog.csdn.net/lemon4869/article/details/107299879
照此办理:
pip install --upgrade protobuf -i https://pypi.tuna.tsinghua.edu.cn/simple/
再来:
python models/export.py --weights ./yolov5s.pt --img 640 --batch 1
。。。
。。。
这回好像真的生成了onnx文件。
这个onnx文件终于生成了。先撒个小花。。。。
然后继续:
python3.7 -m onnxsim --skip-optimization yolov5s.onnx yolov5s_sim.onnx
pip install --upgrade pip
pip install onnx-simplifier
对onnx图使用onnx-simplifer进行简化:
python3.7 -m onnxsim --skip-optimization yolov5s.onnx yolov5s_sim.onnx
下载 https://bbs.huaweicloud.com/forum/thread-118598-1-1.html
帖子的mxManufacture_yolov5_example.zip 附件,解压,
上传slicy脚本:modify_yolov5s_slice.py
修改模型Slice算子
python3.7 modify_yolov5s_slice.py yolov5s_sim.onnx
生成 yolov5s_sim_t.onnx
在当前目录下编辑2个文件:
编辑 aipp_yolov5.cfg
编辑trans_onnx2om.sh
听 @Fate丶SSS 老师说, 目前的image-for-mindx镜像已经不需要升级 tranpose_d.patch补丁了。
下面开始做ATC模型转换:
bash trans_onnx2om.sh
转换成功,OM离线模型文件已成功生成。
将与yolov5s_sim_t.om相适应的配置文件yolov5.cfg和标签文件coco.names上传到同一目录:
下面正式把sample.pipelin的YoLoV3的目标检测部分修改为YoLoV5的目标检测部分了!
打开MindStudio,在Models目录下创建一个yolov5的目录,再到终端将前面准备好的文件都复制到这个目录下:
/root/AscendProjects/Resnet50/models/yolov5
cp /root/yolov5s_convert/yolov5-2.0/aipp_yolov5.cfg .
cp /root/yolov5s_convert/yolov5-2.0/coco.names .
cp /root/yolov5s_convert/yolov5-2.0/yolov5s_sim_t.om .
cp /root/yolov5s_convert/yolov5-2.0/yolov5.cfg .
修改 resize部分,改为640X640且等比缩放
"mxpi_imageresize0": { "props": { "parentName": "mxpi_imagedecoder0", "resizeHeight": "640", "resizeWidth": "640", "resizeType": "Resizer_KeepAspectRatio_Fit" }, "factory": "mxpi_imageresize", "next": "mxpi_modelinfer0" },
修改infer推理部分,将YoLoV3改为YoLoV5:
"mxpi_modelinfer0": { "props": { "modelPath": "../models/yolov5/yolov5s_sim_t.om", "postProcessConfigPath": "../models/yolov5/yolov5.cfg", "labelPath": "../models/yolov5/coco.names", "postProcessLibPath": "libMpYOLOv5PostProcessor.so" }, "factory": "mxpi_modelinfer", "next": "mxpi_imagecrop0" },
为了表示这次处理的是yolov5的结果,我们将main.cpp输出的代码做个修改:
并删除out下的jpg文件:
重新编译:
重新运行:
可以看出,result_test-yolov5文件已生成。
输出结果如下:
2021-08-01 00:45:11 - [INFO] Resnet50 start running... 2021-08-01 00:45:11 - [INFO] Execute command on local: export LD_LIBRARY_PATH=/usr/local/Ascend/mindx_sdk/mxVision_2.0.1/linux-x86_64/mxVision-2.0.1/lib:/usr/local/Ascend/mindx_sdk/mxVision_2.0.1/linux-x86_64/mxVision-2.0.1/lib/plugins:/usr/local/Ascend/mindx_sdk/mxVision_2.0.1/linux-x86_64/mxVision-2.0.1/opensource/lib:/usr/local/Ascend/mindx_sdk/mxVision_2.0.1/linux-x86_64/mxVision-2.0.1/opensource/lib64:/root/AscendProjects/Resnet50/lib:/root/AscendProjects/Resnet50/lib/plugins:${LD_LIBRARY_PATH} && export GST_PLUGIN_SCANNER=/usr/local/Ascend/mindx_sdk/mxVision_2.0.1/linux-x86_64/mxVision-2.0.1/opensource/libexec/gstreamer-1.0/gst-plugin-scanner && export GST_PLUGIN_PATH=/usr/local/Ascend/mindx_sdk/mxVision_2.0.1/linux-x86_64/mxVision-2.0.1/opensource/lib/gstreamer-1.0:/usr/local/Ascend/mindx_sdk/mxVision_2.0.1/linux-x86_64/mxVision-2.0.1/lib/plugins:/root/AscendProjects/Resnet50/lib/plugins && export MX_SDK_HOME=/usr/local/Ascend/mindx_sdk/mxVision_2.0.1/linux-x86_64/mxVision-2.0.1 && cd /root/AscendProjects/Resnet50/out && ./main Begin to initialize Log. The output directory(./logs) of logs file exist. Save logs information to specified directory(./logs). W0801 00:45:12.691449 21204 main.cpp:131] Results:{"MxpiObject":[{"MxpiClass":[{"classId":163,"className":"beagle","confidence":0.837890625}],"classVec":[{"classId":16,"className":"dog","confidence":0.672381699,"headerVec":[]}],"x0":73.496948200000006,"x1":939.73120100000006,"y0":132.934708,"y1":603.36547900000005}]} result_test-yolov5.jpg produced in the directory:/root/AscendProjects/Resnet50/out. 2021-08-01 00:45:13 - [INFO] Run finished, exit status: 0
格式化结果:
{ "MxpiObject": [{ "MxpiClass": [{ "classId": 163, "className": "beagle", "confidence": 0.837890625 }], "classVec": [{ "classId": 16, "className": "dog", "confidence": 0.672381699, "headerVec": [] }], "x0": 73.496948200000006, "x1": 939.73120100000006, "y0": 132.934708, "y1": 603.36547900000005 }] }
貌似 confidence要比前面YoLoV3的低一点。
前面YoLoV3+ResNet50的结果如下:(前面贴过了,再重贴一遍,便于比较)
{ "MxpiObject": [{ "MxpiClass": [{ "classId": 163, "className": "beagle", "confidence": 0.87109375 }], "classVec": [{ "classId": 16, "className": "dog", "confidence": 0.99641436299999997, "headerVec": [] }], "x0": 125.63256800000001, "x1": 918.29089399999998, "y0": 116.434044, "y1": 597.21276899999998 }
下面考虑在图片中叠加 YoLoV5检测和ResNet50分类的className结果:
需要解析出
MxpiObject/MxpiClass/className
和
MxpiObject/classVec/className
RapidJSON的安装与使用
由于代码中的json神操作张小白不是很明白,所以自行下载了RapidJSON:http://rapidjson.org/zh-cn/index.html
RapidJSON 是只有头文件的 C++ 库。
github比较慢,换成在 gitee上的镜像
切换到 项目的目录,将include目录复制到项目下:
将 取json报文的和图片叠加文字部分加上去:
#include "rapidjson/document.h" #include "rapidjson/prettywriter.h" #include "rapidjson/stringbuffer.h" #include
在取到result之后,保存YoLoV5的className和ResNet50的className:
std::string myClassName1 , myClassName2; rapidjson::Document dom; unsigned int i,j; if(!dom.Parse(result.c_str()).HasParseError()) { if(dom.HasMember("MxpiObject") && dom["MxpiObject"].IsArray()){ const rapidjson::Value& arr= dom["MxpiObject"]; for (i = 0; i < arr.Size(); ++i) { const rapidjson::Value& obj = arr; if(obj.HasMember("MxpiClass") && obj["MxpiClass"].IsArray()){ const rapidjson::Value& arr2= obj["MxpiClass"]; for (j = 0; j < arr2.Size(); ++j) { const rapidjson::Value& obj2 = arr2[j]; if(obj2.HasMember("className") && obj2["className"].IsString()){ myClassName1 = obj2["className"].GetString(); std::cout << "className:" << myClassName1 << std::endl; } else{ } }//end of for j } if(obj.HasMember("classVec") && obj["classVec"].IsArray()){ const rapidjson::Value& arr2= obj["classVec"]; for (j = 0; j < arr2.Size(); ++j) { const rapidjson::Value& obj2 = arr2[j]; if(obj2.HasMember("className") && obj2["className"].IsString()){ myClassName2 = obj2["className"].GetString(); std::cout << "className:" << myClassName2 << std::endl; } else{ } }//end of for j } } //end of for i } else{ } } else{ std::cout << "Parse Result Json string error" << std::endl; }
并在最后 画框的时候加上 文字部分:
cv::Rect rect(x0, y0, x1 - x0, y1 - y0); cv::rectangle(src, rect, cv::Scalar(0, 255, 0),5, cv::LINE_8,0); cv::putText(src,myClassName1,cv::Point(x0+50,y0+50),FONT_HERSHEY_SIMPLEX,1,cv::Scalar(255,255,0)); cv::putText(src,myClassName2,cv::Point(x0+50,y0+100),FONT_HERSHEY_SIMPLEX,1,cv::Scalar(255,255,0));
为了可以灵活地切换图片,我们将输入的图片和输出的图片都设置个变量
// read image file and build stream input // std::string picFileName = "../data/test.jpg"; // std::string picFileName_result = "../out/test_result.jpg"; std::string picFileName = "../data/test_tiger.jpg"; std::string picFileName_result = "../out/test_tiger_result.jpg"; // std::string picFileName = "../data/test_rabbit2.jpg"; // std::string picFileName_result = "../out/test_rabbit2_result.jpg";
为了可以切换pipeline,我们将pipeline文件也随时做调整
// std::string pipelineConfigPath = "../pipeline/Sample.pipeline"; // std::string pipelineConfigPath = "../pipeline/YoloV5.pipeline"; // std::string pipelineConfigPath = "../pipeline/yoloV3.pipeline"; std::string pipelineConfigPath = "../pipeline/yoloV3-resnet50.pipeline";
重新编译,运行:
YoloV3+ResNet50认为它是 dog和 beagle(猎兔犬)
从上图可见,类别也在图片中叠加上去了!!
张小白另找了老虎的照片做了如下的试验:
编译:
运行:
YoLoV3认为它是个斑马:zebra
而YoLoV5认为它啥也不是:
而YoLoV3+ResNet50就这样认为:
ResNet50分类倒是对的。确实是老虎。
Fate老师解释说,本来YoLoV3和YoLoV5里面就没有老虎这个选项,所以看不出来反而是对的。如果将其看成是斑马,那反而说明YoloV5比YoloV3的精度要高。。。
额,,,不知道童鞋们Get到没有。。
其实这样高玩赛的作业应该算是做完了吧。
希望同学们能够通过这次CANN训练营应用营的深度学习训练,能够成功使用MindX SDK和MindStudio进行高阶推理。(掌握技能很重要的,也许有个母老虎还需要你去目标检测和分类一下呢。。。)
(全文完,谢谢阅读)
CANN训练营第二期 高玩赛已经开启,请点击:https://bbs.huaweicloud.com/forum/thread-129524-1-1.html
添加下方工作人员微信,添加备注:CANN训练营~ 邀请进群~
C++ pytorch 昇腾 深度学习 神经网络
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。