Skip to content

MNN 深度学习推理引擎使用教程

1. MNN 简介

MNN(Mobile Neural Network)是阿里巴巴开源的轻量级深度学习推理引擎,专为移动端和嵌入式设备优化。

主要特点:

  • 轻量高效:体积小、速度快、内存占用低
  • 跨平台:支持 iOS、Android、Linux、Windows、macOS
  • 多后端:CPU、GPU(OpenGL/Vulkan/Metal)、NPU
  • 易用性:提供 C++、Java、Python 等多语言接口
  • 优化器:内置模型压缩和优化工具

官方资源:

2. 环境准备

2.1 系统要求

  • Android:NDK r21 或更高版本
  • iOS:Xcode 11 或更高版本
  • Linux/Windows/macOS:CMake 3.10+、GCC 4.9+ 或 Clang

2.2 获取源码

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

3. Android 平台编译

3.1 配置 NDK 环境

bash
export ANDROID_NDK="/Users/$USER/Library/Android/sdk/ndk/28.0.13004108"
# 或者在 Linux/Windows 上
export ANDROID_NDK="/path/to/android-ndk"

3.2 编译 ARMv8 动态库

参考文档:https://mnn-docs.readthedocs.io/en/latest/compile/engine.html

bash
cd MNN/project/android

# 编译 64 位 ARM 架构,启用多种后端
../build_64.sh \
  -DMNN_USE_LOGCAT=ON \     # 启用 Android 日志
  -DMNN_NNAPI=ON \          # 启用 NNAPI 支持
  -DMNN_JNI=ON \            # 启用 JNI 接口
  -DMNN_VULKAN=ON \         # 启用 Vulkan GPU 加速
  -DMNN_OPENGL=ON           # 启用 OpenGL GPU 加速

3.3 编译产物

编译完成后,会在 build_64/ 目录生成以下动态库:

text
./libMNN_Express.so      # MNN Express 高级 API
./libMNN.so              # MNN 核心库
./source/jni/libmnncore.so   # JNI 桥接库
./libMNN_GL.so           # OpenGL 后端
./libMNN_Vulkan.so       # Vulkan 后端

3.4 编译 ARMv7 库

如需支持 32 位设备:

bash
../build_32.sh \
  -DMNN_USE_LOGCAT=ON \
  -DMNN_OPENGL=ON

4. Linux/Windows/macOS 编译

4.1 使用 CMake 编译

bash
mkdir build && cd build

# 基础编译
cmake .. \
  -DMNN_BUILD_CONVERTER=ON \    # 构建模型转换工具
  -DMNN_BUILD_TOOLS=ON          # 构建性能测试工具

make -j$(nproc)

4.2 启用 GPU 支持

OpenCL(适用于 AMD/NVIDIA GPU):

bash
cmake .. \
  -DMNN_OPENCL=ON \
  -DMNN_SEP_BUILD=OFF
make -j$(nproc)

CUDA(仅 NVIDIA GPU):

bash
cmake .. \
  -DMNN_CUDA=ON \
  -DMNN_CUDA_PROFILE=ON
make -j$(nproc)

4.3 编译 Python 接口

