一、引言对于使用地平线 征程 6 各平台的用户而言QAT 量化感知训练是模型量化部署流程中重要的环节同时也是量化调优的难点之一。在 QAT 训练过程中受模型结构、量化配置、训练超参、硬件特性适配等多因素影响可能出现训练 Loss 不收敛、前向反向过程存在 NAN/INF 异常值、量化精度指标相比校准阶段回退或无法通过 QAT 训练使指标达标等各类异常问题。本文作为前序 征程 6 平台 QAT 精度调优教程的补充总结地平线 征程 6 系列工具链 QAT 训练环节的各类典型异常问题以及定位解决的流程和思路。阅读前请提前学习 征程 6 平台 QAT 精度调优教程征程 6 E/M 工具链 QAT 精度调优https://developer\。horizon\.auto/blog/13132征程 6 H/P 工具链 QAT 精度调优https://developer\。horizon\.auto/blog/13157二、QAT 训练问题定位流程简述和 qat 训练相同的量化配置检查 calib 阶段精度情况如 calib 精度较差则回头通过 debug 工具定位 calib 阶段量化问题在 1 的基础上如果 calib 精度没问题则取消 qat 链路的 prepare其他配置不变的情况下对模型做 fine-tune或者直接使用 qat 链路的参数配置对浮点模型做 fine-tune如指标明显下降则浮点大概率未收敛优化浮点训练在 2 的基础上设置_FLOAT 取消伪量化来进行 fine-tune模型相当于未引入量化误差应和直接 fine-tune 浮点情况一致如指标明显下降则需要检查是否 qat 链路和流程问题。特殊情况训练数据分布差异大的情况训练早期数据可能出现极大值而 plugin 工具对部分算子有 clip 操作进行数值约束可能造成_FLOAT 训练初期精度差的现象但随着迭代次数增加_FLOAT 精度能对齐浮点在 3 的基础上设置全 int16 进行 qat 训练如果精度指标下降或不达预期需要结合固定校准激活 scale 以及 with_bn 的做法设置不同 lr 进行实验在 4 的基础上回归到混合精度进行 qat 训练如果精度指标下降或不达预期需要增加更高精度量化的算子结合固定校准激活 scale 以及 withbn 的做法设置不同 lr 进行实验。特殊情况如果模型中有训练辅助头或者 loss 计算被量化不符合我们量化适配预期可能造成全 int16 下训练精度达标但是混合精度不达标的现象三、常见问题和原因问题现象问题原因征程 6 尝试开启 POT 量化后QAT 训练指标下降或训不上去pot scale 加大了 qat fine-tune 调优的难度QAT 链路去掉 prepare qat 环节fine-tune 指标就掉浮点权重未收敛往往取浮点训练中间权重易出现1. Calib 指标正常QAT 训练指标下降越训越低2. 全 int16 精度配置QAT 训练指标正常混合精度配置 QAT 训练初始 loss 高越训越差1. 未按照标准建议流程设计调优实验如 QAT 训练未尝试固定 Calib 阶段的激活 scale2. QAT 训练超参学习率未调整到位3. 训练过程中的训练辅助分支或 loss 相关计算被量化4. Calib 阶段的精度调优未做扎实模型有量化敏感算子1. QAT 训练前向或反向过程存在 NAN/INF 异常值2. QAT 训练的 Loss 值极大出现异常值浮点模型或者 Calib 模型中就存在异常值Calib 阶段的精度调优未做扎实异常值未排查出一般异常值可能为数据 mask 操作引入四、解决思路和方案4.1 浮点模型权重未训收敛QAT 本质也是小学习率 fine-tune指标越训越低解决方案用浮点初始学习率的 1/101/100 来微调浮点观察指标是否出现大幅度波动如出现则未收敛需要增加浮点权重的迭代次数。此外还需要对齐浮点训练和 qat 训练各个实验的训练配置例如是否提交集群、机器数和卡数配置、训练集以及 dataloader 配置等4.2 征程 6B/P 的 POT 策略可以提高部署模型一致性表现以及对性能有收益但 pot scale 加大了 qat fine-tune 调优的难度解决方案对于 征程 6B/H/P默认 int16 下开启 POTint8 关闭 POT对于 征程 6E/M默认不开启 POT。因此客户侧选择关闭 POT 再做验证如果模型为全 int 量化建议直接全局关闭 POT# 全局整体关闭 from horizon_plugin_pytorch.quantization.fake_quantize_base import FakeQuantizeBase FakeQuantizeBase._enable_fp16_compute False calib_model prepare(...) # 指定算子关闭prepare后调整 calib_model prepare(...) calib_model.conv1.qconfig Qconfig( activationFakeQuantize.with_kwargs( ..., _enable_fp16_computeFalse ) ) # 指定算子关闭prepare前调整 disable_pot_ops { conv1: Qconfig( activationFakeQuantize.with_kwargs( ..., _enable_fp16_computeFalse ) ) } calib_model prepare( ..., custom_qconfig_mappingdisable_pot_ops )calib 阶段通过敏感度定位到具体 POT 量化敏感算子针对性优化 POT 精度。目前未出现过 calib pot 精度好但是 qat pot 精度差的现象4.3 calib 阶段的量化精度未做扎实主要量化问题仍存在敏感算子误差未解除、INF 值或者极大的 scale 值影响解决方案大部分模型进入到 qat 前 calib 精度应达到的标准calib 精度至少需要达到浮点的 90%建议达到浮点的 95%。结合调优建议 https://developer\。horizon\.auto/blog/13132 以及 https://developer\。horizon\.auto/blog/13157 ; 少数模型 calib 精度没有达到上述标准但通过 qat 也可以训回来需要结合具体模型和 case 来看通常来说如果 qat 训练指标上升但未达标第一时间仍然是优化 calib 精度。calib 阶段做扎实敏感度分析敏感度怎么解读https://developer\。horizon\.auto/blog/13132 以及 https://developer\。horizon\.auto/blog/13157什么是标准的敏感度敏感度值呈现梯度分布主要例如排序前 20% 算子应贡献 80% 的量化误差下面敏感度靠前和最后的值基本一致为常见的错误敏感度敏感算子分布合理敏感算子应集中在模型关键结构例如任务头、多尺度特征融合模块、backbone 高维特征层等不应该均匀地分布在整个网络敏感度值的大小取决于模型输出物理特征或者语义特征以及 badcase 的典型性只要敏感度符合上面特征则可以协助我们定位量化问题不用过多关注敏感度本身的数值量级模型在 badcase 上的整体敏感度应高于单算子的敏感度不同敏感度 metric 下的排序结果应接近敏感度不正常时排查和解决方式在 calib 链路正确的前提下确定 analysis_model 需要比对的量化结构和 baseline_model 是可以对的上的下面提供一种方式在跑敏感度时检查后处理算子是否正确处理对于 topk、nms、sort 和 argmax 这类算子在跑敏感度时应从 forward 中去除对于 sigmoid 算子在跑敏感度时应加在 forward 中检查 badcase 是否数据异常是否为校准数据集中的脏数据对于多类别的回归模型评测链路的后处理中一般会有根据置信度阈值筛选的操作例如 bbox 框通过 score 按照不同的阈值进行过滤这部分过滤的操作也需要加入到 forward 当中进行敏感度分析# Step1 将calib model作为analysis_model并设置评测状态 self.analysis_model analysis_model_convert_pipeline( copy.deepcopy(model) ) self.analysis_model.eval() set_fake_quantize(self.analysis_model, FakeQuantState.VALIDATION) # Step2 复制analysis_model并设置成浮点状态作为baseline_model self.baseline_model copy.deepcopy(self.analysis_model) set_fake_quantize(self.baseline_model, FakeQuantState._FLOAT) # 或者使用FakeQuantState._CALIBRATION_V2异常值或极大 scale 出现场景和解决方式Attn mask、其他数据 mask 等设定的 mask value。优化方式手动设置较小值clamp 算子未指定最大最小值或者最大最小值数值过大或过小norm 算子的拆分算子如 mul/input_mean/var_mean实际对精度的影响需要结合敏感度分析。优化方式手动截断大数值以稀疏分布的截断换取更高的量化分辨率征程 6P 平台尝试设置 fp16/fp32 高精度征程 6M 尝试使用 QAT 训练来进一步优化舍入误差gemm尤其指 matmul层输出。优化方式数据归一化增加 norm 层有物理含义结合物理含义进行放缩结合算子支持情况和目标部署平台切换量化精度例如 int16-fp16、fp16-int16在某些值域内 int16 表示范围更大、fp16-fp32scale 出现 nan 值原因模型中有 tensor 包含 inf/nan 值定位方式日志中会打印建议把 check_nan_scale 打开打开后可具体找到 nan scale 出现的代码位置再逐步定位到 inf/nan 出现的最开始的位置案例参数初始化时未考虑到边界情况通过添加 clamp 即可避免出现 inf/nan# 修改前 depth torch.clamp(proj_pt[..., 2:3], min1e-9) # 修改后 depth torch.clamp(proj_pt[..., 2:3], min1e-2)from horizon_plugin_pytorch.quantization.fake_quantize_base import FakeQuantizeBase FakeQuantizeBase.check_nan_scale forward calib_model prepare(...)def sigmoid_inverse(y): epsilon 1e-6 y torch.clamp(y, minepsilon, max1.0 - epsilon) x torch.log(y / (1.0 - y)) return x结合 calib 阶段调优流程和建议https://developer\。horizon\.auto/blog/13132 以及 https://developer\。horizon\.auto/blog/131574.4 qat 适配过程存在问题一些辅助头分支或者 loss 相关计算被量化解决方案关注 qat train 过程中 miss_key/unexpected_key 的信息被量化的辅助头或者 loss 计算往往会以 miss_key 形式提醒大部分情况被显式量化的操作在对比 calib 和 qat 各自生成的 model_check_result 生成物后可被发现梳理 qat train 阶段计算图 fx_graph 调用关系重点关注模型输出可使用可视化工具from horizon_plugin_pytorch.fx.visualize import visualize visualize(modelqat_calib_model, output_path./horizonn_calib_model_vis.onnx)对比 calib train 和 qat train 过程中生成的 fx_graph重点关注模型任务头输出部分 trace 到的算子和操作4.5 未按照标准建议流程设计调优实验未首先采取例如固定校准激活 scale、取消 warmup、使用较小的固定 lr 微调等优化手段解决方案qat 训练初始 loss 大或出现异常值初始精度掉点应按如下流程设计实验检查是否在训练时使用了特殊的数据增强策略例如旋转和马赛克等应当去除warmup 应当去除去掉 prepare 的步骤用 qat pipeline fine-tune 浮点实验是否正常。如异常需检查训练配置如优化器和 lr_updater保留 qat 的配置设置_FLOAT 或_CALIBRATION_V2不会引入工具 clip 误差状态关闭伪量化节点实验是否正常正常精度应基本对齐浮点则继续固定校准激活 scale 的实验固定校准激活 scalelr 设置为 0实验是否正常正常精度应对齐 calib固定校准激活 scale使用较小的固定 lr 微调进行实验qat 训练 loss 收敛慢精度相比 calib 无变化训不上去实验建议优先调整 BN 状态控制默认 fuse_bn无需开启 sync_bn安排 with_bn 的实验sync_bn 对齐浮点训练配置取消固定校准激活 scale和固定 scale 做对比取消 warmup采取较大的 lr延长 qat 迭代次数4.6 其他解决方案结合问题定位流程定位到问题阶段和现象综合参考上述解决方案不排除可能存在 bug需同步到地平线工具链团队解决