目录

AI模型小解

从常用的模型部署开始讲解,并简单描述推理引擎的作用,以及训练工程师优化的作用。

当前模型训练,常用 tensorflow、pytorch 等框架,其模型格式皆有差异。若希望将其部署到 C++ 运行环境下,则需要进行一系列转换。

使用 LibTorch(PyTorch 的 C++ 发行版):

步骤 内容
1. 导出模型 将模型转为 TorchScript 格式(.pt 文件):
2. 安装依赖 下载并解压 LibTorch(选择与你的系统匹配的版本)
3. 编译项目 使用 CMake 配置:
4. 加载与推理 使用 torch::jit::load() 加载模型,输入为 torch::jit::IValue,输出为 at::Tensor

使用 TensorFlow C++ APITensorFlow Lite

步骤 内容
1. 导出模型 .pb(frozen graph)或 SavedModel 格式模型准备好
2. 编译 TensorFlow C++ 使用 Bazel 编译 TensorFlow 源码,生成 .so 和头文件
3. 加载模型 使用 tensorflow::Session 加载模型,输入为 Tensor,输出为 std::vector<Tensor>
4. 编译项目 配置 CMake 或 Bazel,链接 libtensorflow_cc.so 和头文件
步骤 内容
1. 转换模型 使用 tflite_convert.pb 转为 .tflite
2. 安装依赖 下载 TensorFlow Lite C++
3. 加载模型 使用 tflite::FlatBufferModel::BuildFromFile() 加载 .tflite 模型
4. 推理 使用 tflite::Interpreter 执行推理

如果你希望跨框架部署(如 PyTorch → ONNX → C++):

步骤 内容
1. 导出 ONNX 使用 torch.onnx.export()tf2onnx 转换为 .onnx
2. 使用 ONNX Runtime C++ 下载 ONNX Runtime C++ SDK
3. 加载模型 使用 Ort::Session 加载 ONNX 模型并推理

框架 C++ 工具链 依赖库
PyTorch LibTorch libtorch + CMake
TensorFlow TensorFlow C++ API libtensorflow_cc.so + 头文件
TensorFlow Lite TFLite C++ libtensorflowlite.so + 头文件
ONNX ONNX Runtime libonnxruntime.so + 头文件

如果希望 “一次训练,多端部署”,ONNX 是目前最成熟、最省心的方案之一。

  • 训练阶段:用最熟悉的框架(PyTorch/TensorFlow)。
  • 转换阶段:导出 .onnx 并用工具链优化。
  • 部署阶段:根据目标硬件选择 ONNX Runtime、TensorRT、OpenVINO 等推理引擎即可。

一个模型文件的应用,到部署阶段大致能分为上述三个步骤,下面针对第三个阶段**“推理引擎”**的功能,做出简单的描述。用 .onnx 模型进行举例:

首先,ONNX 本身只是一份标准化文件格式.onnx),它把模型结构、权重和算子定义保存下来,但并不会替你自动做运行时调度。 真正决定“用 CPU 还是 GPU、如何并行、如何融合算子”的是选用的推理引擎(ONNX Runtime、TensorRT、OpenVINO、MNN、NCNN …) 。

负责把 PyTorch / TF / … 训练产出,此时只有“原始权重+结构”,此时只有一张静态图,没有任何离线优化,没有任何调度逻辑

将上述训练模型导出成 .onnx。此时所谓工具链优化,相当于**“离线图改写”** ,改的是上述步骤的静态图本身。假设跳过这一步,推理引擎只能拿到一张“原始图”,很多加速机会就错过了。

那么,工具链优化具体干了什么?可以用一张表格简单列举以下:


动作 作用 举例 不做会怎样
常量折叠 把可提前算出的节点直接算成常量 Conv+ConstAdd → 合并权重 每次推理都重复计算
死代码删除 去掉永远不会执行的节点 Dropout 在 eval 模式下被剪枝 多占内存/算力
算子融合 把多个小算子合并成一个大算子 Conv+BN+ReLUConvReLU 需要多次 Kernel 启动
数据布局转换 把 NCHW ↔ NHWC 或插入 transpose 让后端能用 SIMD/TensorCore 引擎只好插昂贵的转置
量化/稀疏化 把 FP32 权重转 INT8 或稀疏格式 权重压缩 4×,算子变 INT8 引擎只能跑 FP32
形状推断与静态化 把动态 shape 转成固定 shape 避免引擎每次重编译 引擎频繁 realloc,延迟抖动
子图提取 把 GPU/NPU 不支持的节点拆出来 CPU 子图 + GPU 子图 引擎 fallback 到 CPU,整体降速

典型工具:onnxoptimizeronnxsimtf2onnx --fold_consttorch.onnx.export(do_constant_folding=True)

在使用 LibTorch、TensorFlow家族时,也有相关的工具链优化步骤,只是专用名词不同,但本质一样。

如,转换/固化torch.jit.trace / torch.jit.script 得到 .pt(TorchScript 文件) ⬅︎ 这就是 PyTorch 生态里的“工具链优化”阶段

