首页 > 编程语言 >使用 C++ 部署深度学习模型快速上手方案

使用 C++ 部署深度学习模型快速上手方案

时间:2022-11-14 14:48:07浏览次数:64  
标签:tensor 模型 C++ Arm 编译 MegEngine 深度 Lite ptr

本文将从获取一个训练好的 shufflenet_v2 模型出发, 讲解如何使用 MegEngine Lite 的 C++ 接口将其部署到 CPU(Linux x86 / Android Arm)环境下运行。主要分为以下小节:

参见:

MegEngine Lite 还可以 通过 Python 接口进行使用, 使用方便但有局限性。

导出已经训练好的模型

请参考 获得用于 MegEngine Lite 推理的模型

编写 Inference 代码

首先创建一个 main.cpp, 在这个文件中将直接调用 MegEngine Lite 的接口运行 shufflenet_v2.mge 模型, 输入数据 input_tensor 是随机生成的,所以不用在乎计算结果。

#include <iostream>
#include "lite/network.h"
using namespace lite;

int main(int argc, char** argv) {
    std::cout << " Usage: ./demo_deploy model_name" << std::endl;
    if (argc != 2) {
        std::cout << " Wrong argument" << std::endl;
        return 0;
    }

    std::string model_path = argv[1];

    //! create and load the network
    std::shared_ptr<lite::Network> network = std::make_shared<Network>();

    //! load the model
    network->load_model(model_path);

    //! get the input tensor of the network with name "data"
    std::shared_ptr<Tensor> input_tensor = network->get_io_tensor("data");

    //! fill the rand data to input tensor
    srand(static_cast<unsigned>(time(NULL)));
    size_t length =
            input_tensor->get_tensor_total_size_in_byte() / sizeof(float);
    float* in_data_ptr = static_cast<float*>(input_tensor->get_memory_ptr());
    for (size_t i = 0; i < length; i++) {
        in_data_ptr[i] =
                static_cast<float>(rand()) / (static_cast<float>(RAND_MAX));
    }

    //! forward
    network->forward();
    network->wait();

    //! get the inference output tensor of index 0
    std::shared_ptr<Tensor> output_tensor = network->get_output_tensor(0);
    float* predict_ptr = static_cast<float*>(output_tensor->get_memory_ptr());
    float sum = 0.0f, max = predict_ptr[0];
    for (size_t i = 0; i < 1000; i++) {
        sum += predict_ptr[i];
        if (predict_ptr[i] > max) {
            max = predict_ptr[i];
        }
    }
    std::cout << "The output SUM is " << sum << ", Max is " << max << std::endl;
}

上面代码主要完成了几个步骤,包括:

  1. 创建默认配置的 Network;

  2. 载入模型,MegEngine Lite 将读取并解析模型文件,并创建计算图;

  3. 通过输入 Tensor 的名字获取模型的输入 Tensor, 并设置随机数作为输入数据;

  4. 执行 Inference 逻辑;

  5. 获取模型输出 Tensor, 并处理输出数据。

至此完成了一个 shufflenet_v2 模型的推理过程的 C++ 代码编写。

但在真正运行这段代码之前,还需要编译该 C++ 源文件,并链接 MegEngine Lite 库文件。 ⬇️ ⬇️ ⬇️

编译 MegEngine Lite

注解

  • 这一步的目的是获得 MegEngine Lite 的静态链接库和动态链接库,供我们上面代码编译时候进行链接; 编译的过程和 从源码编译 MegEngine 中的介绍是一致的。

  • 下面将演示在 Linux x86 下使用动态链接,Android Arm 上使用静态链接的流程:

  1. 首先需要 Clone 整个 MegEngine 工程,并进入到 MegEngine 的根目录:
git clone --depth=1 [email protected]:MegEngine/MegEngine.git
cd MegEngine
  1. 环境准备 & 执行编译:

    Linux x86

    准备编译依赖的子模块:

./third_party/prepare.sh

安装英特尔数学核心库(MKL):

./third_party/install-mkl.sh

本机编译 MegEngine Lite:

scripts/cmake-build/host_build.sh

Android Arm

准备编译依赖的子模块:

./third_party/prepare.sh

从安卓 官网 下载 NDK 并解压到某路径, 并将改路径设置为 NDK_ROOT 环境变量:

export NDK_ROOT=/path/to/ndk

交叉编译 MegEngine Lite:

scripts/cmake-build/cross_build_android_arm_inference.sh

编译完成之后 MegEngine Lite 库和头文件路径 /path/to/megenginelite-lib

  • Linux x86: build_dir/host/MGE_WITH_CUDA_OFF/MGE_INFERENCE_ONLY_ON/Release/install/lite/

  • Android Arm: build_dir/android/arm64-v8a/Release/install/lite/

编译 Inference 代码

有了上一步得到的 MegEngine Lite 库文件,我们就可以在编译 Inference 代码的时候进行动态链接或静态链接。 下面分别用 Linux x86 和 Android Arm 来展示两种链接方式,演示编译 Inference 代码的步骤:

