专栏算法工具链模型从量化训练到分段导出部署

模型从量化训练到分段导出部署

Huanghui2025-12-30
85
0
本文不是算法论文,也不是 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 也不完整

因此工程上通常分两步:

  1. 导出阶段

    每个子模型保持完整 quant → int → dequant 语义

  2. 部署阶段

    移除模型 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 时,整体流程保持一致:

  1. 切换模型的 forward(forward1 / forward2)
  2. 使用相同的 qconfig 调用 prepare,建立量化结构
  3. 加载全模型校准得到的 state_dict(strict=False
  4. 直接进入 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 参与量化转换

  • 性能更加稳定

  • 精度更加可控


这一参考示例真正想传达的结论

  • 量化语义应先整体确定,再拆模型

  • 分段导出不等于分段量化

  • 模块接口的量化语义,是工程设计的一部分

如果一个大模型需要被拆成多个子模型部署,这一层逻辑基本是不可回避的。

九、什么时候值得用这套方案?

如果你满足以下条件,这套思路非常值得参考:

  • 多模态 / 多分支模型

  • 单模型已经难以一次性部署

  • 对性能、功耗敏感


十、最后的经验总结

很多项目后期遇到的问题,并不是:

  • 模型不行

  • 工具链不行

而是:

一开始没有把量化当成工程设计的一部分。

如果在模型设计阶段,就同步考虑:

  • 量化一致性

  • 模块边界

  • 部署形态

后面的路会顺很多。

希望这篇经验分享,能对正在使用地平线算法工具链做模型工程落地的同学有所帮助。

算法工具链
征程6官方教程
评论0
0/1000