DMS 模型边缘部署优化:TensorRT 量化与 ONNX 部署实践

前言

DMS 模型需要在车载 ECU 上实时运行(≥30 fps),典型约束:

  • 算力有限:嵌入式 NPU(4-10 TOPS)
  • 功耗限制:< 5W
  • 内存有限:< 2GB
  • 延迟要求:< 100ms

解决方案: TensorRT INT8 量化 + ONNX Runtime 部署。


一、量化技术概述

1.1 量化类型

类型 说明 精度损失 加速比
FP32 原始精度 1x
FP16 半精度 极小 2x
INT8 8 位整数 4x
INT4 4 位整数 中等 8x

1.2 量化方法

方法 说明 适用场景
PTQ(训练后量化) 无需重训练 快速部署
QAT(量化感知训练) 训练时模拟量化 高精度需求
混合精度 部分层 INT8,部分 FP16 平衡精度与速度

1.3 TensorRT 量化流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
量化流程:

PyTorch 模型

├─ 导出 ONNX

├─ TensorRT 构建
│ ├─ 校准数据集
│ ├─ INT8 校准
│ └─ 引擎优化

└─ TensorRT 引擎

└─ 推理运行

二、DMS 模型量化实践

2.1 模型结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
典型 DMS 模型结构:

┌─────────────────────────────────────────────┐
│ 输入:面部图像 (224×224×3) │
├─────────────────────────────────────────────┤
│ 骨干网络 │
│ ├─ MobileNetV3 / EfficientNet-Lite │
│ └─ 输出:特征向量 (1×1×1280) │
├─────────────────────────────────────────────┤
│ 检测头 │
│ ├─ 眼睑状态分类 (睁眼/闭眼) │
│ ├─ 视线方向回归 (方位角/仰角) │
│ ├─ 头部姿态回归 (yaw/pitch/roll) │
│ └─ 状态分类 (正常/分心/疲劳) │
└─────────────────────────────────────────────┘

2.2 ONNX 导出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import torch
import torch.onnx
from models import DMSModel

# 加载模型
model = DMSModel()
model.load_state_dict(torch.load("dms_model.pth"))
model.eval()

# 导出 ONNX
dummy_input = torch.randn(1, 3, 224, 224)
torch.onnx.export(
model,
dummy_input,
"dms_model.onnx",
opset_version=11,
input_names=["input"],
output_names=["eye_state", "gaze", "head_pose", "status"],
dynamic_axes={
"input": {0: "batch_size"},
"eye_state": {0: "batch_size"},
"gaze": {0: "batch_size"},
"head_pose": {0: "batch_size"},
"status": {0: "batch_size"}
}
)

2.3 TensorRT INT8 量化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import tensorrt as trt
import pycuda.driver as cuda
import numpy as np

# INT8 校准器
class DMSInt8Calibrator(trt.IInt8EntropyCalibrator2):
def __init__(self, calibration_data, batch_size=1):
super().__init__()
self.batch_size = batch_size
self.data = calibration_data
self.current_index = 0

def get_batch_size(self):
return self.batch_size

def get_batch(self, names):
if self.current_index >= len(self.data):
return None

batch = self.data[self.current_index:self.current_index + self.batch_size]
self.current_index += self.batch_size

return np.ascontiguousarray(batch)

def read_calibration_cache(self):
return None

def write_calibration_cache(self, cache):
with open("calibration.cache", "wb") as f:
f.write(cache)

# 构建 TensorRT 引擎
def build_engine(onnx_path, calibration_data):
logger = trt.Logger(trt.Logger.WARNING)
builder = trt.Builder(logger)
network = builder.create_network(1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH))
parser = trt.OnnxParser(network, logger)

# 解析 ONNX
with open(onnx_path, "rb") as f:
parser.parse(f.read())

# 配置 INT8
config = builder.create_builder_config()
config.set_flag(trt.BuilderFlag.INT8)
config.int8_calibrator = DMSInt8Calibrator(calibration_data)

# 构建引擎
engine = builder.build_engine(network, config)
return engine

