java调⽤onnx模型_pytorch模型的部署(系列⼀)--ncnn的编
译和使⽤
因为我⼀直都是在pytorch上⾯训练模型,所以部署模型通常采取三种⽅案:
⽅案⼀:利⽤腾讯开源的ncnn库(nihui⼤神⽜⽪!!);但这个适合移动端部署,特别是针对andriod的极致优化。
⽅案⼆:libtorch(c++版本的pytorch);pytorch为了弥补部署⽅⾯的劣势,,libtorch也在不断推陈出新,以不断打压tf等⼀众⽼炮⽣存空间
⽅案三:NVIDIA出品的tensorRT;这个是专门为nvidia的显卡作推断⽽⽣,所以如果是要⽤gpu做infer,那么tensorRT将会是⼀个好的选择
如果是针对ncnn和tensorRT,那么⼀般的⽅案是将pytorch⽣成的.pth模型先转换成.onnx模型,然后利⽤onnx的万能属性往不同的框架上移植。
将会分三个系列来记录⼀下如何使⽤者三种⽅式来部署pytorch的模型,(其实主要是⼯具安装和基本使⽤),特别是安装的坑实在是巨多(教程质量是在是令⼈唏嘘,⽽且抄袭严重。。),记录⼀下以免过后忘记。本篇⾸先记录下ncnn的安装和基本使⽤!
⽹上看了很多ncnn的教程,感觉好多都回避了重要步骤,导致你我这样的新⼿ncnn编译失败,(包括ncnn的官⽅⽂档也只是对protbuf提
ubuntu16,所以按照ncnn官⽅的build-for-linux-x86的指导来操作.
了⼀句,并未强调这就导致很多不细⼼的⽹友看飘).我的系统是ubuntu16
编译ncnn的准备⼯作:g++, cmake,protobuf,opencv,
java调用python模型编译ncnn的准备⼯作
前两个不必说,很容易。其中安装protobuf和opencv都可以分别写⼀篇博客了。
关于opencv的安装:不仅是对于ncnn,⽆论是对于哪种部署⽅式,opencv都是必须的。所以⼀定要提前装好。⽹上教程蛮多,这⾥推关于opencv的安装:
荐⼀篇还不错的
ubuntu16.04安装opencv3.4.1教程_⼈⼯智能_cocoaqin的博客-CSDN博客b log.csdn
注意两点:
如果电脑⾥⾯已经装过anoconda,那么编译opencv的过程很可能会出现问题,需要在cmake的时候指定⼀些选项(遇到错误了百度or ⾕歌可解决)。
关于版本的问题 2.x/3.x/4/x,这个我⼀般选择3.x,如果选得不合适可能会导致ncnn调不动。。
关于protobuf的安装:这个需要强调说明,这个直接决定了ncnn编译的成功与否,⽹上⼤部分失败的貌似都是protobuf没有装成功。但关于protobuf的安装:
是解决办法千奇百怪的,像神马在CMakeList⾥⾯注释掉add onnx、caffe神马的,这个指标不治本啊,显然是不⾏的。
protobuf的github仓库:
github/protocolbuffers/protobuf/tree/master/src g ithub
可以按照上⾯链接的指导进⾏安装:关键步骤标识如下:
如果protobuf安装成功后,编译ncnn还是报错,那可以试试重启,还不⾏,重新编译安装protobuf再重启。(⼀切的问题基本都可以在ncnn仓库的issue区到答案,学会利⽤issue区是⼀项重要的能⼒)。
ncnn的github开源仓库:
github/Tencent/ncnn/wiki/how-to-build#build-for-linux-x86g ithub
正式编译ncnn:
对于官⽅给的编译步骤来说,cmake那⾥可以指定的选项:vulkan是针对gpu的,如果想要ncnn能调⽤gpu做推理,那么选项需要打开。
完事了记得在./build下⾯make install⼀下。完了会⽣成include和lib,后⾯利⽤ncnn调⽤⾃⼰模型时需要链接到这⾥。
关于x.pth转onnx:嗯。。。这个不多说,很复杂,坑巨多,⾃求多福,如果在pytorch⾥⾯直接定义了很多骚操作,那么多半转onnx要关于x.pth转onnx:嗯。。。
凉,唯⼀的办法就是绕开某些操作。这个话题不是本篇重点,跳过。说⼀点就是:输⼊和输出⼀定要命名啊:⼏个输出就命名⼏个,这样在⽤ncnn的load的时候才能直接提取出来。⼀般导出onnx的代码举例如下:
关于onnx2ncnn:这个是重点,得到了onnx模型,你也就告别了pytorch的框架了,可以随⼼所欲了,
现在需要将onnx格式的模型转成关于onnx2ncnn:
ncnn能够加载的.bin和.param。⽤于是前⾯编译好的ncnn带的⼯具,如果ncnn编译成功的话:ncnn/build/tools/onnx/下⾯有⼀个编译好的onnx2ncnn。只需要:
./ model.param model.bin
(model是你的onnx名字),如果什么都没输出,那恭喜成功了!如果遇到下⾯这种情况:
那完蛋!虽然成功⽣成了.param和.bin,但是⽤ncnn 来load模型的时候⼀般都会失败!因为⽬前ncnn对于某⼀些操作还不⽀持,所以这⾥还有引⼊⼀个⼯具:ONNX Simplifier!
ONNX Simplifier:
github连接:
daquexian/onnx-simplifier g ithub
如果simplifier都失败了,那可能真得回过头反思反思(根据报错的位置对应到pytorch⽹络的位置)⾃⼰的pytorch定义的⽹络或者某些后处理操作是不是有问题(⼀般问题出在后处理部分:譬如⽬标检测的坐标解码,nms等),所以⼀般不建议将除⽹络以外的部分打包到onnx 中,其实后处理这些最好还是⽤c++来写,主要也快(我个⼈也有疑问,到底是把后处理放到⾥⾯好,还是单独c++来写好?)
ncnn调⽤⽣成的模型以及⼀些基本操作:
建议新建⼀个⽬录来存放下⾯的东西:
编写,main.cpp
<⾥⾯需要包括下⾯的⼀些基本内容:
cmake_minimum_required(VERSION 3.5)
find_package(OpenCV REQUIRED core highgui imgproc)
include_directories(xx/.../ncnn/build/install/include/ncnn)
link_directories(xx/.../ncnn/build/install/lib)
FIND_PACKAGE( OpenMP REQUIRED)
if(OPENMP_FOUND)
message("OPENMP FOUND")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")
endif()
add_executable(main main.cpp)
target_link_libraries(main ncnn ${OpenCV_LIBS})
编写main.cpp:
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <algorithm>
#include <vector>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include "opencv2/imgproc/imgproc.hpp"
#include "net.h"
using namespace std;
//这个函数是官⽅提供的⽤于打印输出的tensor
void pretty_print(const ncnn::Mat& m)
{
for (int q=0; q<m.c; q++)
{
const float* ptr = m.channel(q);
const float* ptr = m.channel(q);
for (int y=0; y<m.h; y++)
{
for (int x=0; x<m.w; x++)
{
printf("%f ", ptr[x]);
}
ptr += m.w;
printf("n");
}
printf("------------------------n");
}
}
//main函数模板
int main(){
string img_path = "xxx.jpg";
cv::Mat img = cv::imread(img_path, cv::IMREAD_COLOR);
cv::Mat img2;
int input_width = 512;//转onnx时指定的输⼊⼤⼩
int input_height = 512;
// resize
cv::resize(img, img2, cv::Size(input_width, input_height));
// 加载转换并且量化后的alexnet⽹络
ncnn::Net net;
//net.opt.num_threads=1;
net.load_param("xxx.param");
net.load_model("xxx.bin");
// 把opencv的mat转换成ncnn的mat
ncnn::Mat input = ncnn::Mat::from_pixels(img2.data, ncnn::Mat::PIXEL_BGR, ls, ws);
/
/ ncnn前向计算
ncnn::Extractor extractor = ate_extractor();
extractor.input("input", input);
ncnn::Mat output0,output1;//取决于模型的输出有⼏个
pretty_print(output0);
pretty_print(output1);
/*
// 或者展平后输出
ncnn::Mat out_flatterned = shape(output0.w * output0.h * output0.c);
std::vector<float> scores;
for (int j=0; j<out_flatterned.w; j++)
{
scores[j] = out_flatterned[j];
}
*/
cout<<"done"<<endl;
return 0;
}
上⾯是⼀个基本模板;后续的⼀些针对tensor的处理就需要根据需求来定,譬如需要对其进⾏解码,NMS等,可以编写纯c++的代码来处理。
ncnn的就先到这⾥,后⾯有时间再好好研究研究ncnn框架。下⼀篇会讲⼀下如何使⽤LibTorch来部署pytorch的模型。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论