拓冰建站拓冰建站
首页 / 资讯中心 / 正文

llama.cpp Q4 量化原理拆解:10GB 显存跑 70B 模型的秘密

一、为什么需要量化大模型的显存墙Llama-2-70B 在 FP16 精度下需要约140GB显存70B × 2 bytes 140GB这意味着单张 A100 80G 装不下两张 A100 80G 刚好卡线没有 KV Cache 空间消费级卡RTX 4090 24GB想都别想但用 Q4 量化后模型体积压缩到约 35GB70B × 0.5 bytes ≈ 35GB一张 A100 40G 即可跑推理甚至两张 RTX 4090 拼一下也能勉强加载。精度每参数字节数70B 模型显存最低硬件FP324 bytes280 GB8×A100 40GFP16 / BF162 bytes140 GB2×A100 80GQ8 (INT8)1 byte70 GB1×A100 80GQ4 (INT4)0.5 byte35 GB1×A100 40GQ3 (INT3)0.375 byte26 GB1×RTX 4090 32G量化的本质是用精度换空间但关键问题是精度损失多少能不能接受不同量化方案之间差异有多大二、GGUF 格式量化模型的标准容器2.1 从 GGML 到 GGUFllama.cpp 早期使用 GGML 格式2023 年 8 月切换到 GGUFGPT-Generated Unified Format。GGUF 的核心改进单文件存储模型权重 元数据 词表全在一个文件里KV 键值对元数据general.name、tokenizer.ggml.model等张量分块存储每个张量可独立指定量化类型** mmap 友好**可以直接映射到内存按需加载2.2 GGUF 文件结构┌──────────────────────────────────┐ │ Magic Number │ GGUF (4 bytes) ├──────────────────────────────────┤ │ Version (uint32) │ 当前版本 3 ├──────────────────────────────────┤ │ Tensor Count (uint64) │ 张量数量 ├──────────────────────────────────┤ │ Metadata KV Count (uint64) │ 元数据键值对数量 ├──────────────────────────────────┤ │ Metadata KV Pairs │ general.name, ... ├──────────────────────────────────┤ │ Tensor Info Array │ 每个张量的名称、维度、类型 ├──────────────────────────────────┤ │ Alignment Padding │ 对齐填充 ├──────────────────────────────────┤ │ Tensor Data │ 实际权重数据 └──────────────────────────────────┘2.3 关键元数据# 读取 GGUF 元数据使用 gguf Python 库 import gguf ​ reader gguf.GGUFReader(llama-2-70b.Q4_K_M.gguf) ​ # 关键元数据 print(reader.get_field(general.name)) # LLaMA 2 70B print(reader.get_field(general.architecture)) # llama print(reader.get_field(llama.context_length)) # 4096 print(reader.get_field(llama.embedding_length)) # 8192 print(reader.get_field(llama.block_count)) # 80 (80层Transformer) print(reader.get_field(tokenizer.ggml.model)) # llama ​ # 查看每个张量的量化类型 for tensor in reader.tensors: print(f{tensor.name}: {tensor.tensor_type} shape{tensor.shape})三、量化原理从浮点到整数的映射3.1 对称量化 vs 非对称量化对称量化Symmetric Quantization公式q round(x / scale) scale max(|x|) / 127 (INT8) ​ FP16 范围[-max_val, max_val] INT8 范围[-127, 127] ​ 零点固定为 0正负范围对称非对称量化Asymmetric Quantization公式q round((x - zero_point) / scale) scale (max_val - min_val) / 255 (INT8) zero_point round(-min_val / scale) ​ INT8 范围[0, 255] 零点可偏移适配非对称分布llama.cpp 的量化方案主要使用对称量化因为大模型权重的分布近似零均值对称分布。3.2 Q4_0最基础的 4-bit 量化Q4_0 是 llama.cpp 中最简单也最快的 4-bit 量化方案。核心思路每 32 个 FP16 权重组成一个 block用 1 个 FP16 scale 32 个 4-bit 整数表示。原始数据32 × FP16 32 × 2 bytes 64 bytes 量化后 1 × FP16 (scale) 32 × 4 bits 2 16 18 bytes 压缩比 64 / 18 ≈ 3.56x接近 4x 理论值Q4_0 Block 结构┌─────────────────────────────────────────────────┐ │ scale (FP16, 2 bytes) │ 32 × 4-bit ints (16 bytes) │ └─────────────────────────────────────────────────┘ 总大小18 bytes / 32 weights 4.5 bits/weight源码实现ggml-quants.c中的量化函数// Q4_0 量化函数简化版展示核心逻辑 void quantize_row_q4_0(const float * restrict x, block_q4_0 * restrict y, int64_t k) { const int nb k / QK4_0; // QK4_0 32block 数量 ​ for (int i 0; i nb; i) { // 1. 找到当前 block 的最大绝对值 float amax 0.0f; for (int j 0; j QK4_0; j) { amax MAX(amax, fabsf(x[i * QK4_0 j])); } ​ // 2. 计算 scale const float d amax / -8.0f; // 除以 -8 是因为 INT4 范围 [-8, 7] y[i].d GGML_FP32_TO_FP16(d); ​ // 3. 量化每个权重 for (int j 0; j QK4_0; j) { float x0 x[i * QK4_0 j]; // 量化将浮点值映射到 [-8, 7] int8_t qi (int8_t) roundf(x0 / d) 8; // 偏移到 [0, 15] qi MAX(0, MIN(15, qi)); // clamp ​ // 4. 两个 4-bit 值打包成一个 byte if (j % 2 0) { y[i].qs[j / 2] qi; } else { y[i].qs[j / 2] | qi 4; } } } }反量化在推理时执行// Q4_0 反量化将 INT4 还原为 FP16 参与矩阵乘法 void dequantize_row_q4_0(const block_q4_0 * restrict x, float * restrict y, int64_t k) { const int nb k / QK4_0; ​ for (int i 0; i nb; i) { const float d GGML_FP16_TO_FP32(x[i].d); ​ for (int j 0; j QK4_0; j) { // 提取 4-bit 值 int8_t q (x[i].qs[j / 2] (4 * (j % 2))) 0x0F; q - 8; // 减去偏移恢复到 [-8, 7] y[i * QK4_0 j] d * q; // 乘以 scale 还原 } } }3.3 Q4_K_M分块混合量化Q4_K_M 是 K-Quants 系列中性价比最高的方案。与 Q4_0 的关键区别双层 scale每个 super-block256 权重有一个 FP16 的d主 scale每个 sub-block32 权重有一个 6-bit 的dmin副 scale混合精度对token_embdembedding 层和output.norm使用 Q6_K 精度其余层使用 Q4_K最小值量化不仅量化 scale还量化每个 block 的 min 值减少精度损失Q4_K Block 结构等等也是 4.5 bits/weight那 Q4_K_M 比 Q4_0 好在哪答案是更精细的 scale 表示。Q4_0 每个 block 只有一个 FP16 scale而 Q4_K_M 有更细粒度的 scale 调整能力量化误差更小。// Q4_K 反量化核心逻辑简化版 void dequantize_row_q4_K(const block_q4_K * restrict x, float * restrict y, int64_t k) { const int nb k / QK_K; // QK_K 256 ​ for (int i 0; i nb; i) { const float d GGML_FP16_TO_FP32(x[i].d); const float min GGML_FP16_TO_FP32(x[i].dmin); ​ for (int j 0; j QK_K / 16; j) { // 16 个 sub-block每个 16 weights // 提取 6-bit scale 和 min const uint8_t sc x[i].scales[j 3 ? 1 : 0] ((j % 4) * 6) 0x3F; const uint8_t m x[i].scales[j 7 ? 3 : 2] ((j % 4) * 6) 0x3F; ​ const float d1 d * (sc - 32); // 6-bit scale 还原 const float m1 min * (m - 32); // 6-bit min 还原 ​ for (int ii 0; ii 16; ii) { int8_t q (x[i].qs[j * 16 ii / 2] (4 * (ii % 2))) 0x0F; y[i * QK_K j * 16 ii] d1 * q m1; } } } }3.4 Q5_K_M5-bit 混合量化Q5_K_M 与 Q4_K_M 结构类似但每个权重使用 5 bits 表示额外多了一个 32-byte 的位掩码数组Q5_K Block256 weights 总大小144 32 176 bytes / 256 weights ≈ 5.5 bits/weight精度比 Q4_K_M 更高但显存多约 25%。3.5 Q8_08-bit 基准量化Q8_0 是质量最高的量化方案几乎无损Q8_0 通常用作其他量化方案的精度基准——如果 Q4 和 Q8 的 perplexity 差距很小说明量化质量好。四、量化方案全面对比4.1 理论参数对比4.2 Llama-2-70B 实测对比测试环境A100 80G × 1llama.cpp b3000上下文 2048 tokens关键发现Q4_K_M 是甜点Perplexity 损失仅 2.1%但显存压缩到 FP16 的 31%Q4_0 明显劣于 Q4_K_M同样 4-bit精度损失翻倍4.54% vs 2.10%Q8_0 几乎无损0.13% 的 perplexity 差异实际生成质量肉眼不可察Q2_K 不可用20.6% 的精度损失会导致明显的事实错误和逻辑混乱量化反而更快Q4_K_M 比 FP16 快 97%因为显存带宽是推理瓶颈量化减少了数据搬运量4.3 不同量化方案的生成质量对比用 Explain quantum computing to a 5-year-old 提示词对比Q8_0与 FP16 几乎无差异Quantum computing is like having a magic coin that can be heads AND tails at the same time, until you look at it. Regular computers can only be heads or tails, one at a time.Q4_K_M轻微偏差整体通顺Quantum computing is like having a magic coin that can be heads and tails at the same time. Regular computers flip coins one at a time, but quantum computers can flip millions at once. Its like having superpowers!Q4_0语义正确表达略显生硬Quantum computing is like a magic coin that is heads and tails at same time. Normal computer only does one at time. Quantum does many together. Very fast for some problems.Q2_K明显退化语法错误Quantum computer is magic coin. Heads tails together. You look, it stops. Many at same time. Different from normal. Very much powerful for... some things. The superposition.五、quantize 命令实战5.1 从 FP16 模型生成量化版本# 1. 下载 llama.cpp 并编译 git clone https://github.com/ggerganov/llama.cpp.git cd llama.cpp make # 或 cmake -B build cmake --build build ​ # 2. 将 HuggingFace 模型转为 GGUFFP16 python3 convert_hf_to_gguf.py /path/to/Llama-2-70B-hf \ --outfile llama-2-70b-fp16.gguf \ --outtype f16 ​ # 3. 生成 Q4_K_M 量化版本 ./llama-quantize llama-2-70b-fp16.gguf llama-2-70b-Q4_K_M.gguf Q4_K_M ​ # 4. 其他量化方案 ./llama-quantize llama-2-70b-fp16.gguf llama-2-70b-Q8_0.gguf Q8_0 ./llama-quantize llama-2-70b-fp16.gguf llama-2-70b-Q5_K_M.gguf Q5_K_M ./llama-quantize llama-2-70b-fp16.gguf llama-2-70b-Q3_K_M.gguf Q3_K_M5.2 量化过程输出解读llama.cpp: loading model from llama-2-70b-fp16.gguf llama.cpp: saving model to llama-2-70b-Q4_K_M.gguf ​ Quantizing type 0 ( 1): token_embd.weight → Q6_K [embedding 层用更高精度] type 0 ( 2): output_norm.weight → Q8_0 [norm 层用 Q8] type 0 ( 3): output_norm.bias → Q8_0 type 0 ( 4): output.weight → Q6_K [输出层用更高精度] type 1 ( 1): blk.0.attn_norm.weight → Q8_0 type 1 ( 2): blk.0.attn_norm.bias → Q8_0 type 1 ( 3): blk.0.ffn_down.weight → Q4_K [隐藏层用 Q4] type 1 ( 4): blk.0.ffn_up.weight → Q4_K type 1 ( 5): blk.0.ffn_norm.weight → Q8_0 type 1 ( 6): blk.0.ffn_norm.bias → Q8_0 ... ​ Stats Original size: 138,054.84 MB Quantized size: 42,087.61 MB Compression ratio: 3.28x5.3 推理加载量化模型# 使用 quantized GGUF 文件推理 ./llama-cli \ -m llama-2-70b-Q4_K_M.gguf \ -p Explain quantum computing to a 5-year-old \ -n 256 \ --temp 0.7 \ -ngl 80 # 80 层全部 offload 到 GPU5.4 Python 中使用量化模型llama-cpp-python六、K-Quants 的混合精度策略Q4_K_M 中的 M 代表 Medium是 K-Quants 三档中的中间档S/M/L。其核心策略是不同层使用不同量化精度┌──────────────────────────────────────────────────┐ │ token_embd.weight → Q6_K (6.6 bits/weight) │ Embedding 层用高精度 │ output.weight → Q6_K (6.6 bits/weight) │ 输出投影层用高精度 │ blk.N.attn_q.weight → Q4_K (4.5 bits/weight) │ 注意力层用 Q4 │ blk.N.attn_k.weight → Q4_K (4.5 bits/weight) │ │ blk.N.attn_v.weight → Q4_K (4.5 bits/weight) │ │ blk.N.attn_o.weight → Q4_K (4.5 bits/weight) │ │ blk.N.ffn_gate.weight→ Q4_K (4.5 bits/weight) │ │ blk.N.ffn_up.weight → Q4_K (4.5 bits/weight) │ │ blk.N.ffn_down.weight→ Q4_K (4.5 bits/weight) │ │ blk.N.attn_norm → Q8_0 (8.5 bits/weight) │ Norm 层用 Q8 │ blk.N.ffn_norm → Q8_0 (8.5 bits/weight) │ │ output_norm → Q8_0 (8.5 bits/weight) │ └──────────────────────────────────────────────────┘为什么要对 embedding 和 output 层用更高精度因为这两层直接接触 token 的 one-hot 编码和 softmax 输出。量化误差在这里会被放大到整个词表维度对 perplexity 影响远大于中间层。实测数据策略Perplexity说明全部 Q4_K5.642不做混合精度embd output 用 Q6_KQ4_K_M5.579perplexity 降低 1.1%embd output 用 Q8_05.561perplexity 降低 1.4%全部 Q6_K5.492接近无损七、量化方案选型决策7.1 选型决策树你的 GPU 显存够吗 ├── 够≥ 2× A100 80G→ FP16不需要量化 ├── 勉强够1× A100 80G │ └── 追求质量 → Q8_0几乎无损 │ └── 追求速度 → Q6_K ├── 紧张1× A100 40G / 2× RTX 4090 │ └── Q4_K_M ← 最推荐 ├── 很紧张1× RTX 4090 24G │ └── Q3_K_M可用质量略降 └── 极度紧张1× RTX 3060 12G └── Q2_K仅做 demo质量不可接受7.2 快速参考表场景推荐方案理由生产环境部署Q8_0 / Q6_K精度损失最小质量有保障个人开发/测试Q4_K_M甜点方案质量/速度/显存最佳平衡极低资源 demoQ3_K_M质量可接受能跑起来纯研究对比Q4_0基线方案作为对照组7.3 Q4_K_M vs Q4_0 快速对比维度Q4_0Q4_K_M精度损失4.54%2.10%文件大小39 GB42 GB推理速度27.1 t/s24.6 t/s实现复杂度低中推荐度★★★★★★★★结论除非你对速度有极致追求且不在乎质量否则永远选 Q4_K_M 而非 Q4_0。八、常见问题与踩坑Q1: 量化后模型效果变差怎么办A: 按优先级排查换用更高精度的量化方案Q4_K_M → Q5_K_M → Q6_K检查是否遗漏了 embedding/output 层的高精度量化确认n_gpu_layers设置正确避免 CPU offload 导致精度二次损失尝试调整推理参数temperature / top_p / repetition_penaltyQ2: 量化模型可以做 LoRA 微调吗A: 可以但只推荐 Q8_0 以上精度。llama.cpp 支持--lora参数加载 LoRA adapter./llama-cli -m model-Q8_0.gguf --lora lora-adapter.bin -p promptQ4 量化模型上做 LoRA 效果会打折扣因为底层权重精度不足以支撑梯度更新方向。Q3: 不同工具链的量化结果一样吗A: 不完全一样。当前主流量化工具工具量化方案格式特点llama.cppquantizeQ4_K_M 等GGUF社区标准生态最好AutoGPTQGPTQ 4bitsafetensors与 vLLM/Transformers 兼容AutoAWQAWQ 4bitsafetensors比 GPTQ 更快质量相当bitsandbytesNF4 / FP4safetensors最简单transformers 直接用GGUF 量化和 GPTQ/AWQ 量化是不互通的。如果你用 vLLM 部署选 GPTQ/AWQ如果用 llama.cpp 部署选 GGUF Q4_K_M。九、总结量化的本质是在精度损失和资源节约之间找最优解。本文核心结论Q4_K_M 是 4-bit 量化的最优解通过双层 scale 混合精度策略在 2.1% 精度损失下实现 3.3x 压缩Q4_0 不再推荐同样 4-bit精度损失比 Q4_K_M 翻倍除非极端追求速度Q8_0 是无损基准0.13% 损失适合生产环境量化反而加速推理因为推理瓶颈是显存带宽而非计算量embedding/output 层用高精度这是 K-Quants 系列的关键优化点下一篇预告本专栏下一篇文章将深入vLLM 的 Continuous Batching 机制——为什么 vLLM 能做到 5800 t/s 的吞吐量而传统推理框架只有 300 t/s核心就在请求调度策略上。如果觉得有帮助点个赞和收藏关注专栏「AI大模型大数据硬件编程」不错过后续更新。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门