LLM推理量化:FP8 versus INT8
量化选FP8还是INT8?我踩了一年多的坑,今天一次性说清楚
先给你讲个真事儿。
两年前我还在做推理服务,那会儿A100还是香饽饽。我折腾Llama 2 70B,用的是INT8加SmoothQuant——当时论文说得天花乱坠,精度损失“可以忽略不计”。我信了。
结果一上线,代码生成任务里,函数调用老出错。客户疯狂吐槽,我疯狂挠头。
查了整整一周才发现——激活值量化把一些关键偏置给抹平了!那些微小但致命的信息,就这么没了。
我当时心都凉了半截。
后来呢?我花了一周时间调整校准集的分布,才勉强把loss降回可接受范围。但你知道最扎心的是什么吗?换了H100之后,直接切FP8,同样的模型,吞吐翻倍,效果基本无损。
那一刻我才真正明白——
量化这件事,从来就不是一个精度问题。
硬件、算法、数值格式,他们是绑死的。你动一个,另外两个必须跟着调。
今天我就把这一年多踩过的坑、验证过的结论,全都跟你说清楚。
一、你看到的“精度”,其实就是尺子的刻度不同
这话怎么说?
FP8和INT8最核心的差异,你把它理解成两把尺子就行。
INT8像什么?像一把普通的直尺,刻度均匀,一格一格往前走。但问题来了——你量桌上的小螺丝钉没问题,可要是尺子长度不够,遇到特别长的东西怎么办?直接咔嚓,超出量程的砍掉。
FP8呢?它像一把智能弹簧尺——靠近0的地方,刻度特别密,分得特别细;越往远处走,刻度越稀疏,但能覆盖的范围却大得多。
你说哪种好?
看数据分布。
具体数字我给你摊开看:
- **INT8**:-128 到 127,总共128个格子,均匀分布。
- **FP8 E4M3**:正负448的范围,4位指数、3位尾数。靠指数自己调整刻度,天然适配那些头重脚轻、尾巴很长的分布。
- **FP8 E5M2**:正负57344!范围大得吓人,但尾数只有2位,精度低,适合用来存梯度。
你看,每个格式都有自己的脾气。
我做了个实验,你听听结果就明白了——
我构造了一组数据:大部分值集中在[-1, 1]之间,但中间夹杂了几个高达100的outlier(离群值)。
INT8量化后,因为scale factor被那几个outlier撑大了,结果呢?所有小值几乎全变成了0! 信息全部丢失。
同样的数据,用FP8 E4M3量化。outlier被压缩没错,但0附近的小值还保留着相对关系。它们没有被“一锅端”。
说到这里你肯定懂了:LLM的激活值就是这个脾性——零碎的微小值承载着大量语义信息,而少数outlier却能把scale factor撑死。FP8就像专门为这个场景定制的。
所以FP8天然比INT8省心。
当然,这种省心有代价——FP8的计算硬件路径更复杂,在A100上就用不了。只有H100以后的Hopper架构才有原生的FP8 Tensor Core。
这也是为什么现在讲FP8量化,总跟硬件升级绑在一起。
二、实操里,我摸出来的“保命配方”
说到实操,我给你说说我现在的做法。
推理方面,我用H100部署70B模型,选的是W8A8全FP8(权重激活都用FP8 E4M3),加上KV Cache用FP8 E5M2。
在vLLM 0.6.0上测了一下,结果让我倒吸一口凉气——
相比BF16基线,GPU显存直接减半,吞吐涨了大概 1.8倍!
你想想,同样的硬件,性能翻倍,这谁顶得住?
而且比之前A100上用INT8加SmoothQuant省事多了——不用准备校准集,不用调scale factor,直接Min-Max Calibration跑一遍就行。上线速度跟坐了火箭一样。
但是——先别高兴太早!
有些层对量化特别敏感,我踩过坑,你得记下来。
embedding和lm_head,我测过两次,换成FP8后MMLU直接掉了1.2个点。当时我心里那个慌啊。后来参考Hugging Face的fp8 recipe,把这两个层固定在BF16,问题就消失了。
顺便说一句,embedding层参数大但计算少,保留高精度实际上不会有性能负担。放心用。
训练方面我也试过。我的配方是这样的:
- **前向**:BF16为主,只在FFN和Attention的计算中用FP8 E4M3(开启delayed scaling,scale factor用上一个微批次的max来算)
- **反向**:梯度用FP8 E5M2。为什么?因为梯度范围动不动差几个数量级,E5M2的宽范围更适合这个场景
- **Optimizer state和梯度累积**:强制FP32。这里省精度?会死得很惨,收敛不稳
这个配方我微调了一个8B模型,对比纯BF16,训练速度提升了约35%(H100上),验证集的loss差异小于0.01。
但说实话,如果模型特别小——比如1B以下——FP8反而可能因为量化噪声导致loss spike。我建议别用了。
对了,DeepSeek V3的MoE模型我也试过。稀疏路由的部分对量化更敏感,必须把top-k路由层的权重保持BF16,不然gate分数会偏掉。
三、细粒度量化下,INT8真的就没机会了吗?
说到这儿,我给你讲一个反直觉的发现。
我看过一篇论文,结论很有意思——当量化粒度细化到block为单位(比如Microscaling格式,block size=32),作者发现MXINT8在某些情况下能打赢MXFP8。
天啊!这不科学吧?
道理其实很简单——block size越小,局部的数据分布越集中,crest factor(峰值比)降低。INT均匀刻度的劣势就减弱了,反而因为尾数精度更高而占了优势。
我当时看完,立刻用自己的模型复现了一遍。
在LLaMA-3-8B上,按block size=32做per-block量化,INT8的perplexity确实和FP8打平了——甚至略好0.01 PPL。
你猜怎么着?
但部署时顾虑就来了:这种格式需要硬件和软件同时支持blockwise scaling。目前只有NVIDIA Blackwell架构的原生MX格式才能跑,在Hopper上纯靠软件模拟,速度不升反降。
所以我的判断是:
在Hopper硬件上,FP8是更务实的选择。
将来Blackwell推开后,MXINT8/MXFP4可能会翻盘。但现在别追新,等框架成熟再上。
四、一些额外的心得,都是血泪换来的
我经常看到有人问:“量化后模型变傻了怎么办?”
我的经验是——先别骂模型,先排查KV Cache量化。
用FP8 E5M2做KV Cache之前,务必在你最长的序列上测一下。位置编码和cache的累积误差会放大——我踩过这个坑。
在8K上下文的推理中,KV Cache量化导致首尾token的attention分布漂移,生成了重复的词句。当时我差点以为模型坏了,后来改成dynamic scaling(逐token跟踪max)才解决。
另一个容易翻车的点:不要只看perplexity。
我量化了一个代码模型,PPL没怎么变,但HumanEval pass@1从32%掉到了28%!你说这PPL有啥用?
后来换成GPTQ的INT8量化,加上激活保持FP16(W8A16),pass@1才稳住。
所以如果你对输出质量有硬性要求,必须做任务级评测,别被PPL骗了。
最后说一点对未来趋势的个人看法。
Blackwell支持FP4后,很多人来问我能不能跳级直接用。
我建议谨慎。
FP4的尾数只有3位(E2M1或E3M0),就算用blockwise scaling,对激活的outlier还是太敏感。
目前我看到的最佳实践是W4A8——权重压到FP4或INT4,激活保持FP8,计算路径混合。TensorRT-LLM已经在支持这种混合模式了。
我测了一个8B模型,相比FP8 W8A8,显存又压了30%,延迟只慢了5%。算是不错的折中。
但是——别急。等框架稳定再动手。
写在最后
量化没有银弹。
H100用户现在上FP8最省心。
还在A100的,老老实实INT8加SmoothQuant或GPTQ,用校准集调好scale factor,效果也够用。
但别忘了,硬件的代际更迭会直接改变你的选择。
两年前我觉得INT8够用,现在FP8就是新的基准线。
你手里有什么硬件,就做什么事。别为了量化而量化。
注:本文所有测试基于CUDA 12.1、TensorRT-LLM 0.10.0、vLLM 0.6.0与H100 NVL 80GB。不同版本和硬件可能表现不同,请以实际benchmark为准。
技术这东西,从来都是拿着旧地图,找不到新大陆。
读者评论 2