深度学习推理基础:推理与训练的区别、推理流程(前向传播)、常见推理框架
各位同学好,我是你们的老朋友。今天咱们聊聊深度学习推理的基础。说实话,很多刚入行的朋友容易把推理和训练混为一谈。我当年刚接触嵌入式部署时,也踩过这个坑。嗯,咱们今天就把这事儿彻底捋清楚。
推理 vs 训练:本质区别在哪?
训练,说白了就是让模型学会「知识」的过程。它需要海量数据、强大的GPU、来回迭代。你想想看,训练一个ResNet-50在ImageNet上,动辄几十个小时,梯度来回传,权重不断更新。
推理呢?推理是模型学会之后,拿着学到的「知识」去干活。它只需要一次前向传播,不需要反向传播,不需要更新权重。说白了,就是「用」模型,而不是「教」模型。
我给大家列个表,一目了然:
| 对比维度 | 训练 | 推理 |
|---|---|---|
| 计算方向 | 前向 + 反向 | 仅前向 |
| 数据需求 | 大量标注数据 | 单张/少量输入 |
| 精度要求 | FP32为主 | 可降为FP16/INT8 |
| 延迟要求 | 相对宽松 | 毫秒级实时 |
| 硬件需求 | 高性能GPU集群 | 嵌入式GPU/边缘设备 |
核心要点:训练是「学习」,推理是「应用」。在嵌入式GPU上,我们只关心推理,因为设备资源有限,不可能跑训练。
推理流程:前向传播到底在做什么?
前向传播,这个名字听起来高大上。其实说白了,就是数据从输入层流到输出层的过程。我习惯把它想象成一条流水线:
- 输入预处理:图像缩放、归一化、通道转换。嗯,这一步经常被忽略,但我在项目中遇到过,预处理不对,后面全白搭。
- 逐层计算:卷积、池化、激活、全连接。每一层都在做矩阵运算。
- 输出后处理:Softmax、NMS(非极大值抑制)、坐标解码。
举个例子,一张224x224的图片输入ResNet-50,经过大约50层卷积,最后输出1000个类别的概率。整个过程就是矩阵乘法和非线性激活的堆叠。
我的经验:在嵌入式GPU上,前向传播的瓶颈往往不在计算,而在内存带宽。数据搬运比计算还耗时。我曾经在Jetson Nano上调试一个模型,发现80%的时间花在了数据拷贝上。后来用了CUDA Stream和Pinned Memory,才把延迟降下来。
常见推理框架:选哪个?怎么用?
市面上推理框架不少,但真正在嵌入式GPU上常用的,就三个:TensorRT、ONNX Runtime、OpenCV DNN。我一个个说。
1. TensorRT:NVIDIA的亲儿子
TensorRT是NVIDIA推出的推理优化引擎。它支持FP16、INT8量化,还能做层融合、内存复用。说白了,就是把你训练好的模型,变成能在GPU上跑得飞快的「特化版本」。
我个人的习惯是:只要用NVIDIA的GPU,首选TensorRT。它能把模型推理速度提升2-5倍,甚至更多。
// TensorRT推理示例(伪代码)
// 1. 创建Builder
IBuilder* builder = createInferBuilder(gLogger);
// 2. 构建网络
INetworkDefinition* network = builder->createNetwork();
// 3. 解析ONNX模型
parser->parse(modelFile, network);
// 4. 构建引擎
ICudaEngine* engine = builder->buildCudaEngine(*network);
// 5. 执行推理
engine->execute(batchSize, buffers);
避坑指南:我曾经在Jetson Xavier上部署一个YOLOv5模型,直接用TensorRT解析ONNX,结果报错说某些算子不支持。后来发现是ONNX导出时用了动态形状。解决办法是固定输入尺寸,或者在导出时设置opset_version为11以上。嗯,这个坑我踩了整整两天。
2. ONNX Runtime:跨平台的好选择
ONNX Runtime是微软开源的推理引擎。它支持多种硬件后端,包括CPU、GPU、甚至NPU。如果你需要在不同平台间切换,ONNX Runtime是个不错的选择。
它的优势在于:模型格式统一(ONNX),一次导出,到处运行。但说实话,在嵌入式GPU上,它的优化深度不如TensorRT。
// ONNX Runtime推理示例(Python)
import onnxruntime as ort
# 创建推理会话
session = ort.InferenceSession("model.onnx")
# 获取输入输出名称
input_name = session.get_inputs()[0].name
output_name = session.get_outputs()[0].name
# 执行推理
result = session.run([output_name], {input_name: input_data})
我的建议:如果你只是做原型验证,或者模型需要跨平台部署,用ONNX Runtime。但如果你追求极致性能,尤其是在Jetson系列上,还是老老实实用TensorRT吧。
3. OpenCV DNN:轻量级选手
OpenCV DNN是OpenCV库自带的深度学习模块。它不需要额外安装CUDA或cuDNN,直接就能跑。但它的优化能力有限,不支持量化,也不支持层融合。
我一般在什么场景用呢?快速验证模型效果,或者做一些简单的图像处理任务。比如在树莓派上跑一个人脸检测,OpenCV DNN就够用了。
// OpenCV DNN推理示例(C++)
cv::dnn::Net net = cv::dnn::readNet("model.onnx");
cv::Mat blob = cv::dnn::blobFromImage(image, 1.0, cv::Size(224, 224));
net.setInput(blob);
cv::Mat output = net.forward();
总结一下:
- 追求极致性能 → TensorRT
- 跨平台兼容 → ONNX Runtime
- 快速验证/轻量任务 → OpenCV DNN
我的实战建议
说了这么多,给大家一个实际的操作路径:
- 先用PyTorch/TensorFlow训练模型,导出为ONNX格式。
- 在PC上用ONNX Runtime验证推理结果是否正确。
- 针对嵌入式GPU(如Jetson),用TensorRT做优化和部署。
- 如果资源实在紧张,考虑用OpenCV DNN做降级方案。
嗯,这套流程我用了好几年,基本没出过大问题。你想想看,每一步都有明确的目标,不会走弯路。
最后提醒一句:不要盲目追求框架的「新」和「快」。我在项目中见过有人为了用TensorRT的最新特性,把模型改得面目全非,结果精度掉了5个点。稳定性和精度,永远比那点速度提升更重要。
好了,今天的内容就到这里。下一章咱们聊聊模型量化的具体实现,包括INT8量化的原理和实战技巧。到时候我会分享一个我在Jetson Nano上做量化时遇到的「诡异」问题,保证让你大开眼界。