# llm-quantization **Repository Path**: yongzi-huang/llm-quantization ## Basic Information - **Project Name**: llm-quantization - **Description**: LLM 量化全链路:从手写 PTQ 到 GPTQ 到端侧部署 | PyTorch / llama.cpp · 手写逐张量/逐通道/逐组量化原语,在 GPT-2 上实现完整 PTQ 流程(校准、敏感层分析、混合精度回退)。 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-25 - **Last Updated**: 2026-10-02 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # LLM Quantization 在 GPT-2 (124M) 上从零实现 LLM 量化全链路:手写 PTQ 量化原语 → 逐层敏感度分析 → GPTQ 误差补偿 → GGUF 端侧部署验证。 --- ## 一、精度对比(PPL) 在 8 条英文文本上评估。基线 FP32 PPL = 34.86。 | 配置 | 位宽 | 粒度 | PPL | 相对 FP32 | logits 余弦 | |---|---|---|---|---|---| | FP32 | 32 | — | 34.86 | — | 1.000000 | | INT8 | 8 | 逐张量 | 40.92 | **+17.40%** | 0.998774 | | INT8 | 8 | 逐通道 | 34.81 | **-0.14%** | 0.999978 | | INT8 | 8 | 逐组(128) | 34.70 | **-0.44%** | 0.999996 | | INT4 | 4 | 逐通道 | 47.31 | **+35.73%** | 0.997842 | | INT4 | 4 | 逐组(128) | 41.40 | **+18.77%** | 0.999221 | | INT3 | 3 | 逐通道 | 550.50 | **+1479%** | 0.995002 | **三点观察**: 1. **INT8 逐通道是端侧底线**:几乎无损(-0.14%),但逐张量掉 17% 2. **粒度越细越好**:逐组(128) 比逐通道多 6 倍 scale,精度更高 3. **INT3 精度悬崖**:PPL 爆炸(+1479%),但 logits 余弦还有 0.995——**余弦相似度掩盖了概率分布尾部的破坏** ## 二、校准方法对比 MinMax 直接用最大绝对值,对 outlier 敏感。99.9% 分位截断过滤掉极端值。 | 校准方法 | 粒度 | PPL 增幅 | |---|---|---| | MinMax | INT4 per_channel | +35.73% | | 99.9% 分位截断 | INT4 per_channel | **+15.55%** | **观察**:分位截断在 per_channel 下把损失砍半。**校准方法的选择很关键。** ## 三、逐层敏感度分析(INT4 per-group + percentile 校准) 逐一量化 48 个线性层,测每层单独量化后的 PPL 增幅。 **Top 10 敏感层**: | 层名 | ΔPPL | |---|---| | h.3.mlp.c_fc | +4.16% | | h.7.mlp.c_fc | +3.58% | | h.0.mlp.c_fc | +3.00% | | h.11.mlp.c_proj | +2.91% | | h.3.mlp.c_proj | +2.29% | | h.5.mlp.c_fc | +2.20% | | h.11.attn.c_attn | +2.18% | | h.5.mlp.c_proj | +2.09% | | h.6.mlp.c_proj | +1.96% | | h.6.mlp.c_fc | +1.78% | **核心发现**: - **单层最大只掉 4.16%**,但全层量化掉 **35.73%** - **INT4 误差是全局累积的**,不是某一层的问题 - 因此混合精度回退效率有限:回退 Top-10 才能从 +35.7% 降到 +12.9% 敏感度图见 `results/sensitivity.png`。 ## 四、混合精度回退(percentile + per_group,INT4) 把最敏感的 Top-K 层保持 FP16,其余层 INT4,观察 PPL 恢复情况。 | 回退层数 | PPL | 相对 FP32 | |---|---|---| | Top-0(纯 INT4) | 42.27 | +21.28% | | Top-1 | 41.74 | +19.76% | | Top-2 | 40.43 | +16.01% | | Top-3 | 39.74 | +14.03% | | Top-5 | 38.04 | +9.14% | | **Top-10** | **34.76** | **-0.28%** | **结论**:`percentile 校准 + per_group(128) 粒度 + 回退 Top-10 敏感层`,INT4 几乎无损(-0.28%,低于 FP32 baseline)。 **但代价是**:回退 10 层到 FP16 增加约 30%~50% 权重读取带宽。所以这个方案对**精度极度敏感、但带宽不紧张**的端侧场景有效;对带宽受限场景,仍需 GPTQ 这类算法级补偿。 ## 五、GPTQ 对比 GPTQ 用 Hessian 二阶信息做误差补偿:量化一列后,把误差推到剩余未量化的列上,优先保证重要权重准确。 | 方法 | 位宽 | PPL | 相对 FP32 | 说明 | |---|---|---|---|---| | FP32 | 32 | 34.86 | — | baseline | | 朴素 PTQ | 4 | 47.31 | **+35.73%** | 独立 round,误差累加 | | **GPTQ** | **4** | **35.73** | **+2.52%** | **Hessian 误差补偿** | 这证明: - **W4A16 是格式目标**,朴素 PTQ 和 GPTQ 都能达成 - **算法决定精度**:Hessian 补偿让各层误差互相对消 ## 六、端侧部署验证(llama.cpp GGUF) 用 llama.cpp 的 GGUF Q4_K_M 验证真实部署效果: | 配置 | 文件大小 | Prefill (pp64) | Decode (tg32) | |---|---|---|---| | F16 | 239 MB | 629 t/s | 46 t/s | | Q4_K_M | 85 MB | 691 t/s | **97 t/s** | **结论**: - 体积压缩 **2.8×**(不是 4×,因为 Q4_K_M 混合精度:V/down 用 Q6_K) - **Decode 加速 2.1×**(访存密集型,量化收益显著) - **Prefill 仅 +10%**(计算密集型,量化收益有限) **这印证了 LLM 推理的核心理论**:Prefill 受算力约束,Decode 受显存带宽约束。量化的收益主要在 Decode 阶段。 --- ## 七、文件说明 ### scripts/ —— 核心脚本 | 文件 | 作用 | |---|---| | `01_fake_quant.py` | 手写量化原语 + FakeQuantLinear + 多配置 PPL 对比 | | `02_sensitivity.py` | 逐层敏感度分析 + 校准对比 + 混合精度回退 | | `03_gptq_quantize.py` | GPTQ INT4 量化(Hessian 二阶误差补偿) | | `04_gptq_eval.py` | GPTQ PPL 评估与朴素 PTQ 对比 | ### results/ —— 实验产出 | 文件 | 内容 | |---|---| | `sensitivity.png` | 48 层敏感度图(INT4 per-group) | ### logs/ —— 量化日志 | 文件 | 内容 | |---|---| | `gptq.log` | GPTQ 逐层量化日志(loss、耗时、显存) | ## 复现步骤 ### 1. 环境 ```bash pip install torch transformers gptqmodel ``` ### 2. 下载 GPT-2 ```bash huggingface-cli download gpt2 --local-dir gpt2/ ``` ### 3. 运行 ```bash # 手写量化对比(CPU 可跑) python scripts/01_fake_quant.py # 逐层敏感度分析 + 校准 + 回退(CPU) python scripts/02_sensitivity.py # GPTQ 量化(需要 GPU) python scripts/03_gptq_quantize.py # GPTQ 评估 python scripts/04_gptq_eval.py ``` ### 4. GGUF 端侧部署 需要编译 llama.cpp: ```bash git clone https://github.com/ggerganov/llama.cpp cd llama.cpp cmake -B build -DGGML_CUDA=OFF cmake --build build -j4 cd .. ``` 转换模型: ```bash python llama.cpp/convert_hf_to_gguf.py gpt2/ --outtype f16 --outfile gpt2-f16.gguf ./llama.cpp/build/bin/llama-quantize gpt2-f16.gguf gpt2-q4km.gguf Q4_K_M ``` 跑 benchmark: ```bash ./llama.cpp/build/bin/llama-bench -m gpt2-f16.gguf -p 64 -n 32 -r 3 ./llama.cpp/build/bin/llama-bench -m gpt2-q4km.gguf -p 64 -n 32 -r 3 ```