Files
2026-05-16 17:16:51 +08:00

4.7 KiB
Raw Permalink Blame History

量化基础与INT8

量化概述

量化Quantization是将模型参数和计算从高精度FP32/FP16转换为低精度INT8/INT4/INT2表示的技术目的是降低显存占用、加速推理、减少计算量。

量化优势

精度 内存占用 计算量相对FP32 精度损失
FP32 4 bytes/param 1x
FP16/BF16 2 bytes/param ~1x 极小
INT8 1 byte/param ~0.25x 较小
INT4 0.5 byte/param ~0.125x 中等
INT2 0.25 byte/param ~0.0625x 较大

量化基本原理

量化映射

将连续值映射到离散值域:

x_float = scale * x_quant + zero_point
x_quant = round((x_float - zero_point) / scale)

其中:

  • scale:量化缩放因子
  • zero_point:零点(用于非对称量化)

量化精度类型

1. 对称量化Symmetric Quantization

使用零对称的映射zero_point = 0

x_quant = round(x_float / scale)
x_float = x_quant * scale

scale = max(|x_float|) / 127  (INT8范围: -127~127)

特点

  • 数值范围关于0对称
  • 适合权重分布接近正态分布的情况
  • 实现简单

2. 非对称量化Asymmetric Quantization

允许zero_point非0

scale = (max(x_float) - min(x_float)) / 255
zero_point = -min(x_float) / scale

x_quant = round((x_float - zero_point) / scale)
x_float = scale * x_quant + zero_point

特点

  • 可处理偏置分布
  • 适合激活值(通常不是零对称)

量化粒度

Per-Tensor量化

整个张量使用同一个scale和zero_point

scale = max(|W|) / 127
W_quant = round(W / scale)

优点:实现简单,通信量小 缺点:精度损失较大(受最值限制)

Per-Channel量化

按通道layer分别设置scale

对于Linear层 weight shape: (out_features, in_features)
每个out_channel有独立的scale

优点:精度更高,每个通道自适应范围 缺点通信量增加需传输多个scale

Per-Token量化

按token维度量化主要用于激活值

每行sequence length维度有独立scale
适合Attention score等动态范围大的张量

INT8推理流程

推理框架

Input FP32 → Quantize → INT8 GEMM → Dequantize → Output FP32
           ↓
        Lookup Table (可选)

核心计算INT8 GEMM

硬件支持INT8矩阵乘法加速如NVIDIA Tensor Core

  • INT8 输入: (M, K) ⊗ (K, N) → INT32 累加
  • INT32 → FP16/FP32 反量化输出

量化流程示例

# 权重量化
W_fp32 = weight.data
W_max = W_fp32.abs().max()
W_scale = W_max / 127.0
W_int8 = (W_fp32 / W_scale).round().clamp(-128, 127)

# 激活量化(动态)
X_fp32 = input.data
X_scale = X_fp32.abs().max(dim=-1, keepdim=True)[0] / 127.0
X_int8 = (X_fp32 / X_scale).round().clamp(-128, 127)

# INT8矩阵乘法
Y_int32 = F.linear(X_int8, W_int8, bias=None)

# 反量化
Y_fp32 = Y_int32 * X_scale * W_scale

量化误差分析

量化误差来源

  1. 截断误差:超出量化范围的值被截断
  2. 舍入误差round操作的近似误差
  3. 尺度误差scale选取不最优

误差度量

# 量化误差计算
error = W_fp32 - W_dequantized  # 反量化后误差
mse = (error ** 2).mean()
snr = 10 * torch.log10(var(W_fp32) / var(error))

误差传播

量化误差在网络中的传播:

  • 卷积层:误差累积,但可被激活函数平滑
  • 残差连接:误差直接累积,影响显著
  • Softmax:指数放大误差,需特别注意

训练后量化Post-Training Quantization, PTQ

静态量化Static Quantization

预计算激活值范围,需要校准数据集:

model.eval()
model.qconfig = QConfig(activation=None, weight=None)  # 指定量化配置
torch.quantization.prepare(model, inplace=True)
# 校准
with torch.no_grad():
    for data in calibration_loader:
        model(data)
torch.quantization.convert(model, inplace=True)

动态量化Dynamic Quantization

运行时动态决定激活范围:

# 权重静态量化,激活动态量化
model_dynamic = torch.quantization.quantize_dynamic(
    model, {nn.Linear}, dtype=torch.qint8
)

量化感知训练Quantization-Aware Training, QAT

在前向传播中模拟量化效果:

# Fake quantization
x_quant = (x / scale).round() * scale

# 使用STE (Straight-Through Estimator) 反向传播
# gradient = gradient.round() * scale ≈ gradient

QAT vs PTQ

特性 PTQ QAT
训练需求 不需要 需要微调
精度 较低 较高
成本 中等
适用场景 快速部署 追求精度