← 返回文章列表
模型部署

TensorRT 量化:INT8 精度掉了 3 个点怎么办

2025-04-30 · 约 7 分钟读完

把一个 YOLOv8 工业缺陷检测模型转成 TensorRT INT8 之后,推理速度快了 40%,但 mAP@0.5 从 94.2% 掉到了 91.1%,整整 3 个点。产线那边说精度不够,不能上线。排查了将近三天,这篇文章记录完整的定位过程和解法。

问题背景

部署环境是 Jetson AGX Orin,GPU 是 Ampere 架构,TensorRT 版本 8.6。模型是 YOLOv8m,输入 640×640,FP32 baseline mAP@0.5 = 94.2%。

INT8 量化的原理是把 FP32 的权重和激活值映射到 INT8 范围(-127~127),需要一个校准数据集来统计激活值的分布,确定量化阈值。大多数精度损失问题,根源都在这一步。

第一步:排查校准数据集

最初我用了训练集里随机抽出的 500 张图做校准,想着反正都是同领域的数据。这里有个隐性问题:训练集的分布不等于产线实际分布。

产线上有几类特殊场景:强反光金属件、极暗环境、多目标密集排列——这些在训练集里比例很低,但在实际推理时占了相当大的比例。校准数据没覆盖到这些场景,导致这几类激活值的量化阈值定得不准,精度大幅下跌。

解法:重新构建校准集,从产线采集了 1000 张图,确保覆盖所有典型场景,每类至少 100 张。重新校准后 mAP 回升到 92.4%,但还不够。

第二步:逐层精度分析

校准集优化之后还有将近 2 个点的损失,说明问题不只是校准数据的问题。这时候需要定位是哪些层在量化时损失最大

TensorRT 提供了 polygraphy 工具可以逐层比对 FP32 和 INT8 的输出差异:

polygraphy run model.onnx \
  --trt --fp32 \
  --data-loader-script calib_loader.py \
  --save-results fp32_results.json

polygraphy run model.onnx \
  --trt --int8 \
  --calibration-cache calib.cache \
  --data-loader-script calib_loader.py \
  --save-results int8_results.json

polygraphy diff-results fp32_results.json int8_results.json \
  --per-output-compare

分析结果发现,SPP(空间金字塔池化)模块之后的几个 Conv 层误差显著偏高,均方误差是其他层的 10 倍以上。原因是 SPP 输出的激活值动态范围非常大,INT8 的 256 个量化区间根本装不下。

第三步:混合精度策略

找到敏感层之后,解法就清晰了:让这些层保持 FP16,其余层用 INT8。TensorRT 支持在 builder 层面配置:

import tensorrt as trt

builder = trt.Builder(logger)
config = builder.create_builder_config()

# 开启 INT8 和 FP16
config.set_flag(trt.BuilderFlag.INT8)
config.set_flag(trt.BuilderFlag.FP16)
config.int8_calibrator = calibrator

# 对敏感层单独设置精度
network = builder.create_network(...)
# ... 构建网络后 ...
sensitive_layers = [
    "spp_cv3_conv",
    "spp_cv3_bn",
    "c2f_after_spp_conv1",
]
for i in range(network.num_layers):
    layer = network.get_layer(i)
    if layer.name in sensitive_layers:
        layer.precision = trt.DataType.HALF
        layer.set_output_type(0, trt.DataType.HALF)

注意: 混合精度会增加不同精度层之间的数据转换开销,但对于敏感层集中的情况,这个开销通常远小于精度损失带来的业务代价。

最终结果对比

方案mAP@0.5推理延迟vs FP32 加速比
FP32 基线94.2%28.6 ms
INT8 全量化(随机校准集)91.1%17.1 ms1.67×
INT8 全量化(产线校准集)92.4%17.1 ms1.67×
混合精度(最终方案)93.6%19.8 ms1.44×

最终精度与 FP32 只差 0.6 个点,推理速度提升了 44%。产线验收通过。

小结

INT8 量化精度掉点的排查路径:校准数据集 → 逐层误差分析 → 混合精度。大多数情况下,第一步就能解决大半问题。如果还不够,再用 polygraphy 找敏感层,做混合精度。暴力全 FP16 当然更省事,但如果部署目标是极致低延迟,混合精度是更好的平衡点。


如果你在用 Jetson 或者边缘 GPU 部署检测模型,遇到类似精度问题欢迎来聊。