OE Agent“天工”新版本发布,请前往论坛顶部查看,欢迎试用并反馈~
查看OE-Skills
专栏算法工具链【地平线J6工具链入门教程】QAT链路模型改造指南

【地平线J6工具链入门教程】QAT链路模型改造指南

芯链情报局2026-07-09
5
0

QAT链路模型改造

模型改造步骤

  1. 量化范围界定:若模型包含前处理或后处理模块,应将不需要量化的前处理置于 QuantStub 之前,后处理置于 DeQuantStub 之后

  2. 不支持算子算子替换:参照算子约束表,将自定义算子,非class继承nn.Module,非torch标准算子以及函数式算子(诸如F.linear,F.conv2d,F.relu等)替换为支持的等价算子(不含F.grid_sample和F.interpolate)
  3. 极值替换:对于那些涉及±inf,nan,除极小值,乘极大值等操作,建议将数值修改至量化友好范围内,或替换为对量化友好的等价操作。

  4. 量化节点插入:在量化边界的起始处插入 QuantStub 标记量化起点,在量化边界的终止处插入 DeQuantStub 标记量化终点,以明确标识部署边界

  5. 辅助分支:辅助分支建议不参与量化链路,需在辅助分支的输入之前插入 DeQuantStub,使其保持浮点运行状态;如果辅助分支的输出要再进入量化部分,则需要在辅助分支输出之后插入QuantStub

  6. 数据增强关闭:对于可能影响数值统计范围的数据增强方法(如 mosaic、random resize、prepare_dn 等),需在量化训练时予以关闭。

QuantStub/DeQuantStub使用说明

  1. QuantStub/DeQuantStub必须注册为模块,否则无法正确处理量化状态

  2. 每个需要量化的tensor,应使用各自独立的 QuantStub,不可复用

Tensor quant节点插入

  • bool类型tensor

在qat链路中涉及bool类型数据计算时,其行为与全float计算有所不同,需要特别注意。当bool仅作为mask进行判断时无需量化,当bool需要与浮点tensor进行交互,需要将bool的tensor转为float然后再插入量化节点,示例如下:

  • int64类型tensor

由于bpu不支持int64类型计算,当模型中出现int64数据会引入CPU的cast算子,会拖慢模型性能。

一般来说,int64出现的情况有以下几种:argmax/argmin的输出,topk的索引输出,scatter,gather的索引,Embedding的输入。

对于argmax/argmin算子来说,根据其输出数据的范围,可以将其转换为int8/int16。

对于topk来说,其输出的index的类型为int64,如果其后面接的为gather,可以参考下面进行修改:

对于Embedding来说,其输入为int64,直接送入int64,会生成cpu算子,需要修改输入为int16/int8,然后在送入embedding之前转换为int64:

对于模型中较多逻辑判断涉及到bool数据类型的赋值和mask操作,由于其需要的索引需要int64,可以使用torchwhere来进行等价替换。
  • 一般tensor

对于张量类型,如果其是从上游得到的无需插入quantstub,如果是在代码中生成或是self中定义的tensor,需要为其插入量化节点:

标量说明

对于标量类型的加减乘除,无需插入量化节点。

量化节点插入检查

有时候,由于工程过大,导致一些quant/dequant节点,忘记插入。下面提供一些检查,快速判断判断是否忘记插入节点的方法:

  1. QuantStub判断

在调用prepare接口,在运行中如果出现类似下面的错误,表示模型中缺少quant节点:

在模型中如果存在在量化域保存前向tensor的情况,需要确保要先将这个tensor dequant之后再保存。

  1. DequantStub判断

在prepare结束后,可以查看日志,如果出现下面这种情况,说明缺少dequant节点。
alt text
在export转换阶段,如果运行报错,且有如下日志输出,则说明缺少dequantstub节点:
alt text

其他说明

  1. 对于显式指定to(torch.float32)这种,如果数据本身就已经是float32的了,再显示调用to(torch.float32)在QAT的prepare过程中可能会报错。需要将这种操作删除

  2. 动态代码块(循环次数根据判断动态变化的代码)涉及算子替换或融合时,必须用horizon_plugin_pytorch.fx.jit_scheme.dynamic_block包裹,否则会导致量化信息错乱或forward报错

  3. 涉及if-else判断的分支,由于prepare时只会trace执行example_inputs所触发的那条分支,数据只会trace到if-else其中一条分支,另一条路径会完全丢失,不会插入任何量化节点,可能会导致训报错或者精度一致性出现问题

  4. 共享模块如果在forward中多次被调用,且每次调用的数据分布差异较大,会导致量化参数统计不准确,因为共享module的scale是对所有调用处的数据统一统计。当数据范围差异较大时,最终的统计范围是以数值范围较大的为准,会导致数据范围小的模块丢失精度,建议使用deepcopy拆分

  5. FreezeBN:如果在浮点训练中使用了 freeze bn 的技巧,需要在prepare之前将 qat mode 设置为 withbn,保证bn不需要被融合且保持评测状态

  6. 如果模型中有最终输出返回None的情况,需要将None从最终输出中删除,否则会导致编译报错

mmdet实践

mmcv中的Linear

由于plugin无法识别mmcv中的linear,会导致模型出现问题,因此,下面提供了1种不修改代码快速处理方式:

mmcv中的MSDA

由于plugin无法对底层为cuda的算子进行检查,这个会在model\check_result中体现为deformable_attention的下一层输入为float32,需要手动修改源码,切换为pytorch版的msda来进行量化:
alt text

mmcv中的SyncBN

如果使用了mmcv实现的syncbn,需要替换为torch的syncbn:

inplace算子转换

Prepare适配Demo

在mmengine中,配置文件通过from_cfg进行实例化,但由于mmengine在from_cfg这个接口内,就已经实例化模型,如果是分布式,模型就已经是多卡状态了,这与prepare需要的模型在单卡上相悖,因此,下面提供了一个简单的修改示例(模型的QuantStub/DeQuantStub需要额外添加,并没有展示在示例中):
运行的主文件,类似于mmengine中的train.py,下载和说明见社区帖子QAT链路模型改造示例 - 地平线开发者社区
算法工具链
技术深度解析征程6
评论0
0/600