Linux x86 动态链接编译

根据自身环境选择编译器(这里使用的是 clang++, 也可以用 g++),动态链接 liblite_shared.so 文件:

export LITE_INSTALL_DIR=/path/to/megenginelite-lib #上一步中编译生成的库文件安装路径
export LD_LIBRARY_PATH=$LITE_INSTALL_DIR/lib/x86_64/:$LD_LIBRARY_PATH
clang++ -o demo_deploy \
  -I$LITE_INSTALL_DIR/include main.cpp \
  -llite_shared -L$LITE_INSTALL_DIR/lib/x86_64

编译完成之后,就得到了可执行文件 demo_deploy.

Android Arm 静态链接编译

Android Arm 编译为交叉编译(在 Linux 主机上编译 Android Arm 中运行的可执行程序)。

以链接 MegEngine Lite 的静态库作为示例,需要确保 NDK 环境准备完成,

export LITE_INSTALL_DIR=/path/to/megenginelite-lib #上一步中编译生成的库文件安装路径
export PATH=${NDK_ROOT}/toolchains/llvm/prebuilt/linux-x86_64/bin/:$PATH
export CXX=aarch64-linux-android21-clang++
${CXX} -llog -lz -s \
  -I${LITE_INSTALL_PATH}/include main.cpp \
  ${LITE_INSTALL_PATH}/lib/aarch64/liblite_static_all_in_one.a \
  -o demo_deploy

编译完成之后,需要将 demo_deploy 和模型文件 shufflenet_v2.mge 拷贝到 Android Arm 机器上。

执行 Inference 文件,验证结果

最后执行编译好的文件,就可以看到推理结果:

./demo_deploy shufflenet_v2.mge

这样就快速完成了 X86 和 Arm 上简单的 demo 部署。

在本例中,最后计算结果可以看到:经过 softmax 之后,输出的结果中 sum = 1, 符合 softmax 的输出特点。

GitHub:MegEngine 旷视天元 (欢迎 star~

Gitee:MegEngine/MegEngine

MegEngine 官网:MegEngine-深度学习,简单开发

欢迎加入 MegEngine 技术交流 QQ 群:1029741705

标签:tensor,模型,C++,Arm,编译,MegEngine,深度,Lite,ptr
From: https://www.cnblogs.com/megengine/p/16888968.html

相关文章

  • C和C++的区别
    一、面向过程语言和面向对象语言C语言是面向过程语言,而C++是面向对象语言(一)面向过程和面向对象的区别(1)面向过程:面向过程编程就是分析出解决问题的步骤,然后把这些步骤一......
  • C++ 位运算Bitwise operations详解 ----- 重要的解题技巧
    什么是位运算:利用位运算符号进行二进制位计算的操作即为位运算维基百科:......
  • CSP 202209-1 如此编码 C++
     链接1#include<iostream>2#include<vector>34intmain(){5intx{},m{};6std::cin>>x>>m;7std::vector<std::vector<int>>nc......
  • 深度讲解React Props
    一、props的介绍当React遇到的元素是用户自定义的组件,它会将JSX属性作为单个对象传递给该组件,这个对象称之为“props”。函数声明的组件,会接受一个props形参,获取属性传递......
  • 深度理解Redux原理并实现一个redux
    Redux的作用是什么Redux的作用在于实现状态传递、状态管理。在这里你可能会说了,如果是状态传递,那我props的传递不也是可以达到这样的效果吗?context上下文方案不也是可以达......
  • 【算法训练营day16】LeetCode104. 二叉树的最大深度 LeetCode559. n叉树的最大深度 Le
    LeetCode104.二叉树的最大深度题目链接:104.二叉树的最大深度初次尝试直接看题解学习思路。看完代码随想录后的想法本题使用的是二叉树的后序遍历,实际上是在求根节点......
  • C++ explicit关键字详解
    C++编码时,可以通过构造函数将相应的数据类型转换成为C++类的对象,从某种程度来说给编码带来了方便,但并不是每次都正确,为了避免这种情况,C++提供了explicit关键字,相对于implic......
  • JUC学习笔记——共享模型之内存
    JUC学习笔记——共享模型之内存在本系列内容中我们会对JUC做一个系统的学习,本片将会介绍JUC的内存部分我们会分为以下几部分进行介绍:Java内存模型可见性模式之两阶段......
  • 在Windows平台使用Visual C++ 2022编译QT6源码
    在Windows平台使用VisualC++2022编译QT6源码目录在Windows平台使用VisualC++2022编译QT6源码0.引言1.准备工作2.配置3.编译和安装0.引言如果您想......
  • C++PrimerPlus中文第六版第6章编程练习答案
    1、#include<iostream>usingnamespacestd;intmain(){charch;cout<<"Entertext:\n";while(cin.get(ch)&&ch!='@'){if(isa......