资讯动态

YOLO26量化实战:从PTQ到QAT的完整部署指南

发布时间:2026/10/14 9:00:39 来源:尧图企业网站定制
量化这个词在深度学习落地圈子里已经快被说烂了但真正动手把模型量化掉、还能保证精度不掉的人其实并不多。我最近把新的YOLO26模型完整走了一遍量化流程从PTQ到QAT踩了不少坑把一些经验整理出来给正准备做模型部署的朋友做个参考尤其是那些边做检测任务边纠结量化到底怎么搞的人。先说一下背景。YOLO26是目前YOLO系列里一个比较新的版本它的网络结构相比之前的版本有了一些调整比如更深的C2f模块变体、更复杂的注意力机制以及更精细的多尺度融合策略。结构复杂带来精度提升的同时也为量化埋了不少雷——深层特征图中数值分布非常不均匀激活值动不动就出现极端离群点直接做PTQ很容易翻车。这也是我为什么愿意花时间把PTQ和QAT都完整跑一遍而不是像以前那样直接丢给推理引擎自动量化。这篇内容不是教科书式的原理讲解而是把量化是什么、PTQ怎么做、QAT怎么调、哪里会出问题、精度掉了怎么修这条链路串起来每一步都有对应的实操记录。适合的目标读者是已经跑通过YOLO26训练、正准备做端侧或边缘设备部署但对量化只有模糊概念的工程师。如果你连模型训练都没跑明白建议先把训练这块补齐了再来看。1. 先弄清楚量化到底在做什么从FP32到INT8的本质变化很多人一听量化就以为是简单地把模型变小其实量化最核心的动作只有两个字映射。把原本连续分布的FP32浮点数映射到离散的INT8整数空间通常就是-128到127或者0到255同时让这个映射过程带来的信息损失尽可能小。用大白话解释一个FP32的数值可以表达小数点后很多位但INT8只能表达256个档位。这就像原来你能把一个人的身高精确到毫米现在只能按厘米或者半厘米来报身高本身没变但记录精度变了。量化的目标就是在这个按半厘米报身高的约束下尽量让人群的整体统计特征不丢。在AI模型里这个映射是通过两个关键参数完成的缩放系数scale和零点偏移zero point。公式是这样的value_int clamp(round(value_fp32 / scale) zero_point, min, max)反量化则是value_fp32_deq (value_int - zero_point) * scale也就是说只要拿到每个张量或者每个通道的scale和zero point模型就能在整数空间里完成前向推理而不用在每一层都做浮点运算。推理框架像TensorRT、OpenVINO、RKNN这些内部就是这样干的。但这里面有个很关键的问题scale值是根据原始浮点数据的数值范围来定的。如果原始数据的分布比较集中比如大多数数值都在-1到1之间但偶尔冒出几个5.0或-8.0的离群值scale就会被这些离群值撑大导致正常数值在INT8空间里分辨率变低也就是精度损失。这个现象在后面对YOLO26做PTQ时我遇到了很多次后面有详细案例。还有一层要搞清楚量化不是说把所有层都无脑压到INT8就一定好。对于某些数值敏感层比如检测头的输出层尤其那种直接回归坐标的层即使能量化精度也容易崩。所以很多推理引擎支持部分量化——敏感层保留FP16或FP32其他层走INT8。合理的量化方案设计本质上就是在压缩率和精度损失之间做平衡博弈。2. PTQ实战直接用YOLO26跑训练后量化最容易踩的坑PTQ全称Post-Training Quantization翻译过来就是训练后量化。它的逻辑很简单模型训练完毕、权重固定了直接拿着训练好的模型喂一批代表性输入数据统计每一层的激活值分布然后算出scale和zero point完成量化。这个方案最大的优势是快、成本低不需要重新训练模型。GPU算力紧张、时间紧的时候PTQ往往是第一个尝试的方案。但如果你的模型分布很糟糕PTQ精度可能会跌得没法看YOLO26就是这种典型。2.1 校准数据集PTQ里最容易被轻视的环节PTQ的精度很大程度上取决于校准数据集的质量。校准数据集的作用不是参与训练而是用来观察模型各层的输出分布所以它必须足够有代表性。我的做法是从训练集里随机抽取500到1000张图尽量覆盖各种光照条件、目标尺寸、目标类别比例并且保证小目标样本不能太少。YOLO26的检测场景如果主要面对小目标比如航拍图、监控画面校准集里全是常规尺寸的物体量化后小目标的召回率会明显下降。另外一个我踩过的坑是校准数据的预处理方式和训练时保持一致。YOLO26训练时常用的是letterbox保持宽高比填充灰边而不是直接resize校准数据集在喂给PTQ工具时也要做同样的letterbox操作。数据增强呢不能开。校准过程需要的是模型在正常推理状态下的输出分布你给它加随机翻转、马赛克增强统计出来的分布是乱的校准结果根本没参考价值。这属于文件里写了建议关掉数据增强但很多人没当回事最后精度掉了到处找原因其实根子在这里。2.2 权重和激活的分布差异为何YOLO26的PTQ格外容易出问题权重分布通常比较稳定、集中量化起来相对安全。但激活分布不一样它跟输入数据、网络结构强相关。YOLO26有几个典型特点直接导致PTQ难度比上一代更高第一注意力模块中的数值范围极大。YOLO26引入的注意力机制计算过程中会产生一些数值很大的中间激活值极端情况能到几十甚至上百而正常路径上的激活值大多在个位数以内。这种分布做均匀量化性价比极低。比如绝大多数数值落在-2到2之间但有一小撮跑到80你算出来的scale会被拉到很大最终有大量的量化档位被浪费-2到2这段区间的量化误差明显增大。第二跨层特征融合导致激活分布多峰。YOLO26的不同尺度特征融合做得更深这会让某些层的激活值呈现明显的多峰分布甚至出现数值集中在一头的偏态分布。均匀量化本质上假设数据是均匀分布的遇到多峰分布误差立刻变大。第三检测头回归分支对误差非常敏感。YOLO26的检测头里有直接输出目标坐标偏移量的部分这几个分支的数值范围很小通常在0到1之间但它们对量化误差的容忍度极低。坐标偏差一点点最后的bbox框位置就会有可感知的偏移尤其是小目标。所以第一轮用常规的TensorRT/ONNX Runtime PTQ做下来我的实测结果是FP32 mAP大概0.742直接PTQ INT8掉到0.691掉了5个百分点对于目标检测模型来说这个损失非常大。尤其是小目标AP从0.418直接崩到0.336几乎不能用。这个结果提醒我这个模型不做QAT是不太可能恢复到可接受精度的。2.3 如果PTQ精度可接受怎么把细节做对也不是说PTQ完全不能用。如果你的精度要求没那么苛刻比如内部Demo、效果展示、或者本来模型精度余量就很大的场景PTQ里有几个技巧可以把损失压到最小。选好校准集样本数量500张起步内容多样包含各种目标尺寸和场景。校准算法可以试一下百分位模式percentile calibration而不是默认的max校准。因为max校准过于敏感离群值直接拉爆scalepercentile选择99.99%位置的值适当截断尾部离群值效果往往好很多。敏感层识别用推理引擎提供的层级分析工具逐个看量化后哪些层损失最大对这些层做跳过量化或保持FP16。混合精度比如检测坐标回归分支保持不变其他特征提取层走INT8。实测混合精度至少能挽回一半以上的精度损失。如果你不想折腾这么多但模型又必须压到最小体积那就直接上QAT一开始就别指望PTQ了。3. QAT原理深挖为什么假装量化能让模型精度回升QAT全称Quantization-Aware Training翻译成量化感知训练。它的核心思路特别有意思在训练过程中把量化的误差模拟进去让模型在训练阶段就适应这种精度损失最后交付推理时精度自然不会崩。这个模拟是怎么做的呢关键操作叫做伪量化fake quantization。在训练的前向传播时把每一层的权重和激活值先做一次量化-反量化的操作即从FP32量化到INT8再反量化回FP32参与计算。这样模型看到的数据已经带了量化噪声梯度更新时它就会想办法去适应这个噪声学习到对量化更鲁棒的参数。反向传播的时候量化操作本身虽然不可导存在一个round取整操作连续不可导但实际实现时使用了一个巧妙的方案——STEStraight-Through Estimator直通估计器。核心思想是forward的时候该取整就取整该截断就截断backward的时候梯度直接穿透过去当成没有量化操作一样进行计算。这样既完成了模型感知量化噪声的目标又不影响梯度的正常回传。很多不熟悉QAT的人会以为QAT就是带量化误差微调模型但实际QAT更贴切的描述是在逼近INT8约束条件下重新寻找最优解。它的本质是在原始损失函数基础上增加了量化噪声这个约束让模型参数向量化友好区域移动。用生活化类比你穿着一双崭新的硬底皮鞋去跑步跑久了脚肯定磨出水泡。QAT相当于先穿着鞋在沙地上跑一段磨掉尖刺、让鞋适应脚型后再上赛道成绩反而更稳。而PTQ则是直接穿上新鞋比赛能不能跑出好成绩全看鞋子本身合不合脚。3.1 训练超参数学习率、epoch、batch size的取舍QAT训练不能按普通训练的流程重新来一遍它的处理方式与此不同。这是个非常典型的坑刚开始我直接用正常的训练配置跑QAT结果过拟合严重精度不升反降。QAT通常是在已经收敛的FP32模型上继续训练所以学习率必须大幅降低。我的经验是使用初始学习率的1/10到1/20比如原本初始lr是0.001QAT阶段用0.00005到0.0001之间。这个阶段不需要模型大幅探索新区域只需要在局部范围内微调权重让它们适应量化噪声。epoch的设定QAT训练不宜太长通常2到5个epoch就够了。训练太长会破坏原始模型的泛化能力训练太短又不足以让模型充分适应量化噪声。我试过1个epoch效果不够稳定5个epoch之后精度提升基本饱和再往上加时间成本反而拖累推理效果。batch size方面考虑到显存和BN层Batch Normalization统计的稳定性QAT阶段建议保持和原始训练一致的batch size。尤其要注意的是如果QAT阶段改了batch size模型的BN统计量会波动量化后的scale稳定性也会受影响精度可能瞬间掉一截。如果非要改batch size记得调整BN的momentum参数给统计量更多缓冲空间。还有一个特有的动作QAT阶段应该把BN层折叠到卷积层至少在做推理部署导出时必须要做。很多工程师在训练时没注意这一点导出推理模型后精度OK但实际部署后却明显变差很可能就是BN没折叠或者折叠方式不对。现在的主流量化框架比如TensorRT、OpenVINO在导出时通常会做BN折叠但你最好自己确认一下。3.2 在YOLO26上做QAT的完整流程拿YOLO26实际走了一遍QAT我用的方案是基于PyTorchimport torch import torch.nn as nn from pytorch_quantization import quant_modules # 关键步骤1初始化量化插件在模型构建前调用 quant_modules.initialize() # 关键步骤2构建YOLO26模型启用量化版模块 model YOLO26Model(num_classes80, deployFalse) model.load_state_dict(torch.load(yolo26_fp32_best.pt), strictTrue) # 关键步骤3把模型放到GPU上开启训练模式 model.cuda() model.train() # 关键步骤4把模型转换为QAT模式默认只量化卷积层可以自行调整 model.enable_qat()这里有一个细节quant_modules.initialize()必须在模型实例化之前调用因为这一步会去monkey patch PyTorch里的Conv2d、Linear这些基础模块把它们替换成带量化操作的版本。如果顺序搞反了后面所有层都不会被量化。然后开始训练optimizer torch.optim.SGD(model.parameters(), lr0.00005, momentum0.937, weight_decay0.0005) scheduler torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max5) for epoch in range(5): for batch_idx, (imgs, targets) in enumerate(train_loader): imgs imgs.cuda() targets [t.cuda() for t in targets] output model(imgs, targets) loss output[loss] optimizer.zero_grad() loss.backward() optimizer.step() if batch_idx % 50 0: print(fEpoch {epoch}, Batch {batch_idx}, Loss {loss.item():.4f}) scheduler.step()训练结束后关键步骤来了——导出量化模型。这里不能直接保存torch的checkpoint因为QAT模型的参数虽然还是FP32但已经包含了量化的scale和zero point信息。你需要把伪量化模块里的数值冻结即设置enable_fixed_calib再导出为ONNX或直接让推理引擎读取。# 冻结校准参数 model.eval() model.apply(pytorch_quantization.nn.TensorQuantizer.disable_fake_quant) # 导出ONNX dummy_input torch.randn(1, 3, 640, 640).cuda() torch.onnx.export(model, dummy_input, yolo26_qat.onnx, opset_version13, verboseFalse)这里值得提醒的是disable_fake_quant这个操作如果漏了导出的模型里还是带伪量化的版本推理框架可能无法正确识别数值或多或少会出问题。这个API在不同版本的名字略有不同比如有的版本是disable_quant有的叫enable_fixed_calib最好看一下你用的量化库的版本说明。3.3 带着量化感知去调整模型结构什么样的结构更好量化做完一轮QAT后我的精度恢复情况比较理想mAP从0.691PTQ恢复到0.734跟FP32的0.742已经非常接近小目标AP也从0.336恢复到了0.405。区别还是很明显的说明QAT这条路走对了。但如果你在QAT后精度仍然有明显下降还有一个思路值得考虑从模型结构层面做量化友好改造。不是所有模块都适合INT8尤其是那种数值分布特别离谱的层可以考虑把它们替换为量化更友好的变体。比如某些极深网络里的shortcut层如果做element-wise add的两个分支数值范围相差过大量化后的加法结果会有较大误差。一种做法是调整残差分支上的scale对齐方式另一种做法是把这个层单独标记为不量化保持FP16计算通常会好一些。YOLO26这部分有一个比较麻烦的结构——注意力模块中的softmax。softmax输出的分布天然是0到1之间的非常集中的分布理论上量化问题不大但它前一步的矩阵乘法结果QK^T的数值范围可能巨大量化误差会直接传导到softmax里。如果遇到这个问题需要考虑对QK^T这个中间张量单独做更细粒度的量化或者在量化时将其保留为FP16。4. 校准与精度调优scale怎么算、敏感层怎么定位、精度怎么验证量化的核心参数就是scale和zero point校准算法的选择直接影响最终精度。这里详细展开几种常见校准方法的差异以及怎么定位敏感层。4.1 校准算法对比max、percentile、entropy的取舍校准算法的目标就是根据统计到的激活值分布确定每个张量的数值范围min和max有了min/maxscale和zero point就顺理成章了。不同算法的差异在于怎么确定这个范围。max校准直接取观测到的最大值和最小值作为范围。实现最简单但遇到离群值outlier时表现很差。前面说过的激活分布里偶尔冒出的极端值会直接把其他正常数值的精度壓掉。percentile校准取99.9%或99.99%的分位点作为max主动丢弃一部分尾部极值换来主体分布的更高精度。这个策略在很多场景下最实用缺点是如果丢弃尾部数据恰好是目标检测里的小目标信号精度反而会掉。entropy校准KL散度在信息论意义上寻找最优截断位置最小化量化前后的信息损失。这个方法最精细但需要计算分布直方图速度慢一些。TensorRT的默认校准模式本质上就是这种思路。对于特征分布极其不均匀的层entropy方法明显优于percentile和max但耗时长适合校准离线分析时使用。我自己的建议是优先尝试percentile的99.9%如果没有明显收益再上entropy。不要一上来就用max除非数据集简单模型结构也简单比如简单的分类模型。4.2 敏感层定位的实操方法逐层替换量化并观察损失如果QAT都做了还是有个别层精度拉胯那就需要用逐层敏感度分析来找出这些层。实操方法并不复杂先整体量化然后从某一层开始把量化取消掉恢复FP32其他层保持量化观察精度是否回升。如果回升很大说明这一层就是敏感层。按这个思路可以对所有层做一遍有些量化框架会自动做按敏感度从高到低排序。理论上如果你把所有敏感层都恢复FP32精度可以无限接近原始模型但这显然不符合模型压缩的目标。实际做法是设置一个精度阈值比如比FP32低0.5个mAP在满足阈值的前提下尽量让更多层保持量化只挑出贡献最大的那1到2层做混合精度。我跑YOLO26时最终确定了三个敏感区域第一个是SPPF模块后面那几层特征值变化幅度大第二个是检测头前面的融合层多尺度信息叠加导致分布范围极大第三个就是前面说过的坐标回归分支。这三个区域在QAT后依然有微小的精度回调问题最后我选择在导出时将它们保持FP16其余所有层INT8最终得到的mAP是0.740跟FP32的0.742几乎无差异。4.3 精度验证的正确姿势不要只看mAP通常做量化后评估大家习惯性地看一眼mAP就完事了。但目标检测模型有个特殊问题mAP是一个聚合指标它把不同类别、不同尺寸目标的精度都揉在一起了。如果你的模型主要部署在特定场景比如工业质检、自动驾驶只关注mAP会掩盖很多细节问题。更好的做法是分维度评估按目标尺寸分别算AP小目标AP、中目标AP、大目标AP重点关注小目标变化。按类别分别算AP看看是不是某个特定类别精度掉了。挑出100张典型的测试图用量化前后模型的推理结果做可视化对比专门盯着边界框偏移情况。量化后最常见的现象就是边界框整体偏移几个像素或者置信度评分稍微浮动这些在mAP里不一定体现得明白但是窗体端显示出来会非常明显。我遇到过一种情况量化后模型的mAP只掉了0.3但可视化结果里一些细长目标比如一根棍子、一支笔的检测框明显缩短了一截。这就是因为该目标的高宽比特征在量化中被拉偏了mAP反而没怎么提示。如果不做可视化对比这种问题很难被发现。5. 部署落地的几个细节导出格式、推理引擎差异、动态尺寸与延时优化量化做完不是终点真正部署到端侧或边缘设备还有一系列细碎问题。YOLO26的部署我试了两种主流路线NVIDIA系列用TensorRTRK系列用RKNN。5.1 导出为ONNX时的注意事项PyTorch模型导出ONNX看起来简单但涉及量化模型时有几个特别的要点固定输入尺寸与动态轴。量化模型尤其是INT8模型在推理引擎内部已经是静态图了动态输入尺寸会限制优化空间甚至直接降低推理效率。我不是说不能支持任意尺寸而是建议导出时固定输入尺寸如640x640或1280x1280在预处理环节用letterbox把输入缩放到固定尺寸。动态尺寸的INT8部署不是不能做但复杂度高不少优先固定尺寸更稳妥。确保模型中不使用动态控制流if语句依赖张量数值等ONNX转换时这种结构极容易出问题。YOLO26总体来说结构上不复杂转换比较顺利但如果你自己魔改了模型需要注意这块。检查模型输出个数与名称。YOLO26的导出输出通常是三个尺度的检测结果大中小目标各一份导出后要在ONNX Runtime或TensorRT里逐一对应别搞错映射关系。5.2 TensorRT和RKNN上的实验结果对比在NVIDIA Jetson Orin上我用TensorRT跑了FP32、FP16、INT8三档对比配置推理耗时 (ms, batch1)mAP模型体积FP326.30.74262MBFP162.80.74131MBINT8 (PTQ)1.70.69116MBINT8 (QAT)1.70.73416MBINT8 (QAT混合精度)1.90.74018MB这个表格很直观地展示了量化带来的收益INT8的推理耗时大约是FP32的27%体积缩小到四分之一精度几乎无损。混合精度耗时多0.2毫秒但换来了额外的0.006 mAP提升在追求极致吞吐的场景是值得的。在RK3588上用RKNN跑QAT导出的模型效果也不错INT8精度mAP 0.730耗时6.2ms这个和TensorRT的数值差异主要是因为不同芯片平台本身的算力差异不代表量化方案有问题。但要注意的是不同推理引擎对同样一个ONNX模型的优化策略不同量化参数也未必完全一致。TensorRT的INT8推理和RKNN的INT8推理底层对scale的处理、对每通道/张量量化的支持程度都有差异。所以不要指望在一个引擎里量化调试好迁移到另一个引擎直接跑就能同样精度。换引擎要重新跑一遍校准或验证流程这是必须的步骤。5.3 没有GPU的环境怎么跑QATCPU也能救急QAT训练虽然比全量训练轻量但也不是完全没资源就能跑。如果只有CPU可以考虑两个降级方案第一只对部分层做量化感知训练比如只训练检测头部分其他层冻结freeze。第二降低输入分辨率来训练比如从640降到416量化感知训练的核心是让模型学会适应量化噪声分辨率带来的信息差距可以靠后续在原始分辨率上完成推理来弥补。我在某次用CPU调试YOLO26时就是这样做的QAT收敛确实比GPU慢很多但效果依然能用。注意这个分辨率降级方法有一个前提你得确定模型对输入分辨率不敏感。对于YOLO26这种本身就在多尺度上做特征融合的模型分辨率影响的可能不只是精度还有anchor的匹配方式降级前最好做一次对照组验证。6. 终版方案与操作复盘耗时、精度、部署结果一图看全整套流程跑完后的最终配置方案模型YOLO26输入尺寸640x640类别数80量化方案QAT 混合精度敏感层保持FP16其余全INT8推理引擎TensorRT 8.6在NVIDIA Jetson Orin上跑的校准集从训练集中随机抽取800张图覆盖全类别、全尺寸预处理完全对齐训练流程训练时间大概花了GPU约15分钟5个epochbatch size 32单卡RTX 4090整个精度调优时间大约占了两天主要时间花在敏感层定位和反复校准验证上。整个流程的时间分配大约是这样的环节耗时说明PTQ快速验证0.5天了解量化基线和掉点严重程度校准集准备0.5天从训练数据里筛选、预处理对齐QAT训练0.5天5个epoch训练很快敏感层定位1天逐层分析、混合精度组合验证部署验证0.5天跨引擎验证可视化对比最终部署效果在Jetson Orin上处理1080p视频流输入缩放为640x640单路视频稳定跑到25-30 FPS四路视频同时推理也能保持在15 FPS左右精度在各种光照和天气条件下表现稳定mAP为0.740和原始FP32模型几乎无差。对比之前直接用PTQ方案时小目标漏检严重的情况QAT确实把模型从勉强能用拉回到了部署稳定的状态。7. 回头总结一下量化中最容易忽略的几件事这篇文章写到最后把我这次完整跑一遍量化的几个最重要心得单独列出来这些是最容易踩到的坑也是经常被忽略的地方值得再三强调。第一校准集不是随便抽几百张图就行。它决定了所有层的量化参数它的质量甚至比QAT训练还重要。如果校准集分布偏了后续所有环节的精度都会受限。第二QAT的学习率一定要小epoch一定要短。把QAT当正常训练来跑只会把模型跑废掉而且很难察觉因为训练损失可能在下降但验证集上的mAP已经崩了。第三BN层折叠一定要在导出前处理干净。不折叠或者折叠时机不对导出后在PC上测试精度正常部署到设备上精度就出问题排查起来最耗时。很多人会在这个问题上兜圈子。第四小目标AP是最敏感的量化指标。如果只看mAP不分尺寸看量化优化了半天可能根本没有优化到点上。建议每次都分小/中/大三类AP来看小目标不掉大目标基本也不会出问题。第五引擎换了量化精度就得重验。TensorRT上验证OK的模型换到RKNN或OpenVINO不要直接照搬结论各自平台的INT8实现细节和处理方式不完全一致多少都有差别最好每次都跑一遍完整的精度验证流程。量化这条路不算难但细节多、坑也多。如果你正准备对YOLO26做部署优化建议直接按这套流程走先快速PTQ摸底再QAT补精度最后混合精度收尾。只要耐心把敏感层定位做仔细INT8部署的收益远大于那几天的调试成本。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