前阵子帮一个朋友调移动端检测模型,遇到的场景非常典型:模型包体太大,内存动不动就超低端机的阈值,方案组第一反应就是压到全INT8。结果压完一看,精度掉了将近两个点,尤其是小目标召回率崩得厉害。当时我第一反应是“加数据、调阈值”,折腾一圈没起色,后来才意识到问题出在量化策略本身——我们用了无差别的均匀量化,把每层都死死压到8bit,根本没考虑不同层对量化误差的敏感度差异。后来换成混合精度量化,精度损失收回到了0.3%以内,内存和时延的收益也基本保留。这篇文章把这套实践的思路、流程和踩过的坑都整理出来,给准备做端侧AI落地的同学作个参考。
混合精度量化在移动端AI加速里不算新概念,但真正落地时和论文里的描述差距很大。论文爱讲“自动搜索最优位宽”,实战里更多是哪层值得给高精度、哪层可以放心压到低比特。文章适合两类人:一类是算法工程师,模型要上手机却面临精度与体积的两难;另一类是移动端开发,想理解量化压缩除了“省内存”之外,到底给推理链路带来什么变化。
1. 移动端部署的硬约束:量化不只是省内存,而是在换算力
1.1 端侧瓶颈:DRAM带宽、指令密度与算子实现差异
手机跑模型的瓶颈往往不是计算本身,而是内存带宽和容量。模型加载进内存后,每一轮前向都要反复读取权重和中间激活值,实时上这些数据要被搬运多次。移动端的DRAM带宽有限,访存功耗也高,量化最直接的收益其实是“让每笔内存搬运装载更多有效计算”。
以常见的4字节浮点权重为例,一张普通卷积光权重就有几百万参数,一次推理要完整读一遍。压到INT8后内存带宽消耗立刻降到原来的三分之一左右,如果进一步压到INT4,理论上带宽还能再砍一半。但这里有个前提:底层算子确实在用低比特指令跑。如果推理引擎在某个算子实现上没有对应的低比特内核,就只能“反量化到浮点、算完再量化回去”,带宽省了个寂寞,精度损失倒是结结实实吃满了。
移动端CPU的并行计算单元对整型数据的吞吐通常高于浮点,这是量化的另一大动力。不过光看理论吞吐没用,算子访存模式、数据排布、缓存命中率,任何一个环节拖后腿,最终延时都不按比例下降。实测经验是,一个优化良好的INT8卷积和FP32卷积比,在算力瓶颈场景大约能快3到5倍,在访存瓶颈场景可能只快1.5倍。
1.2 全INT8不是银弹:敏感层掉点、算子回退与隐藏的浮点路径
很多人觉得全INT8是“最稳的量化方案”,因为硬件支持成熟、工具链简单。但从落地效果看,全INT8经常在两类模型上翻车:小目标检测和回归任务。原因不复杂,这类任务对输出张量的数值精度非常敏感,某些层的数值分布范围很宽又不均匀,INT8的量化步长步子太大,一旦误差累积,最终层的回归值就严重漂移。
还有更隐蔽的问题:算子回退。一个模型上百个算子,推理引擎不会为所有算子都实现INT8内核。遇到不支持的算子,引擎有两种处理方式,一种整图退回FP32,另一种在算子边界做来回转换。后者在图上看起来还是“INT8模型”,实际推理路径里相当一部分算子仍是浮点计算。这种情况下,全INT8看似省了内存,端到端延时可能反而比精心设计的混合精度方案还高。
还有一个被忽视的点:激活值的动态范围。ReLU类的激活函数会把负数截断,数值分布相对集中,量化起来容易。但残差加法、拼接、归一化这类算子会把多路张量叠加起来,数值范围撑得很大。个别层单独看INT8误差很小,叠加几层之后误差像滚雪球一样变大。这就是为什么有些层必须给更多比特位。
1.3 我为什么开始关注混合精度量化
拿那个检测项目举例,第一轮全INT8的结果是整体精度掉了1.8个百分点。我们试着对骨干网络做知识蒸馏,效果还行但训练周期太长;又试了更大的校准数据集,只把损失降到1.4个点左右。真正把问题解决的转机,来自一次逐层扰动分析——把每一层分别改成INT8跑一次,记录输出误差的变化。
结果发现,真正导致精度崩掉的“钉子层”集中在检测头的回归分支和某几个跨尺度特征融合的残差支路上。其余大部分卷积层在INT8下的输出余弦相似度依旧在99%以上。这说明精度预算花歪了:大量低风险层没有省到该省的,反倒为少数高风险层付出了整模型降精度的代价。于是我们把高风险层保留到INT16或FP16,其余层继续留在INT8,精度迅速恢复到浮点基线的99.7%左右。
这件事给我的启发是:量化精度损失本质上是“总误差预算分配”问题。与其对所有层一视同仁,不如把有限的比特位精准花在刀刃上。这就是混合精度量化最直接的实践起点。
2. 混合精度“混”在哪里:从量化粒度到敏感度分层
2.1 量化格式与粒度的基础:per-tensor、per-channel与代表性比特位
混合精度量化的“精度”不是单指位宽,还包括量化粒度和格式。粒度的选择往往比位宽本身更影响精度。
per-tensor是整个张量共享同一个scale和zero-point,实现最简单,但遇到通道间数值范围差异显著的卷积核,量化误差会很大。per-channel是每个输出通道单独计算scale,精度好不少,代价是实现复杂度上升、部分硬件需要额外指令来处理多scale。实践里卷积权重用per-channel基本是标配,激活值因为结构限制大多还是per-tensor,这也是激活量化误差通常比权重大的原因之一。
位宽方面,移动端部署最常见的组合是INT8、INT16和FP16。INT4在部分硬件上能省更多内存,但端侧CPU支持通常较差,可能需要手工做位打包和解包,耗时反超INT8。FP16本质仍是浮点,不存在量化误差,但相比FP32能省一半内存带宽,GPU和部分NPU对FP16的吞吐往往比INT8还高,因此FP16非常适合当那些“舍不得压精度”的层的过渡方案。表格里整理一下这些格式的特点:
| 数据格式 | 存储位宽 | 典型硬件吞吐 | 精度风险 | 常见用途 |
|---|---|---|---|---|
| FP32 | 32bit | 基准 | 无 | 基线对照 |
| FP16 | 16bit | CPU略高于FP32,GPU大幅提升 | 极低,动态范围受限 | 敏感层过渡 |
| INT8 | 8bit | 平衡精度与效率 | 中等,依赖校准质量 | 主干卷积、大参数量层 |
| INT4 | 4bit | 部分硬件无原生支持 | 较高,需per-channel配合 | 参数量大且低敏感度层 |
顺带把QAT(量化感知训练)和PTQ(训练后量化)的区别说清楚。混合精度量化实践里PTQ用的频率更高,因为逐层分位宽做QAT训练成本太高;PTQ的核心是把校准数据喂进模型,统计每层张量的数值分布,再据此确定scale。混合精度方案下的PTQ更依赖校准集质量,因为不同位宽层对分布估计的敏感度不同。
2.2 不同算子的量化敏感度差异
墨守成规的“每层都一样”之所以不可取,根本原因是算子类型决定了它对数值误差的容忍程度。以下是我在实践里总结出的规律,用表格快速一览:
| 算子/模块 | 建议策略 | 理由 |
|---|---|---|
| 首个卷积层 | 保持INT8/FP16 | 输入分布贴近原始图像,噪声经过该层会被骨干放大 |
| 中间深度卷积 | 可压INT4 | 参数少、通道独立计算,激活值分布通常集中 |
| 残差加法 | 保持INT8或更高 | 张量叠加会扩大动态范围,量化步长变大 |
| 检测头回归分支 | 保持INT8/FP16 | 回归值需要细粒度输出,低比特误差直接反映到坐标漂移 |
| 分类/置信度分支 | 可压INT4 | 分类输出对微小偏差容忍度较高 |
| softmax/归一化 | 在浮点域计算 | 指数与归一化对精度极其敏感,低比特误差会指数放大 |
| embedding/lstm门控 | 保持INT8/FP16 | 特征查找与门控值的微小误差会沿时序累积 |
这张表不是教条,而是一种“先验”。落到实处,还是要用数据验证敏感度。因为同一个算子在不同模型里,作用可能是相反的。比如检测头的回归分支,在某些小模型里反而表现出很强的鲁棒性。所以敏感度分析没法省。
2.3 敏感度分析的核心方法:逐层扰动与观察指标
敏感度分析是混合精度量化实践里最花时间、但价值最大的一步。具体思路是把某一层设为目标精度,比如先单独改成INT4,其它层保持浮点,然后比较输出变化。一个模型上百层,要做很多次实验,所以要有策略。
我的操作顺序是:
- 准备校准集。副本上可以直接用验证集子集,三五百张代表性图片即可,关键要覆盖真实业务的分布,比如夜间、遮挡、极端光照等场景。
- 记录模型在浮点模式下的中间层输出和最终指标,作为基准。
- 每一层单独切换目标精度跑一次,计算该层输出张量与浮点基线之间的余弦相似度,同时记录最终任务指标的变化。
- 根据余弦相似度和最终指标的综合变化,给每层排一个“敏感度分”。
度量指标值得多说一句:只看最终任务指标并不够,因为误差可能在层间传递和抵消,某些层单独改起来“无感”,组合起来伤害极大。所以在做粗筛时,我更看重中间张量的余弦相似度,它比“最终精度”更局部、更准确。所有层都测完一轮后,把“敏感度分”和参数量放在一起看,找出那些又大又稳健的层去做低比特压缩,效果好、风险也低。
这个分析过程是纯工程活,没有太多技巧,但可以有效提速:先在GPU上用动态量化模拟目标位宽,用少量校准数据计算余弦相似度粗筛一遍,再只对排名靠前的候选层跑完整验证集精测。全模型过一遍粗筛往往几分钟就能搞定。
3. 层间精度分配的搜索逻辑:从暴力枚举到贪心分配
3.1 手工指定与启发式搜索:误差预算怎么分
敏感度分析做完,手上就有一张“每层在不同位宽下的误差-收益表”。下一步是决定哪些层到底用多少位宽。这本质是一个组合优化问题:位宽选项若干种,层数上百,全枚举不现实。实践里常用两类思路:一类是纯手工指定,靠人对模型的理解来挑,简单直接;另一类是启发式搜索,设定“误差预算”,然后按“每减少1MB占用的精度损失最小”排序,逐层压低直到预算用完。
启发式搜索的思路很像视频编码里的码率控制。编码器会把码率当作预算,优先给视觉上更敏感的区域分配更多码率。量化里“精度损失”就相当于视觉失真,“内存占用缩减”相当于省码率。目标是在失真不超过红线的前提下,把每一份比特都花在边际收益最高的层上。
实际做的时候,误差预算往往来自业务方的指标红线,比如“检测AP下降不能超过0.5%”。这个红线就当作搜索的终止条件。
3.2 一个可复现的敏感度排序与贪心分配流程
坦白说,我见过不少团队直接让工程师手工指定精度配置。小模型可以,几十层的模型勉强,上百层的大模型还是上脚本吧。这里给一个在多个项目里验证过的贪心分配流程。伪代码如下:
# 伪代码:贪心分配精度配置 results = {} # 1. 对每层、每个候选位宽做仿真,记录误差与收益 for layer in model.layers: for bit_width in [4, 8, 16]: metric_loss = simulate(layer, bit_width) # 用余弦相似度或任务指标 mem_saving = estimate_memory_saving(layer, bit_width) results[(layer, bit_width)] = { "error": metric_loss, "saving": mem_saving, "ratio": metric_loss / mem_saving # 单位收益的代价 } # 2. 按“每节省1MB带来的误差”升序排序,优先做代价小的 sorted_keys = sorted(results, key=lambda k: results[k]["ratio"]) # 3. 在总误差预算内逐项应用 budget = 0.005 # 举例:允许最多0.5%的精度损失 current_loss = 0.0 selected = {} for layer, bit_width in sorted_keys: if current_loss + results[(layer, bit_width)]["error"] > budget: continue selected[layer] = bit_width current_loss += results[(layer, bit_width)]["error"]实际工程里要注意,simulate某个层时,其它层最好保持当前配置,因为层间误差不独立。更稳妥的做法是贪心过程中每改一个层,就用校准集重新前向一次,动态更新误差估计。这会增加计算量,但效果更准。
另一点很容易踩坑:不同位宽在不同算子上的收益不是线性的。比如一个算子从INT8改成INT4,内存节省可能只有20%,因为它的参数本来就少;而另一个算子哪怕从FP16改成INT8,节省的内存也可能不值一提,因为参数量大的其实是前一个。排序时一定要把收益计算放进ratio里,不能只按敏感度排序。
3.3 搜完之后强制项:整图编译约束与算子支持检查
精度配置搜索结束,不代表模型就能直接跑起来。有一个经常被忽略的环节是“算子融合”。很多推理引擎在编译模型图时会把“卷积+激活+批归一化”之类的小算子合并成一个大算子。一旦发生融合,你在模型层面做的“层级精度分配”就和引擎实际执行的“算子簇”对不上了。
所以拿到搜索出的精度配置后,一定要导出推理引擎的完整算子图,对照检查每个融合后的算子簇,确认该簇里每个基础算子的精度要求一致。如果某个融合簇里既有高精度层又有低比特层,引擎为了保证正确性,往往会以最高精度为准来编译,那么你辛苦搜出的低比特分配就白废了。
还有算子支持检查。每个候选位宽都必须在目标平台上找到实现路径,否则就得自己写内核,或者换一个次优精度。这里特别建议提前做一张“位宽-算子-平台支持矩阵”,把开发机、主流低端机、中端机、带NPU的机型都标上,避免搜出来一个漂亮配置结果在旗舰机上跑得好好的,换到低端机就整体回退成浮点。
检查完毕,接下来还要重新校准。因为新配置下各层的数值分布已经变了,之前记录的scale不再可靠。校准的意义在于重新统计激活值的分布,并给每个scale留出足够的安全边界。
4. 端侧集成与实测:时延、内存、功耗的联合验证
4.1 集成实测的基本流程与基准指标
搜索和校准都完成,工作只算完成了一半。混合精度模型要真正“可用”,还要看它在真实设备上的表现。这里建议把验证拆成三层:功能正确性、性能指标、稳定性。
功能正确性指输出和浮点基线保持合理一致。逐层余弦相似度可以在这步继续用,但与浮点基线的最终输出误差要控制在业务容忍范围内。性能指标包括单次推理时延、峰值内存、RSS常驻内存、CPU占用、设备温度和功耗曲线。稳定性看的是连续推理多次,耗时是否抖动、是否有内存泄漏、是否偶发崩溃。
实测流程有条基本套路:
- 选参考机型,覆盖高端和中低端;每台设备都清空后台进程,固定屏幕亮度和性能模式。
- 模型编译后预热多次。通常预跑十次以上再开始计时,避免首轮加载、缓存、动态内存分配等干扰。
- 设置不同输入分辨率、不同batch大小,分别记录指标。
- 在FP32、全INT8、混合精度三个版本之间做同一套测试,控制变量。
- 观察多次推理后的内存曲线,确认无持续增长趋势。
理想情况下,混合精度模型要比全INT8多花一点存储,但精度明显接近浮点;比FP32快很多,内存也降下来。它更像是两个极端方案之间的一次“最优折中”。
4.2 一组实测数据及解读
用我们那个检测模型的数据举个例子(数值经过脱敏处理,但趋势和真实情况一致)。分别测了FP32、全INT8和混合精度三个配置:
| 指标 | FP32基线 | 全INT8 | 混合精度 |
|---|---|---|---|
| 相对精度 | 100% | 98.2% | 99.7% |
| 模型文件大小 | 90.4 MB | 24.8 MB | 28.5 MB |
| 8线程峰值内存 | 286 MB | 152 MB | 169 MB |
| 单帧时延(低端机) | 85 ms | 45 ms | 49 ms |
| 单帧时延(中端机) | 41 ms | 23 ms | 25 ms |
全INT8的时延和内存最漂亮,但精度掉得让人没法接受。混合精度只多花了3.7MB内存、几毫秒时延,换回了将近1.5个点的精度。对大多数业务来说,这个交换非常划算。
还有一个细节:内存收益并不等于模型文件大小的收益。混合精度模型比全INT8的模型文件大一点,但推理时的峰值内存差距更明显,因为激活值也占内存。我们保留了部分FP16层后,激活值的内存占用反而比全INT8版本高得不多,原因是这些FP16层大多是访存密集型的细枝末节,激活张量很小,影响有限。
热量和功耗也该提一下。实测中混合精度模型和全INT8模型在连续推理二十多分钟后,设备表面温度差不到1摄氏度,耗电量也几乎持平。原因很好理解,省下来的CPU占用时间基本弥补了额外内存访问带来的功耗。如果你的模型是持续运行比如摄像头实时检测的,这部分数据务必要测,不要只看单帧时延。
4.3 内核切换与数据排列:容易被忽视的端侧性能缺口
混合精度模型跑起来有个特有的开销:不同精度的算子频繁交替时,推理引擎要为每段计算准备不同的内存布局和指令路径。从INT8卷积切到FP16卷积,再到INT8卷积,整个流水线会出现明显的“冷切换”损失。严重时,这种切换开销反而会让混合精度模型比全INT8模型更慢。
解决办法主要有三招:第一招是尽量让相同精度的算子连续排列。如果发现某些低比特层的连接关系导致精度“锯齿波”式交替,考虑手动调整层顺序并验证是否能保持精度不变。第二招是确认引擎是否支持并行多个队列,或者在构图时把精度切换点设计成批量切换,降低切换频率。第三招是数据排列,低比特张量在高比特计算前必须布局转换,如果能让引擎复用预设的布排,可以省下一次转换。
这几招不是所有引擎都支持,所以务必在目标设备上写一个小工具,专门打印出每个算子的实际耗时与调用顺序。通过这个输出,能清楚看到每次精度切换到底花了多少时间。这步看起来费事,但我前后试过几个项目,最后性能优化都从这一步里找到了突破口。
5. 踩坑记录:混合精度量化最常见的四类问题
5.1 精度反弹的隐因:激活值分布偏移与未重校准
混合精度量化过程中最让人头疼的现象是“精度反弹”。某个层从INT8调到INT4后,粗测指标看着没问题,几天后换了一批测试数据,精度突然从99%掉到94%。排查下来,根子在于那一层的激活值分布其实发生了显著偏移,但校准阶段没有发现。
很多人的校准集就一套,只覆盖了典型场景。但真实业务的数据分布很宽泛,有些层在“极端场景”下会出现长尾离群值。原本INT8时scale和zero-point对这些离群值还有一定容忍度,降到INT4后,每格代表的范围变大,离群值直接饱和截断,误差爆炸。这不是模型训练的问题,纯粹是量化scale定得太紧,没有给尾部留足富裕。
解决方法是给每层记录校准时的最小值、最大值以及不同百分位点。建议在校准统计中保留0.5%到99.5%之间的范围,再加一个小幅的padding比例,比如2%到5%。同时,每次调整任意一层的位宽后,都要对下游所有层重新校准,因为你的改动会改变激活值分布,单层scale更新根本不顶用。
5.2 看似“无损”的层成了时延黑洞:低比特控制流分支的代价
有些层看起来压到低比特后精度无碍,但端到端时延反而变长了。这类层往往是控制流分支较多的结构,例如动态路由、注意力加权后的条件分支、detection head里的候选框后处理。量化本身没问题,问题在于低比特化后,引擎无法再走原先高效的向量化路径,被迫拆出一条条单独执行的小kernel,指令缓存压力陡增,调度切换开销盖过了访存节省。
面对这种情况,我建议先看profile数据,统计一下算子级耗时占比。如果某个低比特算子耗时高得异常,优先把它恢复到INT8或者FP16,然后重新测整模型时延。如果恢复后整图时延反而下降了,说明低比特化对这个算子不划算——它的参数太少、分支太重,内存节省带来的正收益抵消不了执行路径变复杂带来的负收益。
这类“精度无碍但性能受损”的层,往往还是手动检查来得更快。因为搜索算法只看了误差和内存收益,没算执行路径复杂度和调度成本。工程上更合理的做法是把“profile出的实际算子耗时”反馈进搜索目标,而不是只在离线阶段评估。
5.3 不同批次大小下的性能抖动与CPU/GPU/NPU差异
同一个混合精度模型,在服务器GPU上压测性能优异,到手机CPU上却可能排名垫底。这里有个常见的系统性差异:服务器评测往往用大batch和满负载,移动端推理通常是batch=1,内核启动开销、内存分配开销占了大头。
我们在一个模型上遇到过很诡异的现象:batch=1下混合精度模型比全INT8慢不少,但batch=4时反而更快。原因是batch=4可以充分利用SIMD,那些高精度层的浮点计算成本被摊薄了;而batch=1时,引擎切换精度和初始化内存布局的成本是固定开销,相对占比被放大。
另一层差异是硬件指令集。CPU上INT8指令通常比较成熟,但INT4支持几乎为零,需要手工位打包,反而变慢。GPU上FP16的吞吐很高,用FP16层比用INT8层还快。NPU更是各显神通,有的对低比特支持极好,有的只能做INT8或FP16,混合精度会被整体回退。所以做混合精度量化前,哪怕配置再漂亮也要先在具体芯片上跑一版“精度-性能烟雾测试”,验证核心算子在不同位宽下真实能达到的吞吐。
5.4 我的排查方法论:先从端侧profile开始,再回到模型
混合精度模型出问题时,我从不先去质疑搜索算法或爆改精度配置。正确的排查顺序应该从端侧性能数据开始:先拿到每一类算子的耗时、内存、数据搬运量,搞清楚瓶颈是计算、带宽还是kernel调度。
要是瓶颈在带宽,把更多层压到低比特是正确方向;要是瓶颈在计算,你可能需要重新审视哪些层在高精度上拖后腿了;要是瓶颈在调度切换,重点就要放在算子重排和精度连续性上。这些方向完全由profile数据决定,而不是拍脑袋。等性能数据搞清楚后,再回到模型层面对应调整,往往一次就能定位得八九不离十。
有一回模型精度和时延都正常,但反复出现偶发的长时间卡顿,怎么检查都找不到原因。后来我打开系统trace,发现是内存碎片导致分配器在某些精度切换的瞬间频繁执行垃圾回收。这其实属于更底层的运行时问题,但混合精度的多布局内存模式会让碎片问题比均匀INT8更容易暴露。解决方法是调整内存分配策略或预先分配固定大小的缓冲池,而不是去改精度配置。
6. 什么时候该退回均匀INT8(以及最后一些实操建议)
6.1 什么场景不适合混合精度量化
混合精度量化不是万金油。有些场景折腾半天,结果还不如干净利落的全INT8来得省心。
第一种不适合的场景是模型规模小。一个十几层的轻量模型,全INT8本来就能做到接近无损,混合精度搜索省下的零点几兆内存根本无所谓,反倒因为多精度切换引入的调度开销,时延可能更差。这种情况下追求“单帧低延迟、极低内存”,均匀INT8是最优解。
第二种是目标设备碎片化的产品。如果同一个模型要派发到几十种不同芯片、不同系统版本的设备上,某些设备可能只支持INT8/FP16,另一批又只支持FP32/INT8,一个混合精度配置很容易在个别机型上触发整体回退。这时不如老实全INT8,保证所有设备行为一致。
第三种是团队没有时间维护。混合精度配置不是一次定死的,每次模型更新后都要重新评估和校准。如果迭代节奏特别快,两周就换一版模型,全INT8加上简单的重校准反而是成本最低的路线。
另外,如果你的工具链本身就支持不了混合位宽导出,也没法自动处理不同精度算子的融合,那强行上混合精度的工程代价极高。遇到这种情况,先花时间确认底层工具链能力边界,再决定值不值得硬碰。
6.2 我个人测试下来的经验清单
这套实践做完,沉淀下来几条可以直接复用的经验。头一条是“不要一上来就全局压低比特”。哪怕时间紧张,至少先做一轮敏感的余弦相似度粗筛,找出那些一旦压就掉点的“钉子层”。很多情况下,把十个高风险层单独留在高精度里,比全模型压INT8再想办法恢复精度要快好几倍。
第二条是“校准集质量决定量化天花板”。校准集不只用来统计分布,还要代表真实业务的多样性。比如目标检测模型,校准集里如果没有小目标、没有模糊目标,量化后这些小目标场景会首先崩掉。
第三条是“把精度配置当代码一样管理”。每个层的位宽配置、校准集版本、量化参数、模型结构版本,都该纳入版本控制。没有这套记录,一个月后的自己都说不清当时的精度参数是怎么来的。
第四条是“花点时间做端侧profile自动化”。在目标设备上自动跑一遍算子级耗时的脚本,能省下大量测试时间。性能问题一旦能定位到算子级,很多看似玄学的现象立刻就有了解释。
最后一条算不上经验,更算态度:混合精度量化的本质是“精度、体积、速度”三元权衡。不要指望免费的午餐,每一项收益都要用牺牲换。你的任务只是确保每次牺牲都花在性价比最高的地方。
我自己在实际项目里越来越觉得,混合精度量化很像在给一整条推理管线的每一段“定价”:高精度意味着高预算,低比特意味着省钱。做量化方案时先别急着动手,花时间把那几个最要命的高消费段找出来,其余的该怎么省就怎么省,结果通常差不了。如果你不确定模型里哪些层该保留高精度,可以先试最保守的中间方案:把所有层压到INT8,再单独把注意力模块、检测头回归分支和残差加回的几处提升到FP16。这个改动往往不需要重新训练,就能在耗费极小的条件下拿回大部分精度损失。等你跑通这条链路,再逐步下探其它层的位宽,整个过程会稳很多。