当前位置: 首页 > news >正文

大语言模型推理速度优化之模型量化实践

背景

模型压缩在尽可能保证模型性能的前提下,可以减少模型的资源占用,提高推理速度,对于实时在线的场景作用非常大。
在这里插入图片描述

基础名词解释

Prefill阶段:预填充阶段,该阶段会对输入的prompt中的所有token进行并行计算,同时缓存已经生成的输入token的KV键值对;

Decode阶段:解码阶段 ,会复用prefill阶段的KV缓存,逐个生成输出Token(每个输出Token都依赖前置的所有Token),由于有依赖关系,因此该阶段不可并行;

模型量化:模型量化是一种用于减少神经网络模型大小和计算量的技术,将模型参数(如权重或激活参数)从高精度数据类型(比如Float32)转换为低精度数据类型(如:int8或者fp4)。模型量化通过以更少的位数表示数据,可以减少模型尺寸,进而减少在推理时的内存消耗,并且在一些低精度运算较快的处理器上可以增加推理速度,同时尽可能的不降低模型性能。

Weight && Activations:Weight参数表示权重参数,Activations表示激活参数,我们有时会见到W8A16、W6A16这类数据,W8A16就表示权重8bit+激活16bit,W6A16就表示权重6bit+激活16bit。

理论知识

常见的数据类型

数据类型位宽符号位指数位尾数位十进制有效数字动态范围相对内存
FP64641115215-17位10^3088x
FP323218236-7位10^384x
BF16161872-3位10^382x
FP161615103-4位10^52x
FP8(E4M3)8143~1位10^21x
FP8(E5M2)8152<1位10^41x
INT882-3位10^21x
INT441位10^10.5x

模型量化对象

Weight:权重是模型在训练过程中学到的固定数值,它是最常见的量化对象。量化权重可达到减少模型内存占用空间。权重在训练完成后固定,数值范围与输入无关,可离线完成量化,通常相对容易量化;

Activation:激活值是模型在处理输入序列时,其前向传播过程中计算出的动态中间结果,这部分通常是内存占用的大头,因此量化激活不仅可以大大减少内存占用。更重要的是,结合权重量化可以充分利用整数计算获得推理模型性能的提升。但是激活输出随输入变化而变化,需要统计数据动态范围,通常更难量化;

KV Cache:这是Transformer模型在自回归生成文本时使用的一种优化技术。在自回归生成中,模型每次预测下一个词元时,都需要处理从开头到当前词元的整个序列。为了减少重复计算,我们可以把前面已经计算好的词元缓存起来,后续词元可以直接使用这部分缓存。当文本较长时,KV缓存会占据较大的内存空间。因此,量化KV缓存对于提高模型长序列生成的吞吐量至关重要;

Gradient:相对上面的量化对象,略微小众一些,主要用于训练场景。在训练深度学习模型时,提督通常是浮点数,量化提督可以在分布式计算中减少通信开销,同时,也可以减少反向传播时的开销。

模型量化分类

量化感知训练(QAT):在模型训练过程中加入伪量化算子,通过训练时统计输入输出的数据范围可以提升量化后模型的精度,适用于对模型精度要求较高的场景;其量化目标无缝地集成到模型的训练过程中。这种方法使LLM在训练过程中适应低精度表示,增强其处理由量化引起的精度损失的能力。这种适应旨在量化过程之后保持更高性能。

量化感知微调(QAF):在微调过程中对LLM进行量化。主要目标是确保经过微调的LLM在量化为较低位宽后仍保持性能。通过将量化感知整合到微调中,以在模型压缩和保持性能之间取得平衡。

训练后量化(PTQ):在LLM训练完成后对其参数进行量化,只需要少量校准数据,适用于追求高易用性和缺乏训练资源的场景。主要目标是减少LLM的存储和计算复杂性,而无需对LLM架构进行修改或重新训练。PTQ的主要优势在于其简单性和高效性。但PTQ可能会在量化过程中引入一定程度的精度损失。

特性训练后量化(PTQ)量化感知微调(QAF)量化感知训练(QAT)
介入阶段训练完成后训练中后期微调从零开始训练
精度损失中(3-5%)低(<1%)理论最低
时间成本分钟级GPU小时级GPU周级
硬件要求需支持低精度训练需定制硬件
开源支持AutoGPTQ, GGUFNNCF, BrevitasFP6-LLM代码库
产业应用★★★★★★★★★☆★☆☆☆☆

常见的量化方法