又如,TensorFlow C++ 工具链优化发生在 “冻结图 + optimize_for_inference” 这一步,产出 .pb。TensorFlow Lite:工具链优化发生在 “tflite_convert” 这一步,产出 .tflite

部署时,需要把 .onnx 喂给某个推理引擎。引擎会:

  1. 解析 ONNX 图
  2. 根据当前机器/板端上的硬件能力(CPU 核心数量、有没有 GPU/NPU、驱动版本等)静态或动态地决定:
    • 哪些节点跑在 CPU
    • 哪些节点跑在 GPU/NPU
    • 哪些节点可以并行
    • 哪些算子可以融合
  3. 生成最终可执行的“内部图”或二进制

这些调度策略是推理引擎开发者预先写好的,用 C++/Python API 只能做“开关”式配置,例如:

Ort::SessionOptions opts;
opts.AppendExecutionProvider_CUDA(cuda_options);   // 让 ORT 尽量用 GPU

动作 作用 举例
内存池/复用 生命周期不重叠的张量共享同一块显存 Conv1_outConv2_out 复用
Kernel 选择 根据硬件指令集挑最快实现 AVX512 vs AVX2、cuDNN vs cuBLAS
并行调度 把无依赖节点扔给不同线程/GPU Stream MatMul 与 ReLU 并行
动态 shape 编译缓存 同一 shape 第二次直接走缓存 避免重复编译
FP16/INT8 Kernel 即时切换 如果硬件支持即用低精度路径 TensorRT 的 tactic selection

所谓推理引擎优化是**“运行时策略”**,发生在加载 .onnx 后,不改动图结构,只做内存布局、Kernel 选择、并行调度等。

虽然推理引擎和工具链有不少优化项加速模型,但训练工程师在“把模型交给推理引擎之前”能做很多决定性能天花板的优化。可以把它们分成 结构级、算子级、数值级、数据级 四大类。

动作 目的 典型工具/做法 收益 注意
算子融合(Conv+BN+ReLU) 减少访存、启动开销 torch.nn.Conv2d(..., bias=False) + torch.nn.BatchNorm2d + torch.nn.ReLUtorch.nn.ConvReLU2d 1.2-3× 提速 需确保推理引擎支持 fused kernel
替换等价结构 减少参数量或计算量 MobileNet、EfficientNet、GhostNet、Depthwise Separable Conv、Group Conv 2-10× 压缩 可能牺牲精度,需重训
动态图→静态图 让引擎做更激进的图优化 torch.jit.trace / torch.jit.script 10-30% trace 对控制流不友好
剪枝(结构剪枝) 直接砍掉通道/层 torch.nn.utils.prune / NNI / AMC 2-5× FLOPs 降 需重训恢复精度
知识蒸馏 小模型学大模型 Teacher-Student 框架 精度↑+速度↑ 训练时间↑

动作 目的 做法 收益 注意
对齐输入/输出维度 避免引擎插入昂贵的 reshape/transpose 训练时就用 NCHW(或引擎偏好格式) 10-50% 部分引擎对 NHWC 更友好
避免冷门算子 减少 fallback 到 CPU 用标准 ONNX opset 11/12/13 内的算子 避免崩溃 自定义算子需写 plugin
常量折叠 减少运行时计算 训练后 freeze BN 参数 轻微 需确保推理时也 freeze
算子参数对齐 触发引擎融合 Conv groups/sizes 与硬件指令集匹配 2-4× 需查阅硬件白皮书

动作 目的 做法 收益 注意
训练时量化感知 (QAT) 让 INT8 精度损失最小 torch.quantization.prepare_qat → 重训 2-4× 提速 + 4× 模型压缩 必须与推理量化位宽一致
混合精度训练 权重用 FP16 torch.cuda.amp 1.5-2× 训练提速 推理时需确认引擎支持 FP16
稀疏化训练 激活/权重稀疏 torch.sparse 或 RigL 算法 1.5-2× 提速 需要硬件支持稀疏指令

动作 目的 做法 收益 注意
输入分辨率/帧率缩放 减少计算量 训练时就用 224×224 而非 512×512 平方级收益 精度可能降
动态形状优化 避免引擎重编译 训练时固定 batch/size 避免延迟抖动 对检测模型尤重要
预处理融合到模型 减少额外 memcpy 把 mean/std/resize 写成 ONNX 节点 5-10% 需引擎支持

  1. 跑通转换torch.onnx.exporttf2onnx 无报错,ONNX Checker 通过。
  2. 性能基准:在目标引擎 + 目标硬件上跑 latency(ms)memory(MB)FPS
  3. 量化验证:INT8 精度下降 < 1%(业务可接受)。
  4. 算子白名单:所有节点都在推理引擎的硬件加速列表里,无 CPU fallback。
  5. 结构化剪枝:砍掉 ≥30% 通道,重训精度恢复。

训练工程师的优化是“给引擎一张好牌”:结构精简、算子标准、数值友好、数据对齐。如果牌面太差,再强的引擎也无法挽救。