Ubuntu18.04上MNN编译与使用(Python版)

目录

1.MNN简介

2.MNN编译

2.1准备工作

2.2编译

2.2.1基础环境依赖

2.2.2主库编译

2.2.2主要工具模块编译

3.模型转化

4.使用MNN进行推理 

4.1 Expr API(推荐)

4.2 Session API


1.MNN简介

        简单介绍一下:MNN(Mobile Neural Network)是一个轻量级的深度神经网络推理引擎,在端侧加载深度神经网络模型进行推理预测。想详细了解MNN的定义以及构成,请参考官方文档:欢迎使用MNN文档 — MNN-Doc 2.1.1 documentation

2.MNN编译

2.1准备工作

        从github下载MNN,参考如下:

git clone https://github.com/alibaba/MNN.git

2.2编译

2.2.1基础环境依赖

(1)cmake(3.10 以上)

(2)protobuf (3.0 以上)

  • 指protobuf库以及protobuf编译器。版本号使用 protoc --version 打印出来。
  • Ubuntu需要分别安装 libprotobuf-dev 以及 protobuf-compiler 两个包。
    sudo apt update
    sudo apt install protobuf-compiler libprotobuf-dev
    

2.2.2主库编译

cd /MNN安装目录/MNN

./schema/generate.sh

./tools/script/get_model.sh # 不建议通过这种方法下载,模型仅demo工程需要,后续有需要时另外下载

mkdir build && cd build && cmake .. && make -j8

# 编译OpenCL库
# mkdir build && cd build && cmake .. -DMNN_OPENCL=ON && make -j8

2.2.3主要工具模块编译

(1)编译模型转化工具

# 在主库编译中,已经创建了build目录,后续其它编译操作均在build目录下进行

cmake .. -DMNN_BUILD_CONVERTER=ON

make -j8

(2)编译测试工具

cmake .. -DMNN_BUILD_TOOL=ON

make -j8

(3)编译BnechMark工具

cmake .. -DMNN_BUILD_BENCHMARK=ON

make -j8

(4)编译模型量化工具

cmake .. -DMNN_BUILD_QUANTOOLS=ON
make -j8

(5)编译评估工具

cmake .. -DMNN_EVALUATION=ON
make -j8

2.3 pymnn构建

cd /MNN安装目录/MNN/pymnn/pip_package
python build_deps.py
python setup.py install --version {MNN版本}

        MNN版本可通过如下方式查看:

cd /MNN安装目录/MNN/build
./MNNConvert --version

    编译pymnn时如果遇到问题,可以参考我的另一篇博文:

Ubuntu 编译pymnn遇到的问题及解决方案

3.模型转化

cd /Your Dictionary/build

./MNNConvert -f ONNX --modelFile /PATH_to_Transfer_test.onnx --MNNModel /PATH_to_Transfer_test.mnn

# 常用参数说明
# -- f:进行转化的模型类型,如:TF, CAFFE, ONNX, TFLITE, MNN, TORCH, JSON
# -- modelFile: 进行转化的模型文件名,如:transfer_test.onnx
# -- MNNModel: 转化后得到的模型文件名,如:transfer_test.mnn
# -- fp16: 将conv/matmul/LSTM的float32参数保存为float16,模型将减小一半,精度基本无损
# -- prototxt: caffe模型结构描述文件,仅当模型是caffe时选择,如:transfer_text.prototxt

4.使用MNN进行推理 

        官方给出的推理API有三种方法,分别是Session API, Module API,Expr API。下面我们将着重介绍Expr API(推荐)和Session API 。