GPTQ(高精度训练后权重量化)

思想:仅量化权重,其中模型权重被量化为int4数值类型,而激活值则保留在FP16。GPTQ将权重分组为多个子矩阵,然后对每个子矩阵内的所有参数逐个量化,每个参数量化后,需要适当调整这个子矩阵内其他未量化的参数,来弥补量化造成的精度损失。GPTQ量化需要准备校准数据集。

优点:

  1. 量化速度较快
  2. 模型性能损耗较小(4位量化的情况下,仍能接近于全精度模型的性能)
  3. 内存占用大幅降低(量化后的175B模型可在单个A100-80GPU运行,未量化需5块GPU)
  4. 推理速度提升(在生成任务中,3位量化的模型在A100 GPU上实现了约3.25倍提速)

缺点:

  1. 依赖校准数据(数据质量差则效果差)
  2. 可能存在过拟合校准集,导致模型在校准集之外的场景下性能下降
  3. 只支持新显卡(Nvidia 30系以上)

AWQ(激活感知的权重量化)

思想:仅保护激活幅度最大的1%权重,此部分权重精度不变,将其余部分权重精度降低至4-bit。

优点:

  1. 泛化性强(不同场景表现稳定)
  2. 可以自动识别关键参数(无需人工指定)

缺点:

  1. 不支持复杂模型结构(比如MoE专家网络)
  2. 需简单调参

BitsandBytes(动态反量化)

思想:权重以int4/int8存储,计算时动态反量化为FP16。

优点:

  1. 无需校准或导出,可直接使用
  2. 支持4/8-bit混合精度

缺点:

  1. 存在性能损失,4-bit量化精度下降较明显
  2. 推理过程中的反量化会增加10%的延迟

QLoRA(量化低秩微调)

思想:基础权重使用4-bit存储,低秩适配器使用BF16精度进行训练

优点:

  1. 可极致降低模型微调使用的显存,比如微调65B模型仅需48G显存;

缺点:

  1. 低秩适配器需要手动调参
  2. 在某些任务上,QLoRA微调的模型精度可能比不上全量微调模型

模型评估

当模型量化压缩完成后,我们需评估新模型的性能。此时,我们可以借助https://github.com/EleutherAI/lm-evaluation-harness,一个开源的大语言模型评测框架。该框架集成了60多个LLM标准学术基准,包括数百个子任务以及变体。

安装

git clone https://github.com/EleutherAI/lm-evaluation-harness
cd lm-evaluation-harness
pip install -e .

使用命令行评测

# 评测Huggingface模型
lm_eval --model hf \--model_args pretrained=deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B,trust_remote_code=true \--tasks cmmlu,coqa \--device cuda:0 \--batch_size auto:1 \--output_path ./eval_out/DeepSeek-R1-Distill-Qwen-1.5B \--use_cache ./eval_cache# 评测本地模型
lm_eval --model vllm \--model_args pretrained=./models/DeepSeek-R1-Distill-Qwen-1.5B,trust_remote_code=true \--tasks cmmlu,coqa \--device cuda:0 \--batch_size auto:1 \--output_path ./eval_out/DeepSeek-R1-Distill-Qwen-1.5B \--use_cache ./eval_cache

动手实践

使用GPTQ量化DeepSeek-R1-Distill-Qwen-1.5B模型

硬件配置

  • GPU-12G
  • 运行内存-32G
  • CPU 6核12线程

工具介绍

https://github.com/vllm-project/llm-compressor/tree/main

使用教程:https://github.com/vllm-project/llm-compressor/blob/main/examples/quantization_w4a16/README.md

原始模型导入

from transformers import AutoTokenizer, AutoModelForCausalLMMODEL_ID = "meta-llama/Meta-Llama-3-8B-Instruct"
model = AutoModelForCausalLM.from_pretrained(MODEL_ID, device_map="auto", torch_dtype="auto",
)
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)

校准数据准备

from datasets import load_dataset# 数据越大,校准效果越好,但是量化过程的耗时也会增加
NUM_CALIBRATION_SAMPLES=16 
MAX_SEQUENCE_LENGTH=2048# Load dataset.
ds = load_dataset("HuggingFaceH4/ultrachat_200k", split=f"train_sft[:{NUM_CALIBRATION_SAMPLES}]")
ds = ds.shuffle(seed=42)# Preprocess the data into the format the model is trained with.
def preprocess(example):return {"text": tokenizer.apply_chat_template(example["messages"], tokenize=False,)}
ds = ds.map(preprocess)# Tokenize the data (be careful with bos tokens - we need add_special_tokens=False since the chat_template already added it).
def tokenize(sample):return tokenizer(sample["text"], padding=False, max_length=MAX_SEQUENCE_LENGTH, truncation=True, add_special_tokens=False)
ds = ds.map(tokenize, remove_columns=ds.column_names)

