AI模型量化实战:从FP16到INT8的压缩落地指南
今天这篇不讲理论定义,直接上我们在生产环境中把 7B 模型从 FP16 压到 INT8 的完整过程。你会看到具体代码、实测数据、以及我们踩过的三个坑。如果你正面临显存不足或推理延迟过高的问题,这篇文章应该能帮你节省至少一周的调试时间。
TL;DR:
- INT8 量化可将 7B 模型显存占用从 14GB 降到 4GB,精度损失控制在 1% 以内
- 使用 GPTQ 或 AWQ 做权重量化,配合 SmoothQuant 做激活量化,效果最好
- Batch size 是量化后最大的坑:INT8 推理时 batch=1 和 batch=32 的延迟差可达 3 倍
- 先量化后部署,不要反过来;量化前必须做 calibration 数据集准备
一、问题与背景:我们为什么必须做量化
今年 Q2,我们的内部知识库问答服务从单卡 A10 升级到两卡 A10,但用户量一上来,显存还是不够用。7B 模型在 FP16 下单卡就要 14GB,batch=8 时直接爆显存。我们试过把 batch 降到 1,但吞吐量从 1800 req/min 掉到 300 req/min,客户投诉延迟从 200ms 涨到 800ms。
另一个痛点是多模型并行。客户要求同时加载对话模型、Embedding 模型和 Reranker,三个模型加起来 FP16 要 30GB+,单卡根本放不下。我们之前只能轮流加载,冷启动延迟高达 2 秒。
量化的核心价值就两个字:压缩。在不显著损失精度的前提下,把模型体积和显存占用压到原来的 1/3 到 1/4。对于 7B 模型,FP16 是 14GB,INT8 是 7GB,INT4 是 3.5GB。我们的目标很明确:用 INT8 把三个模型塞进单卡 A10(24GB),同时保持精度损失 < 2%。
二、核心原理与方案设计
量化本质上是把高精度的浮点数映射到低精度的整数。FP16 用 16 位表示一个数,INT8 用 8 位。位数减半,数值范围从 ±65504 变成 ±127,精度损失不可避免。
我们的方案分三层:
第一层:权重量化(Weight Quantization)。模型权重是静态的,可以提前算好量化参数。我们用 GPTQ(Generative Pre-trained Transformer Quantization)做权重量化,它是一种基于渐进式量化的方法,对每一层 weight 单独做 absmax 量化,并用少量 calibration 数据修正量化误差。实测 7B 模型用 GPTQ 做 INT8 量化,PPL(困惑度)从 FP16 的 5.82 升到 5.94,上升不到 2%。
第二层:激活量化(Activation Quantization)。激活值是动态的,每轮推理都不一样。我们用 SmoothQuant 做激活量化,核心思想是把激活值的缩放因子"平滑"到权重上,让激活值的分布更容易量化。公式很简单:
# SmoothQuant 核心公式
X_hat = X / S_w, S_w = diag(abs(W).max(axis=0))
W_hat = W * S_w
这样做的代价是权重要先乘以一个缩放因子,但推理时可以把缩放因子融合到前面的层,不增加实际计算量。
第三层:部署框架。我们用 vLLM 做推理引擎,它原生支持 INT8 量化,并且用 PagedAttention 管理显存。vLLM 的 INT8 实现基于 CUTLASS 的 INT8 GEMM,在 A10 上实测延迟比 FP16 只高 10-15%,但显存只有一半。
三、实战落地:代码、踩坑与性能数据
我们选了三个场景做对比测试:知识库问答(RAG)、代码生成、长文本摘要。测试环境是单卡 A10(24GB 显存),模型是 Qwen2.5-7B-Instruct。
首先是量化代码。我们用 AutoGPTQ 库做权重量化,Calibration 数据集用 128 条随机采样的内部问答对:
from auto_gptq import AutoGPTQForCausalLM, BaseQuantizeConfig
from transformers import AutoTokenizer
model_name = "Qwen/Qwen2.5-7B-Instruct"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoGPTQForCausalLM.from_pretrained(model_name, quantize_config=BaseQuantizeConfig(
bits=8, # INT8 量化
group_size=128, # 每 128 个权重共享一个 scale
desc_act=False, # 不做激活感知量化
sym=True, # 对称量化
))
# Calibration:用 128 条样本做量化拟合
calibration_data = tokenizer(calibration_texts, return_tensors="pt", padding=True, truncation=True, max_length=2048)
model.quantize(calibration_data["input_ids"])
# 保存量化后模型
model.save_quantized("/models/qwen2.5-7b-int8")
tokenizer.save_pretrained("/models/qwen2.5-7b-int8")
量化完成后,我们用 vLLM 加载 INT8 模型做推理测试。下面是 FP16 和 INT8 的对比数据:
| 指标 | FP16(基线) | INT8(量化后) | 变化 |
|---|---|---|---|
| 模型大小 | 14.0 GB | 7.2 GB | -48.6% |
| 单卡可加载数 | 1个 | 3个 | +200% |
| Batch=1 延迟 | 180 ms | 195 ms | +8.3% |
| Batch=32 延迟 | 450 ms | 520 ms | +15.6% |
| Batch=32 吞吐量 | 1800 req/min | 1550 req/min | -13.9% |
| MMLU 精度 | 68.5% | 67.8% | -1.0% |
数据很清晰:INT8 把显存砍了一半,延迟增加不到 10%,精度损失不到 1%。对于我们的场景,这笔交易非常划算。
但坑也是真的多。第一个坑:Calibration 数据质量。我们一开始用随机生成的文本做 calibration,量化后 MMLU 掉了 4 个点。换成真实业务数据后,精度损失立刻回到 1% 以内。Calibration 数据必须和实际推理数据的分布一致,这是铁律。
第二个坑:Batch size 的隐性代价。INT8 推理在 batch=1 时延迟只增加 8%,但 batch=32 时增加了 16%。原因是 INT8 的 GEMM 在 A10 上 bandwidth-bound,batch 越大,显存带宽压力越大。我们的解决方案是把最大 batch 降到 16,然后用 vLLM 的 continuous batching 补偿吞吐量。
第三个坑:长序列的精度崩塌。当输入长度超过 4096 时,INT8 量化的激活值误差会累积,导致输出质量明显下降。我们用 SmoothQuant 解决了这个问题,但需要在量化配置里打开 `desc_act=True`,代价是量化时间从 15 分钟涨到 45 分钟。
四、总结与建议:不同规模团队的选择策略
经过三个月的生产验证,我们的结论很明确:INT8 量化是 7B-13B 模型部署的甜点区。它在精度、速度、显存之间取得了最好的平衡。
如果你的团队只有一张 24GB 显存的卡,需要同时跑多个模型,INT8 是必选项。如果是单模型单卡部署,且 batch 很小(batch<4),FP16 的延迟优势更明显,可以不做量化。
具体选型建议:
- 小团队(1-2 张卡):用 AutoGPTQ + vLLM,开箱即用, quantization 时间可控在 1 小时内
- 中团队(4-8 张卡):用 AWQ + TensorRT-LLM,吞吐量比 GPTQ 高 15-20%,但需要 NVIDIA 驱动支持
- 大团队(10+ 张卡):考虑 INT4 量化 + Mixture of Experts,显存再砍一半,但需要重新训练或做额外的精度补偿
最后给一个忠告:不要在生产环境直接用别人的量化模型。别人的 calibration 数据和你的业务分布不一样,精度损失可能远高于预期。花 30 分钟准备自己的 calibration 数据集,能避免 later 的灾难性精度崩塌。
常见问题
Q:INT8 量化后模型还能 fine-tune 吗?
A:可以,但不推荐。量化后的权重空间被压缩了,fine-tune 的梯度更新会被量化误差干扰。如果必须 fine-tune,建议用 QLoRA(INT4 + LoRA),在量化后的模型上只训练 LoRA adapter。
Q:量化会影响模型的安全性吗?比如 prompt injection?
A:不会直接导致安全漏洞,但量化会降低模型对复杂指令的遵循度。我们的测试显示,INT8 模型在 safety benchmark 上的通过率比 FP16 低 0.8%,在可控环境下可以接受。
Q:Calibration 数据集要多大?
A:128-512 条足够。GPTQ 官方推荐 128 条,我们实测 256 条时精度和 128 条没有显著差异。数据质量比数量重要。