3、量化核心概念:量化定义、定点与浮点、量化误差、量化粒度(per-tensor/per-channel/per-group)
量化(Quantization)是将高精度数值(通常是 FP32/FP16)映射到低精度数值(如 INT8/INT4)的过程,其本质是用更少的比特位表示神经网络中的权重和激活值。在 vLLM 推理引擎中,量化直接决定了显存占用、吞吐量和输出质量之间的平衡。理解量化定义、定点与浮点差异、误差来源以及粒度选择,是掌握 vLLM 量化推理的第一步。本节将系统拆解这四个核心概念,并结合实际推理场景说明其工程意义。
3.1 量化定义:从连续到离散的映射
量化在数学上定义为一个映射函数:Q(r) = round(r / S) + Z,其中 r 是原始浮点数值,S 是缩放因子(Scale),Z 是零点偏移(Zero Point)。反量化过程为 r ≈ S × (Q(r) - Z)。在 vLLM 中,量化通常应用于权重矩阵和激活张量,将 FP16 的 2 字节数据压缩为 INT8 的 1 字节或 INT4 的半字节,从而减少显存带宽压力。
从信息论角度看,量化是有损压缩。关键在于选择合适的 S 和 Z,使得映射后的分布尽可能接近原始分布。vLLM 支持多种量化方案,包括 GPTQ、AWQ、SqueezeLLM 等,它们的主要区别在于校准策略和误差补偿方式。例如,AWQ 通过观察激活值分布来保护重要权重通道,而 GPTQ 则基于 Hessian 矩阵进行二阶误差补偿。
--quantization 参数)决定了使用哪种映射策略,直接影响最终模型质量。
3.2 定点与浮点:数值表示的本质差异
浮点数(如 FP32/FP16)采用科学计数法表示:(-1)^sign × mantissa × 2^exponent,动态范围大但硬件计算单元复杂。定点数则固定小数点的位置,例如 Q7.8 表示 7 位整数位加 8 位小数位,硬件实现简单但动态范围受限。量化推理的核心思想就是将浮点运算转换为定点运算,利用整数矩阵乘法单元(如 Tensor Core 的 INT8 模式)加速计算。
| 特性 | FP32 | FP16 | INT8 | INT4 |
|---|---|---|---|---|
| 位宽 | 32 bit | 16 bit | 8 bit | 4 bit |
| 动态范围 | ~1e-38 ~ 3e38 | ~6e-5 ~ 6e4 | -128 ~ 127 | -8 ~ 7 |
| 相对精度 | 高 | 中 | 低 | 极低 |
| 显存占用 | 4 B | 2 B | 1 B | 0.5 B |
| 计算速度 | 慢 | 中 | 快 | 最快 |
在 vLLM 推理中,权重通常以 FP16 加载,但可以通过量化层转换为 INT8/INT4 存储。激活值在推理过程中动态量化,需要额外的 Scale 和 Zero Point 参数。定点运算的优势在于:INT8 矩阵乘法在 NVIDIA GPU 上的吞吐量是 FP16 的 2~4 倍,而 INT4 配合特殊硬件(如 INT4 Tensor Core)可进一步提升。
3.3 量化误差:来源分析与控制策略
量化误差主要来自三个方面:舍入误差(Rounding Error)、截断误差(Clipping Error)和量化分辨率不足。舍入误差是均匀分布的,期望值为 0,但方差与量化步长相关;截断误差发生在数值超出量化范围时,会导致梯度爆炸或激活值饱和;分辨率不足则表现为小数值被量化为 0,丢失细节信息。
控制量化误差的常用方法包括:
- 对称量化 vs 非对称量化:对称量化假设数据分布关于 0 对称,无需零点偏移;非对称量化更适合 ReLU 后的激活值分布。
- 校准数据集:使用少量真实样本统计张量分布,选择最优的 Scale 和 Zero Point。vLLM 支持通过
--calibration-dataset指定校准数据。 - 误差补偿:GPTQ 使用 Hessian 矩阵进行权重更新补偿,AWQ 通过缩放显著通道来减少误差。
- 混合精度:对敏感层(如 attention 的 QKV 投影)保留 FP16,对不敏感层(如 FFN 的中间层)使用 INT8。
3.4 量化粒度:per-tensor / per-channel / per-group
量化粒度决定了 Scale 和 Zero Point 的共享范围,是精度与计算复杂度之间的权衡。三种粒度的对比如下:
| 粒度 | 共享范围 | 额外参数 | 精度 | 计算开销 | 适用场景 |
|---|---|---|---|---|---|
| per-tensor | 整个张量 | 1 组 Scale/ZP | 最低 | 最低 | 小模型、对精度不敏感 |
| per-channel | 每个输出通道 | C 组(C 为通道数) | 中 | 中 | 权重量化,多数场景推荐 |
| per-group | 每 G 个元素 | N/G 组 | 高 | 较高 | 激活量化、低比特(INT4) |
per-tensor 是最简单的实现,整个权重矩阵共享一个 Scale。对于分布均匀的权重(如经过 LayerNorm 后的值),效果尚可;但若权重存在明显的通道间差异,误差会放大。per-channel 为每个输出通道独立计算 Scale,能更好地适应不同通道的数值范围,是 vLLM 中 GPTQ 和 AWQ 的默认选择。per-group 进一步细化,将通道内的元素按固定大小分组(如 128 个一组),每组独立量化。这在 INT4 量化中尤为重要,因为 4 比特的表示范围极小,必须通过分组来降低误差。
--quantization-format 参数可控制量化粒度。例如,使用 gptq 时可通过 --group-size 设置分组大小(常见值为 128 或 64)。较小的 group-size 能提升精度,但会增加存储 Scale 参数的开销(每个组多 2 字节),需根据显存预算权衡。
3.5 案例拆解:LLaMA-7B 的 INT8 量化部署
假设我们要在 vLLM 中部署 LLaMA-7B 模型,目标是将显存占用从 FP16 的 14GB 降至 INT8 的 7GB,同时保持推理精度损失小于 0.5%。初始测试发现,直接使用 per-tensor 量化后,模型在 MMLU 基准上的准确率从 45.2% 降至 42.8%,损失 2.4%,远超预期。
分析过程:通过可视化权重分布,发现 LLaMA-7B 的 FFN 层权重存在明显的通道间方差差异——部分通道的数值范围是其他通道的 10 倍以上。per-tensor 量化使用全局 min/max,导致小数值通道被严重压缩,信息丢失。
解决方案:改用 per-channel 量化,并采用 AWQ 算法保护 1% 的显著权重通道。具体操作如下:
# 使用 AWQ 量化 LLaMA-7B
python -m vllm.entrypoints.llm \
--model meta-llama/Llama-2-7b-hf \
--quantization awq \
--quantization-format per-channel \
--calibration-dataset c4 \
--calibration-samples 256 \
--max-model-len 4096
结果对比:per-channel + AWQ 后,MMLU 准确率恢复至 44.9%,仅下降 0.3%。显存占用降至 7.2GB,推理吞吐量从 850 tokens/s 提升至 2100 tokens/s。代价是量化时间增加了约 15 分钟(校准过程),且模型文件增大了约 200MB(存储额外的 Scale 参数)。
复盘经验:对于 7B 以上模型,per-tensor 量化几乎不可用;per-channel 是精度与性能的最佳平衡点。若需进一步压缩至 INT4,建议使用 group-size=128 的 per-group 量化,并配合 GPTQ 的二阶补偿。
3.6 常见误区与总结
在实际使用 vLLM 量化功能时,以下误区需要特别注意:
- 误区一:量化后必须微调。实际上,现代量化算法(如 AWQ、GPTQ)在大多数场景下无需微调即可保持精度,微调反而可能引入过拟合。
- 误区二:校准集越大越好。校准集应具有代表性而非数量多,通常 128~512 条样本即可。过多样本会增加校准时间且可能过拟合到校准分布。
- 误区三:所有层都适合量化。Attention 的 Softmax 输出和 LayerNorm 的 gamma/beta 参数通常保持 FP16,因为这些层对精度极其敏感。
- 误区四:忽略零点偏移。对于非对称量化,零点偏移的精度同样重要,使用 INT32 存储零点可避免累积误差。