W8A8量化手把手教程:gemma-4-26B-A4B-it-w8a8-llmcompressor 的 LLM Compressor oneshot 代码逐行注释
前言:这篇教程能帮你做什么
W8A8量化可以让大模型体积直接减半:AMD 用LLM Compressor的oneshot函数,将 gemma-4-26B-A4B-it 从48.1 GiB 压缩到 25.3 GiB(约 47%),而 GSM8K 基准的精度恢复率高达98.95%。本教程对该仓库中量化脚本的每一行代码逐行注释,帮你完整复现 W8A8 量化流程,适合新手和普通开发者快速上手。
💡W8A8 是什么?“W8”指权重用 8 位整数(INT8)存储,“A8”指激活值也动态量化为 8 位。相比只量化权重(W8A16),W8A8 在 AMD EPYC CPU 推理时能充分利用 INT8 指令,算得更快。
模型速览:这份 W8A8 量化模型长什么样
这个仓库是 AMD 基于LLM Compressor v0.13.0对 gemma-4-26B-A4B-it(RedHatAI 版)做量化后的成品 checkpoint,主要服务于AMD EPYC CPU 推理(ZenDNN 加速栈),用 vLLM 加载。核心信息一览:
| 项目 | 说明 |
|---|---|
| 架构 | Gemma4ForConditionalGeneration(多模态 MoE,30 层 × 128 路由专家,top-8 路由) |
| 量化方案 | W8A8:权重 INT8 对称 / per-channel 静态,激活 INT8 对称 / per-token 动态 |
| 量化算法 | RTN(Round-to-Nearest,四舍五入最近取整) |
| 量化产物格式 | compressed-tensors(int-quantized格式) |
| 体积变化 | 48.1 GiB → 25.3 GiB(约 47% 缩减) |
| 推理引擎 | vLLM v0.29.0 + ZenDNN v6.1.0 + ZenTorch v2.13.0.2 |
其中“量化了哪些层、哪些层保留 BF16”是这份 W8A8 配方最关键的取舍,README.md 中给出了精确范围:
- 被量化的层:30 层中每层的 128 个路由专家(
experts.*.{gate,up,down}_proj)、共享 dense MLP(mlp.{gate,up,down}_proj)和自注意力(self_attn.{q,k,v,o}_proj); - 保留 BF16 的层:MoE 路由器(
router.proj及其 scales)、视觉塔(vision tower)、多模态投影器(embed_vision)、lm_head、embed_tokens以及所有 LayerNorm。
⚠️为什么要保留这些层?路由器参数很小但对路由决策极其敏感,一旦量化导致“送错专家”,精度会明显受损;视觉塔和
lm_head则直接影响多模态输入质量与最终输出分布。
复现 W8A8 量化:oneshot 代码逐行注释
下面是完整的量化脚本(源自 README.md 的 Quantization 部分),分 5 个步骤讲解。
第 1 步:安装依赖并导入模块
import torch import transformers from transformers import AutoConfig, AutoProcessor, AutoTokenizer from llmcompressor import oneshot from llmcompressor.modifiers.quantization import QuantizationModifierAutoConfig/AutoProcessor/AutoTokenizer:transformers 的自动加载器,分别负责读配置、处理器、分词器;oneshot:LLM Compressor 的核心入口函数,一次调用完成“量化 + 保存”,无需校准数据、无需训练循环,这就是它叫 oneshot 的原因;QuantizationModifier:定义量化“配方(recipe)”的类,等价于下面仓库里 recipe.yaml 描述的内容。
版本要求(与 README.md 的 Requirements 一致):
torch==2.13.0.2 zentorch==2.13.0.2 vllm==0.29.0 llmcompressor==0.13.0第 2 步:按原始架构加载 BF16 模型与分词器
model_id = "RedHatAI/gemma-4-26B-A4B-it" output_dir = "./gemma-4-26B-A4B-it-w8a8-llmcompressor-v0.13.0" # Step 1: Load the BF16 model and tokenizer. This is a composite multimodal MoE # model, so load via the architecture named in its own config: # AutoModelForCausalLM would demote config.json to the text-only Gemma4TextConfig # and produce a checkpoint vLLM rejects. config = AutoConfig.from_pretrained(model_id, trust_remote_code=True) model_cls = getattr(transformers, config.architectures[0]) model = model_cls.from_pretrained( model_id, dtype=torch.bfloat16, device_map="cpu", trust_remote_code=True, ) tokenizer = AutoTokenizer.from_pretrained(model_id, trust_remote_code=True)逐行注释:
| 代码 | 注释 |
|---|---|
model_id = "RedHatAI/gemma-4-26B-A4B-it" | 源 BF16 模型地址(多模态 MoE 原版) |
output_dir = "..." | 量化后 checkpoint 的输出目录 |
AutoConfig.from_pretrained(...) | 读取 config.json,拿到architectures[0](即Gemma4ForConditionalGeneration) |
model_cls = getattr(transformers, config.architectures[0]) | 动态取出与架构名对应的模型类 |
model_cls.from_pretrained(...) | 以 BF16 精度加载到CPU(device_map="cpu"),量化在 CPU 上完成 |
⚠️新手最容易踩的坑:不要用
AutoModelForCausalLM加载!它是“复合多模态 MoE”模型,AutoModelForCausalLM会把配置降级为纯文本的Gemma4TextConfig,产出的 checkpoint 会被 vLLM 直接拒绝。必须先读 config.json 中声明的architectures再按名加载(本仓库的 config.json 里可以看到"architectures": ["Gemma4ForConditionalGeneration"])。
第 3 步:定义 W8A8 量化配方(核心)
# Step 2: Define the W8A8 recipe. The routed experts are quantized along with # the shared MLP and attention; only the router, vision path and lm_head are # skipped. recipe = QuantizationModifier( scheme="W8A8", targets=["Linear"], ignore=[ "lm_head", r"re:.*lm_head", # MoE router: tiny and extremely sensitive, wrong routing wrecks accuracy. r"re:.*router\.proj$", # Vision tower and multimodal projector must stay BF16. r"re:.*vision_tower.*", r"re:.*embed_vision.*", r"re:.*embed_audio.*", ], )| 参数 | 注释 |
|---|---|
scheme="W8A8" | 直接选用内置 W8A8 方案:权重 8 位对称 + 激活 8 位动态,无需手动指定每个量化参数 |
targets=["Linear"] | 量化对象:所有nn.Linear层(即专家投影、MLP、注意力投影) |
ignore=[...] | 豁免清单,用正则(re:前缀)按层名跳过:lm_head、MoE 路由器router.proj、视觉塔vision_tower、视觉/音频嵌入embed_vision/embed_audio |
这份 Python 配方与仓库中 recipe.yaml 完全对应:
default_stage: default_modifiers: QuantizationModifier: targets: [Linear] ignore: [lm_head, 're:.*lm_head', 're:.*router\.proj$', 're:.*vision_tower.*', 're:.*embed_vision.*', 're:.*embed_audio.*'] scheme: W8A8 requires_calibration_data: false可以看到requires_calibration_data: false——RTN 算法不需要校准数据集,这是 oneshot 量化相对 QAT/GPTQ 类方法的显著优势。
第 4 步:调用 oneshot 完成量化并保存
# Step 3: One-shot quantize and save in compressed-tensors format oneshot( model=model, recipe=recipe, tokenizer=tokenizer, output_dir=output_dir, trust_remote_code_model=True, )model/recipe/tokenizer:三要素缺一不可,tokenizer 用于在保存时写入分词配置;output_dir:输出目录,oneshot 会把量化后的权重按compressed-tensors格式写入model.safetensors分片,并更新config.json中的quantization_config(本仓库 config.json 的quantization_config字段就是这一步的产物:weights.strategy="channel"、input_activations.strategy="token"且dynamic=true);trust_remote_code_model=True:允许加载依赖自定义代码的模型架构。
第 5 步:补存 Processor 并冒烟测试
# Step 4: Save the processor; oneshot does not write it and vLLM needs it processor = AutoProcessor.from_pretrained(model_id, trust_remote_code=True) processor.save_pretrained(output_dir) # Smoke test inputs = tokenizer("What are we having for dinner?", return_tensors="pt") output = model.generate(**inputs, max_new_tokens=30) print(tokenizer.decode(output[0], skip_special_tokens=True))- 注意:oneshot 只保存模型和分词器,不会写
processor_config.json,而 vLLM 加载多模态模型时必须要它,所以要手动save_pretrained(对应仓库中的 processor_config.json); - 冒烟测试:用 tokenizer 编码一句英文问题,
model.generate生成最多 30 个 token 并解码打印,只要输出是通顺的自然语言,就说明量化后模型权重完整可用。
量化效果验证:GSM8K 评测 98.95% 恢复率
按 README.md 的评测流程(lm_eval+ vLLM 引擎,5-shot),W8A8 量化模型相对 BF16 基线的表现:
| Benchmark | BF16 基线 | W8A8(本模型) | 恢复率 |
|---|---|---|---|
| GSM8K (5-shot) | 0.9462 | 0.9363 | 98.95% |
也就是说,体积减少近一半的同时,数学推理能力几乎无损——这正是 W8A8 + RTN 在这类 MoE 模型上的理想结果。
量化产物怎么用:vLLM 快速启动
量化完成后,用 vLLM 加载(来自 README.md Quick Start 部分):
from vllm import LLM, SamplingParams model = LLM( model="amd/gemma-4-26B-A4B-it-w8a8-llmcompressor-v0.13.0", dtype="bfloat16", ) sampling_params = SamplingParams(temperature=0.7, max_tokens=256) outputs = model.generate(["Hello, how are you?"], sampling_params) print(outputs[0].outputs[0].text)OpenMP 环境变量(性能优化):CPU 推理前建议先设置LD_PRELOAD指向libomp.so(LLVM OpenMP)或libiomp5.so(Intel OpenMP),再启动 vLLM:
export LD_PRELOAD=$(find /path/to/env -name "libomp.so" | head -1)⚠️ 两个使用限制(见 README.md Limitations):该模型版本锁定(ZenDNN v6.1.0 / ZenTorch v2.13.0.2 / PyTorch v2.13.0.2),其他版本可能无法正确加载;且仅面向AMD EPYC CPU 推理,不是为 GPU 推理准备的。
仓库文件速查表
| 文件 | 说明 |
|---|---|
| README.md | 模型卡:量化细节、oneshot 脚本、vLLM 用法、评测与限制 |
| recipe.yaml | 量化配方:W8A8 方案、targets 与 ignore 清单 |
| config.json | 模型架构 +quantization_config(compressed-tensors 量化元信息、ignore 层列表) |
| generation_config.json | 生成默认参数(temperature、top_k=64、top_p=0.95 等) |
| processor_config.json | 多模态处理器配置(图像/视频/音频) |
| tokenizer_config.json / tokenizer.json | 分词器配置与词表(含<image>、<video>等多模态特殊 token) |
| chat_template.jinja | 对话模板 |
| model-00001-of-00002.safetensors / model-00002-of-00002.safetensors / model.safetensors | INT8 量化权重(分片)与合并权重,共约 25.3 GiB |
| model.safetensors.index.json | 权重分片索引 |
| LICENSE / NOTICE.txt | 许可证与版权声明 |
总结
用 LLM Compressor 复现 W8A8 量化的完整流程其实只有 5 步:读配置 → 按架构名加载 BF16 模型 → 定义 QuantizationModifier 配方(scheme + targets + ignore)→ 调用 oneshot 一键量化 → 补存 Processor 并冒烟测试。关键在于两处细节:一是对复合多模态 MoE 模型必须用config.architectures[0]指定类加载,二是 MoE 路由器、视觉塔和lm_head必须保留 BF16。按本教程操作,你就能得到与 gemma-4-26B-A4B-it-w8a8-llmcompressor 相同规格的量化模型——体积减半、精度近乎无损。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考