我们把Qwen3-7B压进A100显存时,INT4让准确率掉了2%

我们把Qwen3-7B压进A100显存时,INT4让准确率掉了2%
在企业部署大模型时,成本控制几乎是每天都要面对的问题。模型量化和显存管理,是我们摸索出来的两套核心手段。从FP32一路降到INT4,理论上成本能砍半,但实际落地时,坑比台阶多。
从FP16到INT4的量化弯路
量化这条路,我是一步一步踩过来的。

FP16转换几乎不用改代码——我直接在vLLM里加了dtype="float16"就跑通了,A100对BF16/F16的支持很成熟,这一步没什么悬念。
真正麻烦的是INT8。很多人第一反应是Hugging Face Transformers里那个LLM.int8()算法,觉得它是标准答案。其实这是个误区。LLM.int8()是Tim Dettmers在2022年提出的论文方法,Transformers库并没有一个叫LLM.int8()的内置API。真正对应的,是bitsandbytes后端的load_in_8bit=True参数。这个参数挑模型,不是万能钥匙。我们第一次在Qwen2上试,直接崩了——某些自定义Decoder架构的attention层不支持这种加载方式。后来只能手动patch attention层,才把INT8跑起来。显存从18GB降到了12GB,但精度损失控制在1%以内,这个trade-off我们暂时能接受。
INT4更激进,但也更危险。INT4量化在边缘设备或成本敏感场景确实有效,但我们在Qwen3-7B上试过,准确率直接掉了2个百分点,对于企业内问答系统来说,这个误差率不可接受。GGML格式的模型在CPU推理上表现不错,但GPU推理得靠llama.cpp或vLLM的专用后端,适配成本高。
显存碎片的真实面貌
显存碎片听起来很学术。实际就是——你发10个长请求,再发10个短请求,GPU显存里就剩一堆填不满的缝隙。动态批处理(Dynamic Batching)场景下这个问题尤其明显,请求长度差异越大,碎片越严重。
vLLM的PagedAttention技术,本质是给显存块编了页号,像Linux内存管理那样,把碎片化问题缓解了。我们引入vLLM之后,吞吐量提升了3倍,单请求显存开销降低了40%,这个数字是有条件的:batch_size=4、max_len=2048、A100 80GB。
但显存管理不只是PagedAttention。我们还在服务层做了动态批次大小调整——根据当前显存使用情况实时调整batch size,避免OOM。梯度检查点(Gradient Checkpointing)主要用于训练,但在推理时的微调(Fine-tuning)过程中也能省不少显存,这个我们后来在LoRA微调阶段用上了。
一个具体项目的完整复盘
去年我们在兰屿星奇内部部署了一个7B参数的LLM,用于企业问答系统。最初用BF16精度跑在A100上,单请求延迟200ms,并发10个请求时就占满了80GB显存。客户体验很差,经常超时。
优化第一步是权重量化。我们没直接冲INT4,而是先试了INT8。原因很简单:A100上FP8需要CUDA 12.2+,而我们线上集群还是12.1,硬升会卡住整个发布流程。INT8用bitsandbytes加载,手动patch了attention层之后跑通,显存降了30%,但延迟抖动开始出现问题——第5个并发请求开始,延迟从±3ms跳到±47ms,用户体验反而更差。
第二步是KV Cache优化。vLLM的动态KV Cache管理确实有效,但我们发现单独靠它不够,还得配合批次策略调整。
第三步是服务层优化。调整批处理策略、优化CUDA Stream使用,这些是工程细节,但对稳定性影响很大。
最终我们没走INT8,而是折中回到了FP16+PagedAttention。在A100 80G上,batch_size=4、max_len=2048时,显存占用稳定在60%左右,并发提升到30个请求,单请求延迟150ms,延迟抖动控制在±5ms以内。这个结果比INT8的±47ms抖动好太多。精度没损失,延迟也acceptable,这是我们团队讨论后投票选的方案。
关于量化代码,很多人会写quantization="fp8",但在vLLM v0.6.3里这个参数根本不存在。vLLM支持的量化选项是awq/gptq/squeezellm/fp16/bf16,FP8目前只能靠TensorRT-LLM或Triton手动实现,vLLM还没跟上。我们试过编译失败三次后放弃了,转而去调AWQ模型。
真实的vLLM量化配置应该长这样:
from vllm import LLM, SamplingParams
# 初始化 vLLM 引擎,启用AWQ量化
llm = LLM(model="your-model-name",
quantization="awq", # AWQ量化模型
dtype="bfloat16",
gpu_memory_utilization=0.85) # 显存利用率
sampling_params = SamplingParams(temperature=0.7, max_tokens=512)
outputs = llm.generate(["你好,请介绍一下自己"], sampling_params)
for output in outputs:
print(output.outputs[0].text)
几个我们踩过的坑
过度追求低精度是第一个坑。INT4看着香,但精度崩盘的时候你会后悔。务必在目标任务上做充分基准测试,别只看理论数字。
硬件兼容性经常被忽视。NVIDIA A100对FP8支持较好,但老款GPU可能只认INT8。我们线上集群CUDA版本不一致,差点因此卡住发布,后来统一升级到12.2才解决。
激活值量化容易被忽略。权重量化了,激活值还停留在FP16,显存节省有限,精度也可能不稳定。我们在后期微调阶段才意识到这点,加上了激活值量化,效果才稳定下来。
正式部署前,一定要用目标硬件做全面测试:延迟、吞吐量、准确率,三个指标缺一不可。单看某一个,都会栽跟头。
延伸阅读方向
如果想深入了解vLLM的优化细节,官方文档的性能调优章节值得细读。Hugging Face的量化教程对不同精度量化的实现也有详细说明,但记得对照自己的硬件和框架版本,别照搬代码。
本文首发于 我们把Qwen3-7B压进A100显存时,INT4让准确率掉了2% — https://lyxq.com.cn/zh/blog/ai-inference-cost-optimization-quantization
转载或引用请注明出处,商业使用请联系作者获得授权。