量化压缩代码

from llmcompressor import oneshot
from llmcompressor.modifiers.quantization import GPTQModifier# Configure the quantization algorithm to run.
recipe = GPTQModifier(targets="Linear", scheme="W4A16", ignore=["lm_head"])# Apply quantization.
oneshot(model=model, dataset=ds,recipe=recipe,max_seq_length=MAX_SEQUENCE_LENGTH,num_calibration_samples=NUM_CALIBRATION_SAMPLES,
)# Save to disk compressed.
SAVE_DIR = MODEL_ID.rstrip("/").split("/")[-1] + "-W4A16"
model.save_pretrained(SAVE_DIR, save_compressed=True)
tokenizer.save_pretrained(SAVE_DIR)

量化压缩耗时约10分钟,部分日志输出如下

(25/29): Propagating: 100%|█████████████████████████████████████████████████████████████████████████████████████████████| 256/256 [00:02<00:00, 127.97it/s]
(26/29): Calibrating: 100%|██████████████████████████████████████████████████████████████████████████████████████████████| 256/256 [00:09<00:00, 27.58it/s]
2025-06-15T22:03:28.826392+0800 | on_sequential_batch_end | INFO - Quantizing model.layers.25.self_attn.q_proj using 256 samples
2025-06-15T22:03:29.408727+0800 | compress | METRIC - time 0.58s
2025-06-15T22:03:29.409013+0800 | compress | METRIC - error 16848.74
2025-06-15T22:03:29.409136+0800 | compress | METRIC - GPU 0 | usage: 48.28% | total memory: 12 GB
2025-06-15T22:03:29.409202+0800 | compress | METRIC - Compressed module size: 4.77696 MB
2025-06-15T22:03:29.409326+0800 | on_sequential_batch_end | INFO - Quantizing model.layers.25.self_attn.k_proj using 256 samples
2025-06-15T22:03:29.932167+0800 | compress | METRIC - time 0.52s
2025-06-15T22:03:29.932492+0800 | compress | METRIC - error 1962.31
2025-06-15T22:03:29.932624+0800 | compress | METRIC - GPU 0 | usage: 48.28% | total memory: 12 GB
2025-06-15T22:03:29.932690+0800 | compress | METRIC - Compressed module size: 0.79616 MB
2025-06-15T22:03:29.932814+0800 | on_sequential_batch_end | INFO - Quantizing model.layers.25.self_attn.v_proj using 256 samples
2025-06-15T22:03:30.451121+0800 | compress | METRIC - time 0.52s
2025-06-15T22:03:30.451454+0800 | compress | METRIC - error 9955.87
2025-06-15T22:03:30.451579+0800 | compress | METRIC - GPU 0 | usage: 48.28% | total memory: 12 GB
2025-06-15T22:03:30.451649+0800 | compress | METRIC - Compressed module size: 0.79616 MB
2025-06-15T22:03:30.451778+0800 | on_sequential_batch_end | INFO - Quantizing model.layers.25.self_attn.o_proj using 256 samples
2025-06-15T22:03:30.976098+0800 | compress | METRIC - time 0.52s
2025-06-15T22:03:30.976397+0800 | compress | METRIC - error 12021.09
2025-06-15T22:03:30.976531+0800 | compress | METRIC - GPU 0 | usage: 48.28% | total memory: 12 GB
2025-06-15T22:03:30.976597+0800 | compress | METRIC - Compressed module size: 4.773888 MB
2025-06-15T22:03:30.976721+0800 | on_sequential_batch_end | INFO - Quantizing model.layers.25.mlp.gate_proj using 256 samples
2025-06-15T22:03:31.568360+0800 | compress | METRIC - time 0.59s
2025-06-15T22:03:31.568674+0800 | compress | METRIC - error 107954.09
2025-06-15T22:03:31.568797+0800 | compress | METRIC - GPU 0 | usage: 48.28% | total memory: 12 GB
2025-06-15T22:03:31.568864+0800 | compress | METRIC - Compressed module size: 27.84768 MB
2025-06-15T22:03:31.568991+0800 | on_sequential_batch_end | INFO - Quantizing model.layers.25.mlp.up_proj using 256 samples
2025-06-15T22:03:32.157024+0800 | compress | METRIC - time 0.59s
2025-06-15T22:03:32.157350+0800 | compress | METRIC - error 118589.96
2025-06-15T22:03:32.157465+0800 | compress | METRIC - GPU 0 | usage: 48.28% | total memory: 12 GB
2025-06-15T22:03:32.157542+0800 | compress | METRIC - Compressed module size: 27.84768 MB
2025-06-15T22:03:32.157675+0800 | on_sequential_batch_end | INFO - Quantizing model.layers.25.mlp.down_proj using 256 samples
2025-06-15T22:03:35.601911+0800 | compress | METRIC - time 3.44s
2025-06-15T22:03:35.603181+0800 | compress | METRIC - error 108456.48
2025-06-15T22:03:35.603345+0800 | compress | METRIC - GPU 0 | usage: 48.28% | total memory: 12 GB
2025-06-15T22:03:35.603418+0800 | compress | METRIC - Compressed module size: 27.84768 MB
(26/29): Propagating: 100%|█████████████████████████████████████████████████████████████████████████████████████████████| 256/256 [00:02<00:00, 126.78it/s]
(27/29): Calibrating: 100%|██████████████████████████████████████████████████████████████████████████████████████████████| 256/256 [00:09<00:00, 27.55it/s]
2025-06-15T22:03:46.915018+0800 | on_sequential_batch_end | INFO - Quantizing model.layers.26.self_attn.q_proj using 256 samples

