Q: INT8 和 W4A16 的量化流程和应用场景?
INT8(W8A8)量化——全面量化方案:
1 2 3 4 5 6 7 8 9 10
| 量化流程: 1. 准备校准数据(128-512 条代表性输入) 2. 前向推理收集统计信息: - 权重: per-channel min/max(静态,只需算一次) - 激活: per-tensor 或 per-token min/max(动态,每次推理计算) 3. 计算量化参数: - 对称量化: scale = max(|x|) / 127 - 非对称量化: scale = (max - min) / 255, zp = round(-min / scale) 4. 量化: q = clamp(round(x / scale), -128, 127) 5. 推理执行: INT8 矩阵乘(Tensor Core 加速)→ FP32 累加 → rescale → 输出
|
W8A8 的核心挑战——激活 outlier:
1 2 3 4 5 6 7 8 9
| 问题: 激活中某些 channel 的值远大于其他(如最大值 100,其余 < 1) → per-tensor scale 被 outlier 撑大 → 正常值量化精度极低
SmoothQuant 解决方案: 将激活的难度"转移"给权重: Y = (X × diag(s)^{-1}) × (diag(s) × W) ↑ 激活除以 s(缩小 outlier) ↑ 权重乘以 s(补偿) s 的选择: s_j = max(|X_j|)^α / max(|W_j|)^{1-α}, α∈[0.5, 0.75]
|
W8A8 适用场景:
- 硬件支持 INT8 Tensor Core(A100: 624 TOPS, 是 FP16 的 2x)
- 对精度要求较高(PPL 增加 0.1-0.3)
- Prefill 阶段加速效果显著(大矩阵乘 compute-bound,INT8 TC 吞吐 2x)
W4A16 量化——仅量化权重方案:
1 2 3 4 5 6 7 8 9 10 11 12 13
| 量化流程: 1. 分组: 将权重按 group_size=128 分组 2. 每组独立计算 scale 和 zero_point: scale = (max - min) / 15 (4-bit 范围 [0, 15]) zp = round(-min / scale) 3. 量化: q = clamp(round(w / scale + zp), 0, 15) 4. 打包: 2 个 INT4 值打包到 1 个 INT8(节省存储) 5. 存储: INT4 权重 + FP16 scale/zp (每 128 个权重一组)
推理时: load INT4 权重 (0.5 byte/element) → 寄存器内 unpack + dequant 为 FP16 → 与 FP16 激活做 FP16 GEMM (Tensor Core)
|
W4A16 适用场景:
- 显存受限(权重减小 4x:7B 从 14GB → 3.5GB)
- Decode 阶段加速(memory-bound,带宽减少 4x → 加速 ~3x)
- 对精度容忍度中等(PPL 增加 0.3-1.0)
两者对比:
| 维度 |
W8A8 (INT8) |
W4A16 |
| 量化对象 |
权重 + 激活 |
仅权重 |
| 计算方式 |
INT8 Tensor Core GEMM |
FP16 Tensor Core GEMM(权重先 dequant) |
| 权重压缩 |
2x (FP16→INT8) |
4x (FP16→INT4) |
| Prefill 加速 |
1.5-2x(INT8 TC 算力高) |
无加速(仍是 FP16 计算) |
| Decode 加速 |
1.5-2x(权重读取减 2x) |
2-3x(权重读取减 4x) |
| 精度损失 |
小(PPL +0.1-0.3) |
中(PPL +0.3-1.0) |
| 实现复杂度 |
高(需要处理激活 outlier) |
中(只需离线量化权重) |
| 代表方法 |
SmoothQuant, TensorRT INT8 |
AWQ, GPTQ, Marlin |
实践选择建议:
1 2 3
| A100/H100 + 精度优先 → W8A8 (SmoothQuant + TensorRT) 显存紧张 + Decode 为主 → W4A16 (AWQ + Marlin kernel) H100 + 训练+推理 → FP8 (Transformer Engine, 最简单)
|
Q: 数据分布极不均匀(最小值 -10000,其余在 [-1,1])应该用什么量化方式?
这是一个典型的 outlier 问题——少数极端值将量化范围撑大,导致绝大部分正常值的有效量化位数极低。
问题量化分析:
1 2 3 4 5
| 假设使用 INT8 对称量化 (范围 [-128, 127]): scale = 10000 / 127 ≈ 78.7 正常值 [-1, 1] 量化后 → round(±1 / 78.7) = 0 结果: 99.9% 的值被量化为 0!精度完全丧失
|
解决方案(按推荐优先级排序):
1. 离群值单独处理(最推荐,LLM.int8() 方案):
1 2 3 4 5 6 7 8 9
| 策略: 检测 outlier channel → 分离处理 正常 channel (|x| < threshold): INT8 量化,正常参与 INT8 GEMM Outlier channel (|x| ≥ threshold): 保持 FP16,参与 FP16 GEMM 最终结果 = INT8 部分输出 + FP16 部分输出
优点: 无信息损失,outlier 精确处理 缺点: 混合精度 GEMM 实现复杂,有额外开销 典型 threshold: 6.0(实验确定)
|
2. Per-group 量化(AWQ/GPTQ 方案):
1 2 3 4 5 6 7 8 9 10
| 策略: 每 128 个连续权重共享一个 scale 如果 outlier 只影响一个 group: 该 group: scale = 10000/127 ≈ 78.7(这个 group 精度差) 其他 group: scale = 1/127 ≈ 0.008(精度很好) 损失被局限在单个 group 内,不影响全局
优点: 简单高效,是 GPTQ/AWQ 的标准做法 缺点: 如果 outlier 分散在多个 group 中,每个 group 都受影响
|
3. Clipping + Calibration(截断方案):
1 2 3 4 5 6 7 8 9 10
| 策略: 计算最优截断点,牺牲 outlier 精度换取正常值精度
不截断: MSE_total = 大量正常值的量化误差 (因 scale 太大) 截断到 ±c: MSE_total = 正常值的量化误差 (小) + 截断误差 (outlier 被限制到 ±c) 找最优 c: min_c (E[(x - Q(clip(x, -c, c)))²]) 通常取 99.9% 或 99.99% 百分位数
优点: 实现简单 缺点: outlier 信息完全丢失
|
4. 非均匀量化(适配非均匀分布):
1 2 3 4 5 6 7 8 9
| NF4 (NormalFloat4): 假设权重近似正态分布 量化点: 在标准正态分布的等概率分位点处设置 0 附近密集(正态分布概率密度高),尾部稀疏
对于有 outlier 的分布: 可用对数量化: q = round(sign(x) × log(1 + |x|) / log(1 + max_val) × 127) 使得大值区间分配更多 bin
缺点: 无硬件加速(非标准格式),需要自定义 dequant kernel
|
5. SmoothQuant 思想(转移难度):
1 2 3 4 5 6 7
| 如果 outlier 出现在激活中: 找到 outlier channel → 将该 channel 的缩放因子 s 设大 激活该 channel 除以 s(outlier 被缩小) 权重对应 channel 乘以 s(补偿) 效果: 激活分布变平坦 → 量化容易 权重接受 "难度转移" → 权重是静态的,可离线精确量化
|
实际工程推荐:
- 如果是权重 outlier → per-group 量化(group_size=128)解决大部分问题
- 如果是激活 outlier → SmoothQuant 或 LLM.int8() 分离处理
- 极端情况 → 混合精度:outlier channel 保持 FP16,其余 INT8/INT4
Q: 手撕:手写一个 CUDA 算子?
(编程题)