本文不是算法论文,也不是 API 手册
而是一线工程中,模型量化训练 + 分段导出部署 的一套可落地实践总结
一、为什么要写这篇?
在真实项目中,经常会遇到这种情况:
模型结构没问题
精度评估也能接受
单模型推理是通的
但一旦进入工程阶段:
模型太大,一次性编译和部署成本很高
Camera / LiDAR / BEV Task 明明可以并行,却被绑在一个模型里
拆模型之后:
性能下降
精度不稳定
问题很难定位
算法已经 OK 了,但工程形态没有设计好。
“整体量化训练 + 分段导出部署” 是一个绕不开的话题。
二、一个现实矛盾:模型要拆,但量化不能碎
从工程角度看,拆模型是很自然的:
Camera 一段
LiDAR 一段
BEV Task 一段
部署时做流水:
前段先跑
后段接着跑
不同模块使用不同加速资源
问题并不在拆模型,而在量化怎么处理。
三、很多项目都会踩的坑
常见但危险的做法
每个子模型:
各自 prepare
各自 calibration
各自 export
子模型之间:
输出 dequant 成 float
输入再重新 quant
实际后果
- 性能一定不好
Quant / Dequant 需要 CPU 或 VPU 参与,BPU pipeline 被频繁打断 - 精度一定不稳
同一份 feature 被多次重新量化,误差不可控 - 调试成本极高
精度下降时,很难判断是模型问题还是量化边界问题
结论很明确:
模型可以拆,但量化不能拆。
四、正确的工程思路
1. 量化训练必须是整体视角
- QAT / calibration 只做一次
所有子模块共享同一套量化语义
scale / zero-point 全局一致
2. 分段导出只是部署形态
拆的是模型结构
不是数值语义
分段导出 ≠ 分段量化
五、一个看起来“反直觉”,但必须接受的事实
在分段导出时,常常会看到:
子模型输入是 Quant
子模型输出是 DeQuant
第一反应往往是:
不是说模块之间不要反量化吗?
关键解释只有一句话:
这是给工具链看的,不是给最终部署看的。
六、为什么分段导出时仍然需要 Quant / DeQuant?
原因非常现实:
在 export / convert 阶段
- 工具链需要看到完整的量化闭环
否则 scale 无法推导,graph 也不完整
因此工程上通常分两步:
导出阶段
每个子模型保持完整 quant → int → dequant 语义
部署阶段
移除模型 I/O 上多余的 Quant / DeQuant
子模型之间直接传递量化 feature
代码中通常体现为:
这是性能和精度同时稳定的关键步骤。
七、一个可落地的整体方案结构
在实际工程中,比较稳定的一种拆分方式是:
整体模型
只进行一次 QAT / calibration
分段模型(示例)
Block1
Block2
量化策略
前段模型输出的 scale
显式传递给后段模型使用
不重新统计、不重新量化
最终效果:
精度可控
性能稳定
调试链路清晰
八、参考示例
用一个最小示例说明“整体量化一次 + 分段复用”的工程逻辑
- 量化语义和节点如何在完整模型阶段嵌入和一次性确定
- 分段导出时,各子模型如何复用同一套量化语义
避免模块之间发生隐式的重新 Quant / DeQuant
示例模型结构说明:关注“量化节点位置”,而不是算子本身
示例模型在逻辑上被拆分为两个阶段:
- Stage1:前半段特征提取(如 Backbone / Encoder)
- Stage2:后半段任务计算(如 Head / Decoder)
在模型中显式引入两类量化边界:
- quant:模型最前端输入量化
- quant1:Stage1 → Stage2 之间的接口量化语义(关键)
示例 forward 结构如下(节选):
这里需要强调的是:
quant1 的存在不是为了“拆模型”,
而是为了显式定义 Stage1 → Stage2 之间的量化语义边界。
分段 forward 的设计目的:服务导出,而不是多此一举
为了支持分段导出,示例中定义了两个 forward 变体:
这两个 forward 的真实目的在于:
- forward1
- 明确 Stage1 的输出量化语义
为分段导出阶段提供完整量化闭环
- forward2
- 明确 Stage2 使用的输入量化语义
与 Stage1 输出保持一致,而不是重新量化
关键结论是:
Stage2 的输入量化并不是重新统计,
而是延续 Stage1 已经确定的量化语义。
整体校准是分段复用成立的前提条件
这一阶段的目标非常明确:
在真实数据分布下,把整模型的量化语义一次性确定下来。
分段导出的关键:复用量化语义,而不是重新校准
在导出 Stage1 / Stage2 时,整体流程保持一致:
- 切换模型的 forward(forward1 / forward2)
- 使用相同的 qconfig 调用 prepare,建立量化结构
- 加载全模型校准得到的 state_dict(strict=False)
直接进入 export / convert 阶段
以 Stage2 为例:
这里需要给读者明确指出两点:
- strict=False
允许在不同 forward / 子图结构下,仅加载匹配到的量化参数
加载的重点是:
量化语义(observer / scale / zero-point)
而不是“普通权重”
关于 Quant / DeQuant:导出阶段保留,部署阶段收敛
在示例代码中可以看到:
分段模型的输入 / 输出均包含 Quant / DeQuant
- 这是导出阶段的正常状态,也工具链的要求
需要明确的是:
这些 Quant / DeQuant 并不是最终部署形态的一部分。
工程上的典型做法是:
- 导出阶段
保留 Quant / DeQuant
满足工具链对量化闭环的要求
- 部署阶段
移除跨段 I/O 上多余的 Quant / DeQuant
让量化 feature 在子模型之间直接流转
这样做的直接收益包括:
避免 CPU / VPU 参与量化转换
性能更加稳定
精度更加可控
这一参考示例真正想传达的结论
量化语义应先整体确定,再拆模型
分段导出不等于分段量化
模块接口的量化语义,是工程设计的一部分
如果一个大模型需要被拆成多个子模型部署,这一层逻辑基本是不可回避的。
九、什么时候值得用这套方案?
如果你满足以下条件,这套思路非常值得参考:
多模态 / 多分支模型
单模型已经难以一次性部署
对性能、功耗敏感
十、最后的经验总结
很多项目后期遇到的问题,并不是:
模型不行
工具链不行
而是:
一开始没有把量化当成工程设计的一部分。
如果在模型设计阶段,就同步考虑:
量化一致性
模块边界
部署形态
后面的路会顺很多。
希望这篇经验分享,能对正在使用地平线算法工具链做模型工程落地的同学有所帮助。
