1. 为什么 NVFP4 推理精度恢复值得单独写一篇大模型部署这件事走到推理优化这一步量化基本是绕不开的坎。FP8 已经在不少生产环境里跑通了大家尝到甜头之后自然想再往下压——FP4 就是下一个目标。NVIDIA 在 Blackwell 架构上推的NVFP4格式把每个权重压到 4 bit显存占用直接砍到 FP16 的四分之一这对动辄几十上百 GB 的模型来说诱惑太大了。但问题也随之而来4 bit 的表示能力实在太有限直接做 post-training quantizationPTQ精度掉得让人心疼尤其是 reasoning 类任务和长上下文场景输出质量肉眼可见地崩。我最近在读的一篇工作就是冲着这个痛点去的——面向 NVFP4 推理精度恢复的量化感知蒸馏Quantization-Aware DistillationQAD。它的核心思路不复杂既然低比特量化会破坏模型已经学到的知识分布那就让量化后的学生模型去对齐全精度教师模型的输出分布用蒸馏的方式把丢掉的精度找回来。听起来像是 QAT 和 KD 的结合体但真正落地时NVFP4 这个格式本身的特殊性、蒸馏 loss 的设计、教师学生之间的精度 gap 处理每一步都有讲究。这篇内容适合谁看如果你正在做 LLM 或 VLM 的推理部署手头有 Blackwell 或计划上 Blackwell 的卡想搞清楚 FP4 到底能不能用、怎么用才不掉点那这篇值得花时间。如果你只是听说过量化感知蒸馏这个词想弄明白它和普通 PTQ、QAT 的区别我也会从最基础的地方讲起。我会尽量把论文里的关键设计拆开补上实操时容易踩的坑以及我自己在类似流程里总结的一些经验。2. NVFP4 格式与量化感知蒸馏的核心逻辑拆解2.1 NVFP4 到底特殊在哪为什么不能照搬 INT4 那套很多人第一次接触 NVFP4 会下意识把它当成另一种 4 bit 量化然后拿 INT4 的经验往上套结果发现不对劲。NVFP4 是浮点格式不是定点。它由三部分组成1 位符号位、2 位指数位、1 位尾数位。这个 E2M1 的布局意味着它能表示的数值是离散的一组浮点数动态范围比 INT4 大但精度分布是非均匀的——靠近零的地方密远离零的地方稀。这就带来一个直接后果NVFP4 对权重的缩放scaling策略极其敏感。INT4 时代常用的 per-channel scale 在 NVFP4 上不一定最优因为浮点的指数位本身就在做动态范围适配。论文里通常会采用分块缩放block-wise scaling比如每 16 或 32 个元素共享一个 scale factor这个 scale 用 FP8 存储。为什么要分块因为一整层共享一个 scale遇到权重分布跨度大的层小值全被压成零大值又溢出分块之后每块的动态范围收窄量化误差自然下降。我实测下来的感受是block size 的选择是个权衡block 越小scale 开销越大额外存储和计算但精度越好block 越大压缩率越高但精度损失明显。论文里一般会在 16 和 32 之间做消融具体选哪个要看你的模型和任务对精度的敏感度。另外要注意NVFP4 的 scale 本身也是量化过的FP8这层量化的量化会引入额外误差蒸馏的时候必须把这个因素考虑进去否则教师和学生的分布对齐会偏。2.2 量化感知蒸馏和普通 PTQ、QAT 的本质区别先把三个概念摆清楚不然后面容易混。PTQPost-Training Quantization模型训练完了拿一批校准数据跑一遍统计权重和激活的分布直接算出 scale然后把权重round 到低比特。快但精度损失不可控4 bit 下尤其明显。QATQuantization-Aware Training在训练过程中模拟量化误差让模型提前适应低比特表示。精度好但需要完整的训练流程、大量数据和算力对已经上线的模型来说成本太高。QADQuantization-Aware Distillation介于两者之间。它不重新训练整个模型而是用蒸馏的方式让量化后的学生模型去拟合全精度教师模型的输出。关键区别在于QAT 是让模型适应量化QAD 是让量化模型去模仿一个没被量化破坏的参照物。前者靠数据标签后者靠教师的软标签soft label信息量更大收敛也更快。论文里之所以选 QAD 而不是 QAT我理解主要是两个原因。一是成本QAD 通常只需要少量校准数据几千条样本级别和很短的蒸馏步数就能恢复大部分精度二是 NVFP4 的量化误差不是简单的均匀噪声它和权重分布强相关用教师的输出分布做监督比用硬标签更能捕捉这种结构性误差。2.3 教师模型怎么选学生模型怎么初始化这一步是很多人容易忽略但实际影响很大的地方。教师模型理论上越强越好但有个前提教师和学生的架构必须一致或高度兼容。你不能拿一个 70B 的教师去蒸馏一个 7B 的学生中间隔着架构差异输出分布对不齐蒸馏反而会带偏。论文里的做法通常是教师就是同一个模型的全精度版本FP16 或 BF16学生是这个模型经过 NVFP4 量化后的版本。这样两者的参数空间是对应的蒸馏时可以直接做逐层或逐输出的对齐。学生初始化有两种常见策略一种是从量化后的权重直接开始PTQ 初始化另一种是从教师权重的量化副本开始。前者更贴近最终部署形态后者收敛更稳。我倾向于先用 PTQ 初始化跑一版 baseline看看掉点情况再决定蒸馏的力度。还有一个细节教师模型要不要冻结答案是必须冻结。蒸馏过程中只更新学生的参数或者只更新 scale 相关的参数教师只做前向推理提供软标签。如果教师也参与更新整个流程就退化成自训练了失去参照意义。3. 蒸馏流程的关键细节与实操要点3.1 损失函数设计不只是 KL 散度那么简单蒸馏的 loss 设计是这篇工作的核心之一。最朴素的做法是用 KL 散度对齐教师和学生的 logits 分布但 NVFP4 场景下这样不够。原因在于量化误差不仅影响最终输出还影响中间层的激活分布。如果只对齐最后一层中间层的误差会累积尤其在深层网络里。论文里一般会组合多种 loss输出层 KL 散度对齐 logits这是基础。中间层特征对齐用 MSE 或 cosine similarity 对齐某些关键层的 hidden states。选哪些层通常是 attention 输出和 FFN 输出这两处对量化最敏感。任务特定 loss如果是 VLM还要加上视觉-语言对齐部分的 loss否则量化可能破坏跨模态的语义映射。权重怎么分配我的经验是输出层 loss 占大头0.6-0.8中间层辅助0.2-0.4任务 loss 视情况微调。温度系数 T 也很关键T 越大软标签越平滑学生学到的分布信息越丰富但太大又会模糊类别边界。论文里常用 T2 到 T4我实测 T3 在多数任务上比较稳。注意KL 散度的方向不能搞反。应该用教师分布去指导学生学习即 KL(teacher || student)而不是反过来。反了会导致学生把教师的错误也学过去。3.2 校准数据的选择与预处理QAD 对校准数据的依赖比 PTQ 小但也不是随便抓一批就行。数据要满足两个条件一是覆盖模型实际部署时的输入分布二是多样性足够避免蒸馏后模型在特定类型输入上过拟合。具体操作上我会从真实业务数据里采样 2000-5000 条按任务类型分层。比如做对话模型就按单轮、多轮、长上下文、代码、数学推理等类别各采一些。预处理时要注意和推理时的 tokenizer 行为完全一致包括 padding、truncation 策略。有个坑是校准数据的 max length 如果比推理时短蒸馏出来的模型在长输入上会崩因为量化 scale 是在短序列上统计的。另外校准数据不需要标签因为蒸馏用的是教师输出不是 ground truth。这一点让 QAD 比 QAT 灵活很多你可以用大量无标注数据。3.3 蒸馏步数与学习率调度蒸馏步数不是越多越好。NVFP4 的量化误差是结构性的蒸馏到一定程度后loss 下降会停滞再训就是浪费算力甚至过拟合。论文里通常几百到几千步就收敛具体看模型规模。学习率方面因为学生是从量化权重初始化的参数已经在低比特空间里了学习率不能太大否则会把量化结构破坏掉。我一般用 1e-5 到 5e-5 的量级配合 cosine decay 或 linear warmup decay。如果只更新 scale 参数不更新权重学习率可以再大一点因为 scale 的搜索空间更平滑。有个实操技巧分阶段蒸馏。先冻结权重只调 scale让量化误差先降一波再解冻部分层比如最后几层做微调。这样比一上来全解冻更稳也不容易过拟合。4. 完整实操流程与关键环节实现4.1 环境准备与依赖确认动手之前先把环境理清楚。NVFP4 的量化 kernel 目前主要在 NVIDIA 的 Transformer Engine 和相关推理框架里支持你需要确认GPU 架构Blackwell 及以上才原生支持 NVFP4 的硬件加速老卡只能模拟速度没优势。框架版本Transformer Engine、CUDA、PyTorch 的版本要匹配版本错位会导致 kernel 调用失败或精度异常。量化工具链确认你用的量化库支持 NVFP4 的 block-wise scaling有些库只支持 per-tensor那精度会差很多。我踩过的坑是不同版本的 TE 对 NVFP4 的 scale 布局定义不一样升级版本后旧的 checkpoint 可能加载不了。所以一旦选定版本整个流程里不要随意升级。4.2 量化配置与参数选择量化配置决定了学生模型的起点。关键参数包括参数常见取值影响block size16 / 32越小精度越高开销越大scale 格式FP8 E4M3 / E5M2E4M3 精度高E5M2 范围大量化对象仅权重 / 权重激活激活量化难度更大但收益也大校准样本数512 / 1024 / 2048越多越稳边际收益递减我的建议是先用 block size16、scale 用 FP8 E4M3、只量化权重跑一版看掉点情况。如果掉点可接受比如 perplexity 上升小于 5%就不用上激活量化如果掉得厉害再考虑加激活量化或缩小 block size。4.3 蒸馏训练的核心代码结构下面是一个简化的蒸馏训练循环展示关键步骤。实际代码会根据框架不同有差异但逻辑是通用的。import torch import torch.nn.functional as F # 教师模型全精度冻结 teacher.eval() for p in teacher.parameters(): p.requires_grad False # 学生模型NVFP4 量化后 student.train() optimizer torch.optim.AdamW( [p for p in student.parameters() if p.requires_grad], lr2e-5, weight_decay0.01 ) scheduler torch.optim.lr_scheduler.CosineAnnealingLR( optimizer, T_maxtotal_steps ) T 3.0 # 蒸馏温度 alpha 0.7 # 输出 loss 权重 beta 0.3 # 中间层 loss 权重 for step, batch in enumerate(calib_loader): input_ids batch[input_ids].cuda() attention_mask batch[attention_mask].cuda() with torch.no_grad(): teacher_out teacher( input_ids, attention_maskattention_mask, output_hidden_statesTrue ) student_out student( input_ids, attention_maskattention_mask, output_hidden_statesTrue ) # 输出层 KL 散度 kl_loss F.kl_div( F.log_softmax(student_out.logits / T, dim-1), F.softmax(teacher_out.logits / T, dim-1), reductionbatchmean ) * (T * T) # 中间层特征对齐 mse_loss 0.0 for s_h, t_h in zip(student_out.hidden_states[::4], teacher_out.hidden_states[::4]): mse_loss F.mse_loss(s_h, t_h) mse_loss / len(student_out.hidden_states[::4]) loss alpha * kl_loss beta * mse_loss optimizer.zero_grad() loss.backward() torch.nn.utils.clip_grad_norm_(student.parameters(), 1.0) optimizer.step() scheduler.step() if step % 100 0: print(fstep {step}, kl {kl_loss.item():.4f}, fmse {mse_loss.item():.4f})几个细节值得说T*T这个缩放是为了让梯度量级和温度解耦这是 Hinton 蒸馏里的经典处理。中间层对齐我用了[::4]隔层采样全层对齐计算量太大且收益有限。梯度裁剪是必须的量化模型的梯度容易爆。4.4 蒸馏后的评估与验证蒸馏完不能只看 loss要做端到端评估。评估维度包括Perplexity最基础的指标看语言建模能力有没有恢复。下游任务准确率MMLU、GSM8K、HumanEval 等看 reasoning 能力。长上下文表现用长文档 QA 或摘要任务测看量化有没有破坏长程依赖。生成质量人工抽检自动指标有时会骗人抽几十条输出人工看一遍。我一般会做三组对比全精度教师、PTQ 学生、QAD 学生。如果 QAD 能把 PTQ 的掉点恢复 70% 以上就算成功。恢复不到 50% 的话要么是蒸馏配置有问题要么是量化本身太激进得回头调 block size 或量化对象。5. 常见问题与排查技巧实录5.1 蒸馏后精度不升反降怎么办这是最常见的问题。原因通常有几个一是学习率太大把量化权重训崩了二是教师和学生的输入预处理不一致导致分布对不齐三是 loss 权重失衡中间层 loss 太大反而干扰了输出对齐。排查顺序先把学习率降到 1e-5 试一版如果还不行检查 tokenizer 和 padding 是否一致最后调 loss 权重。我遇到过一次是校准数据的 attention_mask 处理错了导致教师看到的是完整序列学生看到的是截断序列蒸馏完全跑偏。5.2 长上下文场景掉点严重NVFP4 的 scale 是在校准数据上统计的如果校准数据都是短序列长序列的激活分布会超出 scale 覆盖范围导致量化误差剧增。解决办法是在校准集里加入长序列样本或者对长序列单独统计 scale。另一个思路是分层设置 block size对 attention 层用更小的 block。5.3 蒸馏收敛慢或不收敛先确认教师模型是否真的冻结了有没有误开梯度。然后看 loss 曲线如果 KL 一直不降可能是温度 T 设得不对试试调大 T。如果 MSE 降但 KL 不降说明中间层对齐和输出对齐冲突了降低 MSE 权重。还有一种可能是学生模型的量化 scale 被冻结了导致它没法适应蒸馏目标这时候要解冻 scale 参数。5.4 常见问题速查表现象可能原因解决方向精度不升反降学习率过大 / 预处理不一致降 lr核对 tokenizer长上下文掉点校准数据太短加入长序列样本收敛慢温度不当 / loss 失衡调 T调权重显存溢出batch 太大 / 中间层对齐全开减 batch隔层对齐推理速度没提升kernel 未生效检查 TE 版本和 GPU 架构提示每次只改一个变量否则出了问题根本不知道是哪个改动导致的。量化蒸馏的参数空间很大系统性地做消融比盲目试要高效得多。5.5 几个我踩过的坑第一个坑是忽略了 scale 的精度。NVFP4 的 scale 用 FP8 存如果 scale 本身量化误差大权重的有效精度会进一步下降。后来我把 scale 改成 per-block 动态计算精度明显好转。第二个坑是蒸馏数据里混入了训练集。这会导致评估指标虚高实际部署时打脸。校准数据一定要和评估数据严格隔离。第三个坑是没做量化感知的初始化。直接拿全精度权重 round 到 NVFP4 再蒸馏收敛很慢。后来改成先用少量数据做一轮 scale 搜索类似 GPTQ 的思路再开始蒸馏收敛快了很多。6. 这套流程还能怎么扩展NVFP4 加 QAD 这套组合目前主要用在 LLM 和 VLM 的推理加速上但思路可以迁移。比如 MoE 模型不同 expert 的权重分布差异大可以给每个 expert 单独做量化和蒸馏。再比如多模态模型视觉编码器和语言解码器的量化敏感度不同可以分开处理视觉部分用更高比特语言部分用 NVFP4。另一个方向是把 QAD 和 LoRA 结合。蒸馏时只更新低秩适配器不动原始量化权重这样既能恢复精度又能保持部署时的结构不变。我试过在 7B 模型上这么做恢复效果和全参数蒸馏接近但显存占用小了一半。最后分享一个实际体会量化蒸馏不是万能药它能恢复的是量化引入的误差恢复不了模型本身就不会的东西。如果教师模型在某类任务上本来就弱蒸馏出来的学生只会更弱。所以选教师的时候先确认它在你的目标场景上足够强否则蒸馏就是在放大缺陷。