3、ONNX Runtime安装与配置:在ARM Linux上编译ONNX Runtime、Python与C++ API选择
好,咱们进入第三章。这一章讲的是ONNX Runtime的安装与配置,说白了就是怎么在ARM Linux上把这个东西跑起来。我见过不少同学,模型转换好了,结果卡在安装这一步,折腾一整天。嗯,咱们今天就把这事彻底说清楚。
3.1 为什么要在ARM上编译?
你可能会问:直接用pip安装不香吗?
香,但仅限于x86平台。ARM平台上,尤其是树莓派、瑞芯微、全志这些嵌入式板子,官方预编译包要么没有,要么性能拉胯。我去年在RK3588上做过一个项目,直接用pip装了个ONNX Runtime,推理速度慢得离谱。后来一查,发现它用的是CPU通用指令集,根本没针对ARM的NEON指令集做优化。
所以,自己编译是绕不开的路。虽然过程有点繁琐,但编译一次,后面就爽了。
3.2 编译前的准备工作
先看看你的板子环境。我个人习惯用Ubuntu 20.04或22.04的ARM版本,Debian也行。需要确认以下几点:
- CPU架构:aarch64还是armv7l?用
uname -m看一眼 - 内存:至少2GB,推荐4GB以上。我曾在1GB内存的板子上编译,swap分区设了4GB,结果编译了整整6个小时
- 磁盘空间:至少留出5GB
依赖库方面,先把这些装上:
sudo apt update
sudo apt install -y build-essential cmake git python3-dev
sudo apt install -y libprotobuf-dev protobuf-compiler
sudo apt install -y libssl-dev libcurl4-openssl-dev
sudo dphys-swapfile swapoff && sudo dphys-swapfile setup && sudo dphys-swapfile swapon。不然编译到一半内存爆了,那叫一个崩溃。
3.3 正式编译ONNX Runtime
从GitHub拉取源码,我建议用稳定版本,别追最新。目前1.16.x系列比较稳:
git clone --recursive -b v1.16.3 https://github.com/microsoft/onnxruntime.git
cd onnxruntime
然后就是编译配置。这里有个关键点:你要想清楚用Python还是C++ API。我个人建议,如果只是做快速验证,用Python;如果是产品级部署,必须用C++。
编译Python版本:
./build.sh --config Release --build_shared_lib --parallel
pip install build/Linux/Release/dist/*.whl
编译C++版本:
./build.sh --config Release --build_shared_lib --parallel --cmake_extra_defines CMAKE_INSTALL_PREFIX=/usr/local/onnxruntime
cd build/Linux/Release
make install
这里有个坑,我曾经踩过:--parallel 参数默认用满所有CPU核心。如果你的板子散热不行,比如树莓派4B,建议手动指定核心数,比如 --parallel 2。不然温度飙到85度,直接降频,编译速度反而更慢。
3.4 Python API vs C++ API:怎么选?
这个问题我经常被问到。直接上对比表:
| 对比项 | Python API | C++ API |
|---|---|---|
| 开发效率 | 高,几行代码搞定 | 低,需要写不少胶水代码 |
| 推理性能 | 中等,有Python解释器开销 | 高,几乎没有额外开销 |
| 内存占用 | 较高,Python运行时占不少 | 低,适合资源受限设备 |
| 调试难度 | 容易,print大法好 | 较难,需要gdb |
| 适用场景 | 原型验证、快速迭代 | 产品部署、性能敏感场景 |
我的建议是:前期用Python验证模型正确性,后期用C++做最终部署。我在一个安防项目中就是这么干的——先用Python调通了模型,然后花了两天时间把推理代码改成C++,推理速度提升了将近30%。
3.5 Python API快速上手
编译完成后,验证一下是否安装成功:
python3 -c "import onnxruntime; print(onnxruntime.__version__)"
如果能打印出版本号,恭喜你,成了。
一个简单的推理示例:
import onnxruntime as ort
import numpy as np
# 创建推理会话
sess = ort.InferenceSession("model.onnx")
# 获取输入输出信息
input_name = sess.get_inputs()[0].name
output_name = sess.get_outputs()[0].name
# 构造输入数据(假设是1x3x224x224的图像)
input_data = np.random.randn(1, 3, 224, 224).astype(np.float32)
# 执行推理
output = sess.run([output_name], {input_name: input_data})
print(output[0].shape)
你看,就这么几行。Python API的简洁性确实没得说。
3.6 C++ API实战
C++这边稍微复杂点。首先,编译时需要链接ONNX Runtime的动态库:
g++ -o inference inference.cpp \
-I/usr/local/onnxruntime/include \
-L/usr/local/onnxruntime/lib \
-lonnxruntime \
-Wl,-rpath,/usr/local/onnxruntime/lib
代码示例:
#include <onnxruntime/core/session/onnxruntime_cxx_api.h>
#include <iostream>
#include <vector>
int main() {
// 初始化环境
Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "test");
Ort::SessionOptions session_options;
session_options.SetIntraOpNumThreads(1);
// 加载模型
Ort::Session session(env, "model.onnx", session_options);
// 获取输入输出信息
Ort::AllocatorWithDefaultOptions allocator;
auto input_name = session.GetInputNameAllocated(0, allocator).get();
auto output_name = session.GetOutputNameAllocated(0, allocator).get();
// 构造输入
std::vector<int64_t> input_shape = {1, 3, 224, 224};
std::vector<float> input_data(1*3*224*224, 1.0f);
Ort::Value input_tensor = Ort::Value::CreateTensor<float>(
Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault),
input_data.data(), input_data.size(), input_shape.data(), input_shape.size()
);
// 推理
auto output_tensors = session.Run(Ort::RunOptions{nullptr},
&input_name, &input_tensor, 1,
&output_name, 1
);
// 获取输出
float* output_data = output_tensors[0].GetTensorMutableData<float>();
std::cout << "Output shape: " << output_tensors[0].GetTensorTypeAndShapeInfo().GetShape()[0] << std::endl;
return 0;
}
说实话,C++代码确实啰嗦。但性能优势摆在那里,尤其是嵌入式设备上,每一毫秒都珍贵。
3.7 避坑指南
最后,分享几个我踩过的坑:
- 编译时内存不足:我曾经在1GB内存的板子上编译,直接OOM。解决方案是增加swap分区,或者用
--parallel 1单线程编译 - Python版本不匹配:ONNX Runtime的Python绑定只支持特定Python版本。编译前用
python3 --version确认一下,我建议用Python 3.8或3.10 - 动态库找不到:编译完C++程序后,运行时提示找不到libonnxruntime.so。记得设置
LD_LIBRARY_PATH或者用-Wl,-rpath指定库路径 - 模型加载失败:如果模型是用更高版本的ONNX导出的,而你的ONNX Runtime版本较低,会报错。保持版本一致很重要
嗯,这一章的内容就这些。安装配置是基础,但基础打牢了,后面优化才能事半功倍。下一章咱们聊聊模型量化和精度校准,那才是真正拉开性能差距的地方。