模型压缩前的文件大小约为3.5G,压缩后的模型文件大小为1.5G

模型评估代码

from vllm import LLM
model = LLM("./Meta-Llama-3-8B-Instruct-W4A16-G128")
lm_eval --model vllm \--model_args pretrained="./Meta-Llama-3-8B-Instruct-W4A16-G128",add_bos_token=true \--tasks gsm8k \--num_fewshot 5 \--limit 250 \--batch_size 'auto'

量化前、后的模型评估结果如下

模型体积CoQAgsm8K
量化前3.5G0.42320.684
量化后1.5G0.42320.684

参考资料

  1. https://github.com/EleutherAI/lm-evaluation-harness
  2. https://temp53ai.uweb.net.cn/news/finetuning/2025040154176.html
  3. https://zhuanlan.zhihu.com/p/11886909512
  4. https://zhuanlan.zhihu.com/p/695144724
  5. https://zhuanlan.zhihu.com/p/704228271
  6. https://mp.weixin.qq.com/s?__biz=Mzk0NzcwNTQyOA==&mid=2247483763&idx=1&sn=287fea25d46ba0e602b50313caf15acc&chksm=c3738248f4040b5ed0f770123e6a269a9121807d084da41774fbfb7efa4df9a39557274e416a&scene=21#wechat_redirect
  7. https://zhuanlan.zhihu.com/p/716688741
  8. https://zhuanlan.zhihu.com/p/627436535
  9. https://zhuanlan.zhihu.com/p/646210009
  10. https://zhuanlan.zhihu.com/p/667109491
  11. https://zhuanlan.zhihu.com/p/9270075965
  12. https://zhuanlan.zhihu.com/p/1899107168172630461
  13. https://qwen.readthedocs.io/zh-cn/latest/deployment/vllm.html
  14. https://github.com/vllm-project/llm-compressor/blob/main/examples/quantization_w4a16/README.md

相关文章:

  • 【Elasticsearch】全文检索 组合检索
  • 正交视图三维重建 笔记 2d线到3d线
  • red-black-tree
  • 《Go语言高级编程》玩转RPC
  • axure基础操作
  • Rust高效编程实战指南
  • c++学习(五、函数高级)
  • GEO(生成式引擎优化)—— 内容创作者与企业的生死新战场
  • 掌握 MySQL 的基石:全面解读数据类型及其影响
  • HarmonyOS NEXT仓颉开发语言实战案例:动态广场
  • 单调栈一文深度解析
  • Flutter基础(路由页面跳转)
  • 【Cursor黑科技AI编程实战】
  • PMO 与IPD、CMMI、项目管理什么区别和联系
  • C++扩展 - 关键字应用 - decltype
  • 中级统计师-经济学基础知识-第三章 市场失灵与分配不公及其公共治理
  • Uni-App 小程序面试题高频问答汇总
  • 【QT】QT的发展历史与介绍
  • 机器学习配置环境
  • Python实现对WPS协作群进行群消息自动推送