bash
cd pymnn/pip_package
python3 setup.py bdist_wheel
pip install dist/*.whl

5. 模型转换

5.1 支持的框架

MNN 支持从以下框架转换模型:

  • TensorFlow / TensorFlow Lite
  • PyTorch (ONNX)
  • Caffe
  • ONNX

5.2 转换 ONNX 模型

bash
# 使用 MNNConvert 工具
./MNNConvert \
  -f ONNX \
  --modelFile model.onnx \
  --MNNModel model.mnn \
  --bizCode biz

5.3 转换 TensorFlow 模型

bash
./MNNConvert \
  -f TF \
  --modelFile frozen_model.pb \
  --MNNModel model.mnn \
  --bizCode biz

5.4 模型量化

将 FP32 模型量化为 INT8:

bash
./quantized.out \
  model.mnn \
  model_quant.mnn \
  images_folder

6. C++ 推理示例

6.1 基础推理代码

cpp
#include <MNN/Interpreter.hpp>
#include <MNN/Tensor.hpp>
#include <iostream>

int main() {
    // 1. 创建解释器
    std::shared_ptr<MNN::Interpreter> net(
        MNN::Interpreter::createFromFile("model.mnn")
    );
    
    // 2. 创建会话
    MNN::ScheduleConfig config;
    config.type = MNN_FORWARD_CPU;  // 或 MNN_FORWARD_OPENCL, MNN_FORWARD_VULKAN
    config.numThread = 4;
    
    MNN::Session* session = net->createSession(config);
    
    // 3. 获取输入输出张量
    MNN::Tensor* input = net->getSessionInput(session, nullptr);
    MNN::Tensor* output = net->getSessionOutput(session, nullptr);
    
    // 4. 准备输入数据
    auto inputTensor = new MNN::Tensor(input, MNN::Tensor::CAFFE);
    float* inputData = inputTensor->host<float>();
    // 填充输入数据
    for (int i = 0; i < inputTensor->elementSize(); i++) {
        inputData[i] = 1.0f;
    }
    
    // 5. 复制数据到 MNN
    input->copyFromHostTensor(inputTensor);
    
    // 6. 执行推理
    net->runSession(session);
    
    // 7. 获取输出
    auto outputTensor = new MNN::Tensor(output, MNN::Tensor::CAFFE);
    output->copyToHostTensor(outputTensor);
    
    float* outputData = outputTensor->host<float>();
    std::cout << "Output: " << outputData[0] << std::endl;
    
    // 8. 清理
    delete inputTensor;
    delete outputTensor;
    
    return 0;
}

6.2 编译和运行

bash
g++ inference.cpp \
  -o inference \
  -I/path/to/MNN/include \
  -L/path/to/MNN/lib \
  -lMNN \
  -std=c++11

./inference

7. Python 推理示例

7.1 安装 MNN Python

bash
pip install MNN

7.2 推理代码

python
import MNN
import numpy as np

# 1. 创建解释器
interpreter = MNN.Interpreter("model.mnn")

# 2. 创建会话
session = interpreter.createSession({
    'numThread': 4,
    'backend': 'CPU'  # 或 'OPENCL', 'VULKAN'
})

# 3. 获取输入输出
input_tensor = interpreter.getSessionInput(session)
output_tensor = interpreter.getSessionOutput(session)

# 4. 准备输入数据
tmp_input = MNN.Tensor((1, 3, 224, 224), MNN.Halide_Type_Float,
                       np.random.randn(1, 3, 224, 224).astype(np.float32),
                       MNN.Tensor_DimensionType_Caffe)

input_tensor.copyFrom(tmp_input)

# 5. 执行推理
interpreter.runSession(session)

# 6. 获取输出
tmp_output = MNN.Tensor(output_tensor.getShape(),
                        output_tensor.getDataType(),
                        np.zeros(output_tensor.getShape()).astype(np.float32),
                        MNN.Tensor_DimensionType_Caffe)

output_tensor.copyToHostTensor(tmp_output)

# 7. 处理结果
output_data = tmp_output.getNumpyData()
print("Output shape:", output_data.shape)
print("Output:", output_data)

8. Android 集成

8.1 添加依赖

build.gradle 中:

gradle
android {
    defaultConfig {
        ndk {
            abiFilters 'armeabi-v7a', 'arm64-v8a'
        }
    }
}

dependencies {
    implementation 'com.alibaba.android:mnn:2.8.0'
}

8.2 Java 推理代码

java
import com.taobao.android.mnn.MNNNetInstance;
import com.taobao.android.mnn.MNNImageProcess;

public class MNNInference {
    private MNNNetInstance mNetInstance;
    private MNNNetInstance.Session mSession;
    
    public void loadModel(String modelPath) {
        // 加载模型
        mNetInstance = MNNNetInstance.createFromFile(modelPath);
        
        // 创建会话
        MNNNetInstance.Config config = new MNNNetInstance.Config();
        config.numThread = 4;
        config.forwardType = MNNForwardType.FORWARD_CPU.type;
        
        mSession = mNetInstance.createSession(config);
    }
    
    public float[] inference(float[] inputData) {
        // 获取输入输出张量
        MNNNetInstance.Session.Tensor inputTensor = 
            mSession.getInput(null);
        MNNNetInstance.Session.Tensor outputTensor = 
            mSession.getOutput(null);
        
        // 设置输入
        inputTensor.reshape(new int[]{1, 3, 224, 224});
        float[] input = inputTensor.getFloatData();
        System.arraycopy(inputData, 0, input, 0, inputData.length);
        
        // 执行推理
        mSession.run();
        
        // 获取输出
        float[] output = outputTensor.getFloatData();
        return output;
    }
}

9. 性能优化

9.1 选择合适的后端

cpp
// CPU(通用,兼容性好)
config.type = MNN_FORWARD_CPU;

// GPU - OpenCL(适用于大多数 GPU)
config.type = MNN_FORWARD_OPENCL;

// GPU - Vulkan(更现代,性能更好)
config.type = MNN_FORWARD_VULKAN;

// GPU - Metal(iOS/macOS)
config.type = MNN_FORWARD_METAL;

9.2 使用模型量化

INT8 量化可以减少模型大小 75%,加速推理 2-4 倍:

bash
./quantized.out original.mnn quantized.mnn calibration_images/

9.3 使用 FP16

在支持的设备上使用 FP16 可以提升性能:

cpp
config.type = MNN_FORWARD_OPENCL;
config.precision = MNN_LOW;  // 使用 FP16

10. 参考资料