4.1 Module API(推荐

        使用步骤以及流程图,仅展示如何使用该API以及一些注意事项,完整的demo请参考我的另一篇博客:

Yolov5在MNN上的推理实现(Python版)

(1)加载MNN模型:

        API:MNN.nn.load_module_from_file('mnn_model', ["input_name"], ["output_name"])

        参数介绍:input_name和output_name分别代表模型的输入输出名。输入输出的名字可通过netron来查看,如图:

(2)构建占位符,保存输入numpy:

        API:MNN.expr.placeholder(shape, format, dtype)

        参数介绍:输入图片的shape、输入图片的数据排列格式、数据的类型。并通过Var.write(data)将数据写入Var中。

(3)对输入数据进行格式转换

        原因:opencv读取图片的格式是NHWC,但是MNN Module需要的数据排列格式是NC4HW4。

        API:MNN.expr.convert(data, format)

        参数介绍:目标数据、目标数据排列格式

(4)执行推理

        API: module.forward(data)

        参数介绍:module是通过MNN.nn.load_module_from_file()实例化的对象。data是输入数据

(5)对输出数据进行格式转换

        原因:推理后的数据排列格式是NC4HW4,为了方便后续处理,需要转成NCHW

        API:同(3)

(6)对输出数据进行类型转换

        原因:通过expr API推理得到的结果的数据类型是Var数据类型,不方便后续的处理,因此需要将其转化为其它数据类型,如ndarray。

        API:var_data.read(),var_data.read_as_tuple()

        参数介绍:var_data:需要进行转化的Var数据,前者转换后得到是ndarray数据类型,后者转换后得到是tuple数据类型。

# (1) 加载MNN模型
net = MNN.nn.load_module_from_file(mnn_model_path,  ["input_name"], ["output_name"])
# (2) 构建一个Var类型的占位符来保存numpy
input_var = MNN.expr.placeholder(im.shape, MNN.expr.NCHW)
input_var.write(im)
# (3) 数据排列方式转换
input_var = MNN.expr.convert(input_var, MNN.expr.NC4HW4)
# (4) 推理
output_var = net.forward(input_var)
# (5) 数据排列方式转换
output_var = MNN.expr.convert(output_var, MNN.expr.NCHW)
# (6) 数据类型转换
output_var = output_var.read()

4.2 Session API

        使用步骤及流程图,仅展示如何使用该API以及一些注意事项,完整的demo请参考我的另一篇博客:

Yolov5在MNN上的推理实现(Python版)

(1)加载MNN模型

                API:MNN.Interpreter(mnn_path)

(2)定义配置,并根据配置创建session,常用配置如下:

                API: interpreter.createSession(config)

                参数介绍:interpreter,(1)中加载的模型名

KEY 说明
backend 可选值:"CPU"或0(默认), "OPENCL"或3,"OPENGL"或6"VULKAN"或7"METAL"或1"TRT"或9"CUDA"或2"HIAI"或8
precision 可选值:"normal"(默认), "low","high","lowBF"
numThread value为推理线程数,只在 CPU 后端下起作用

(3)获取session的输入,session是推理数据的持有者。此时,该tensor为空,类似占位符的作用。

        API: interpreter.getSessionInput(session)

        参数介绍:session,(2)中创建的session名

(4) 创建临时Tensor,用来存储实际输入数据

        API:Tensor(shape, dtype, input_data, dimension)

        参数介绍: 创建一个指定形状,指定数据类型,数据,数据排列方式(tensorflow是nhwc,caffe是nchw)的tensor。

        注:如果输入数据是numpy或者其它格式类型(除MNN定义的类型外),都需要使用这种方式(带四个参数的方式)创建,否此创建出来的tensor中没有数据或者读不出来。

(可选) 创建完Tensor后,可以手动将输入Tensor的形状校正成正常输入形状,在校正完成后记得重新分配内存。

          API:interpreter.resizeTensor(data, shape)

          API: interpreter.resizeSession(session)

(5)将临时Tensor的数据复制给输入Tensor,即(3)中的Tensor。需要注意的是:MNN Tensor的数据排列方式是NC4HW4,并非NCHW格式,因此对中间结果的可视化并不友善。

        API:  target_tensor.copyFrom(ori_tensor)

        参数介绍: 从ori_tensor拷贝数据到target_tensor中

(6)执行会话推理

        API: interpreter.runSession(session)

(7)创建输出Tensor,此时,该tensor为空,类似占位符的作用。

        API: interpreter.getSessionOutput(session)

(8)创建临时Tensor,用来存储算法结果。

        API:Tensor(shape, dtype, input_data, dimension)        

(9)拷贝临时数据到输出Tensor

        API:ori_tensor.copyToHostTensor(target_tensor)

        参数介绍: 从ori_tensor拷贝数据到target_tensor中

(10)将输出tensor转换为numpy类型方便后续处理

         API:tensor.getNumpyData()

(11)补充

        mnn中的输入输出的shape并没有在netron中显著标明,要通过MNNTensor.getShape()查看。

    # (1)加载模型
    interpreter = MNN.Interpreter(mnnModulePath)
    # (2)配置
    config = {}
    config['precision'] = 'low'
    config['backend'] = 'CPU'
    config['thread'] = 4
    # 根据配置创建session
    session = interpreter.createSession(config)
    # (3)获取输入Tensor名
    input_tensor = interpreter.getSessionInput(session)
    # (可选)改变Tensor shape
    interpreter.resizeTensor(input_tensor, (1, 3, 640, 640))
    interpreter.resizeSession(session)
    # (4)创建临时Tensor
    mnn_input = MNN.Tensor((1, 3, 640, 640), MNN.Halide_Type_Float, im, MNN.Tensor_DimensionType_Caffe)

    # (5)拷贝数据到输入Tensor
    input_tensor.copyFrom(mnn_input)
    # (6)执行会话推理
    interpreter.runSession(session)
    # (7)获取输出Tensor名
    output_tensor = interpreter.getSessionOutput(session)
    # (8)创建临时Tensor
    tmp_output = MNN.Tensor((1, 25000, 85), MNN.Halide_Type_Float, np.ones([1, 25000, 85]).astype(np.float32),
                            MNN.Tensor_DimensionType_Caffe)
    # (9)拷贝数据到输出Tensor
    output_tensor.copyToHostTensor(tmp_output)
    # (10)转换类型
    pred = tmp_output.getNumpyData()

猜你喜欢

转载自blog.csdn.net/m0_46303486/article/details/129931784