2.4 ONNX Runtime 部署

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import onnxruntime as ort
import numpy as np

class DMSInference:
def __init__(self, onnx_path, provider="TensorrtExecutionProvider"):
# 创建 ONNX Runtime 会话
self.session = ort.InferenceSession(
onnx_path,
providers=[provider, "CUDAExecutionProvider", "CPUExecutionProvider"]
)

# 获取输入输出信息
self.input_name = self.session.get_inputs()[0].name
self.output_names = [o.name for o in self.session.get_outputs()]

def infer(self, image):
"""推理"""
# 预处理
input_tensor = self.preprocess(image)

# 推理
outputs = self.session.run(
self.output_names,
{self.input_name: input_tensor}
)

# 后处理
results = self.postprocess(outputs)
return results

def preprocess(self, image):
"""预处理"""
# 归一化、缩放等
image = image.astype(np.float32) / 255.0
image = (image - [0.485, 0.456, 0.406]) / [0.229, 0.224, 0.225]
image = np.transpose(image, (2, 0, 1)) # HWC -> CHW
image = np.expand_dims(image, 0) # CHW -> BCHW
return image

def postprocess(self, outputs):
"""后处理"""
eye_state = outputs[0] # 眼睑状态
gaze = outputs[1] # 视线方向
head_pose = outputs[2] # 头部姿态
status = outputs[3] # 状态分类

return {
"eye_state": eye_state,
"gaze": gaze,
"head_pose": head_pose,
"status": status
}

三、性能对比

3.1 测试环境

项目 规格
平台 NVIDIA Jetson Xavier NX
CPU 6-core ARM
GPU 384-core Volta
内存 8GB
NPU 6 TOPS

3.2 延迟对比

精度 延迟 (ms) FPS 模型大小
FP32 45 22 25 MB
FP16 18 55 13 MB
INT8 8 125 7 MB

3.3 精度对比

指标 FP32 FP16 INT8
眼睑状态准确率 97.5% 97.3% 96.8%
视线误差(°) 3.2 3.3 3.5
状态分类准确率 94.2% 94.0% 93.5%

四、部署最佳实践

4.1 量化策略

场景 推荐策略
精度敏感 FP16 或 混合精度
速度优先 INT8 PTQ
最高精度 INT8 QAT

4.2 硬件选择

平台 算力 推荐精度 适用场景
Jetson Nano 0.5 TOPS INT8 后装 DMS
Jetson Xavier NX 6 TOPS INT8/FP16 前装 DMS
Jetson AGX Orin 275 TOPS FP16 高端车型
Qualcomm SA8255 50+ TOPS INT8 主流量产

4.3 优化技巧

技巧 说明
层融合 TensorRT 自动融合
内核调优 针对目标硬件调优
动态批处理 按需调整 batch size
异步推理 CPU-GPU 异步

五、与 IMS 开发的关联

5.1 部署优先级

阶段 任务 优先级
1 模型导出 ONNX P0
2 TensorRT INT8 量化 P0
3 延迟优化(< 30ms) P0
4 内存优化(< 1GB) P1
5 多模型调度 P2

5.2 集成流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
IMS 集成流程:

1. 模型训练
└─ PyTorch 训练

2. ONNX 导出
└─ 标准化格式

3. TensorRT 量化
├─ 校准数据集
└─ INT8 引擎构建

4. ONNX Runtime 集成
└─ C++ API 封装

5. 性能测试
├─ 延迟测试
└─ 精度验证

六、总结

关键要点

要点 说明
INT8 量化 4x 加速,7x 模型压缩
ONNX 标准 跨平台部署
TensorRT 优化 NVIDIA 平台首选
精度权衡 < 1% 精度损失,> 4x 加速

开发启示

启示 说明
边缘优先 部署约束决定模型设计
量化早做 训练时考虑量化友好
校准关键 好的校准数据决定精度
持续优化 硬件升级带来新机会

参考来源:

发布日期: 2026-04-13
标签: #DMS #TensorRT #ONNX #量化 #边缘部署 #模型优化