跳转至

02 - 量化算法详解

深入了解 PTQ 、 QAT 、 GPTQ 、 AWQ 等量化算法

📖 章节概述

本章将详细介绍各种量化算法的实现原理和特点,包括 PTQ 、 QAT 、 GPTQ 、 AWQ 等。

🎯 学习目标

完成本章后,你将能够:

  • 理解各种量化算法的原理
  • 掌握量化算法的实现方法
  • 了解不同算法的优缺点
  • 能够根据场景选择合适的算法

1. PTQ (训练后量化)

1.1 算法原理

PTQ 在模型训练完成后直接进行量化,无需重新训练。主要步骤:

  1. 收集统计信息:使用校准数据收集激活值分布,必要时也统计权重范围
  2. 计算量化参数:基于统计信息计算 scale 和 zero_point
  3. 应用量化:将权重和激活值量化为目标精度

1.2 实现代码

Python
import torch
import torch.nn as nn
import numpy as np

class PTQQuantizer:
    """
    PTQ量化器
    """
    def __init__(self, bits=8, symmetric=False):
        self.bits = bits
        self.symmetric = symmetric
        self.qmin = -2**(bits-1) if symmetric else 0
        self.qmax = 2**(bits-1) - 1 if symmetric else 2**bits - 1

    def calibrate(self, model, dataloader):
        """
        校准模型

        Args:
            model: 要校准的模型
            dataloader: 校准数据加载器
        """
        model.eval()
        stats = {}

        def make_hook(name):
            def hook(module, inputs, output):
                if name not in stats:
                    weight = module.weight.detach()
                    stats[name] = {
                        'weight_min': weight.min().item(),
                        'weight_max': weight.max().item(),
                        'act_min': float('inf'),
                        'act_max': float('-inf'),
                    }

                output_tensor = output[0] if isinstance(output, tuple) else output
                output_tensor = output_tensor.detach()
                stats[name]['act_min'] = min(stats[name]['act_min'], output_tensor.min().item())
                stats[name]['act_max'] = max(stats[name]['act_max'], output_tensor.max().item())
            return hook

        hooks = []
        for name, module in model.named_modules():
            if isinstance(module, nn.Linear):
                hooks.append(module.register_forward_hook(make_hook(name)))

        try:
            with torch.no_grad():
                for batch_x, _ in dataloader:
                    _ = model(batch_x)
        finally:
            for hook in hooks:
                hook.remove()

        return stats

    def quantize_weight(self, weight, stats):
        """
        量化权重

        Args:
            weight: 权重张量
            stats: 统计信息
        """
        # 计算scale和zero_point
        weight_min = stats.get('weight_min', stats['min'])
        weight_max = stats.get('weight_max', stats['max'])

        if self.symmetric:
            scale = max(abs(weight_min), abs(weight_max)) / (2**(self.bits-1) - 1)
            zero_point = 0
        else:
            scale = (weight_max - weight_min) / (2**self.bits - 1)
            zero_point = round(self.qmin - weight_min / scale) if scale > 0 else 0
            zero_point = max(self.qmin, min(self.qmax, zero_point))

        if scale == 0:
            scale = 1.0

        # 量化
        quantized = torch.round(weight / scale + zero_point)
        quantized = torch.clamp(quantized, self.qmin, self.qmax)

        # 反量化
        dequantized = (quantized - zero_point) * scale

        return quantized, dequantized, scale, zero_point

# 使用示例
# quantizer = PTQQuantizer(bits=8, symmetric=False)
# stats = quantizer.calibrate(model, dataloader)
# quantized, dequantized, scale, zero_point = quantizer.quantize_weight(weight, stats['layer1'])

2. QAT (量化感知训练)

2.1 算法原理

QAT 在训练过程中模拟量化误差,使模型适应量化后的精度。主要特点:

  1. 前向传播:模拟量化操作
  2. 反向传播:使用直通估计器( STE )
  3. 参数更新:更新浮点数参数

2.2 实现代码

Python
import torch
import torch.nn as nn

class QuantizeLayer(nn.Module):
    """
    量化层
    """
    def __init__(self, bits=8, symmetric=False):
        super().__init__()  # super()调用父类方法
        self.bits = bits
        self.symmetric = symmetric
        self.scale = nn.Parameter(torch.ones(1))
        self.zero_point = nn.Parameter(torch.zeros(1))

    def forward(self, x):
        """
        前向传播
        """
        # 量化
        quantized = torch.round(x / self.scale) + self.zero_point

        # 截断
        qmin = -2**(self.bits-1) if self.symmetric else 0
        qmax = 2**(self.bits-1) - 1 if self.symmetric else 2**self.bits - 1
        quantized = torch.clamp(quantized, qmin, qmax)

        # 反量化(使用直通估计器)
        dequantized = (quantized - self.zero_point) * self.scale

        return dequantized

class QATModel(nn.Module):
    """
    QAT模型
    """
    def __init__(self, base_model):
        super().__init__()
        self.base_model = base_model
        self.quantize_layers = nn.ModuleList()

        # 为每个线性层添加量化层
        for name, module in base_model.named_modules():
            if isinstance(module, nn.Linear):  # isinstance检查对象类型
                self.quantize_layers.append(QuantizeLayer(bits=8))

    def forward(self, x):
        """
        前向传播
        """
        # 简化实现,实际需要更复杂的处理
        x = self.base_model(x)
        return x

# 使用示例
# qat_model = QATModel(model)
# # 训练qat_model...

3. GPTQ

3.1 算法原理

GPTQ 基于 Hessian 信息进行量化,通过最小化量化误差来优化量化参数。核心思想:

  1. 计算 Hessian 矩阵:基于校准数据计算 Hessian
  2. 优化量化参数:使用 Hessian 信息优化 scale
  3. 迭代量化:逐层或逐组量化

3.2 实现代码

Python
import torch
import torch.nn as nn

class GPTQQuantizer:
    """
    GPTQ量化器
    """
    def __init__(self, bits=4, group_size=128):
        self.bits = bits
        self.group_size = group_size

    def quantize_layer(self, weight, hessian):
        """
        量化层

        Args:
            weight: 权重张量
            hessian: Hessian矩阵
        """
        # 分组量化
        quantized_weight = torch.zeros_like(weight)

        for i in range(0, weight.shape[0], self.group_size):
            for j in range(0, weight.shape[1], self.group_size):
                # 获取权重组
                weight_group = weight[i:i+self.group_size, j:j+self.group_size]

                # 计算量化参数
                scale = weight_group.abs().max() / (2**(self.bits-1) - 1)

                # 量化
                quantized_group = torch.round(weight_group / scale)
                quantized_group = torch.clamp(
                    quantized_group,
                    -2**(self.bits-1),
                    2**(self.bits-1) - 1
                )

                # 反量化
                dequantized_group = quantized_group * scale

                # 更新量化权重
                quantized_weight[i:i+self.group_size, j:j+self.group_size] = dequantized_group

        return quantized_weight

# 使用示例
# quantizer = GPTQQuantizer(bits=4, group_size=128)
# quantized_weight = quantizer.quantize_layer(weight, hessian)

4. AWQ

4.1 算法原理

AWQ 基于激活值的分布进行量化,考虑激活值的统计特性来优化权重量化。

4.2 实现代码

Python
class AWQQuantizer:
    """
    AWQ量化器
    """
    def __init__(self, bits=4):
        self.bits = bits

    def quantize_with_activation(self, weight, activation_stats):
        """
        基于激活值统计量化

        Args:
            weight: 权重张量
            activation_stats: 激活值统计信息
        """
        # 基于激活值分布调整量化参数
        scale = activation_stats['std'] * 3 / (2**(self.bits-1) - 1)

        # 量化
        quantized = torch.round(weight / scale)
        quantized = torch.clamp(
            quantized,
            -2**(self.bits-1),
            2**(self.bits-1) - 1
        )

        # 反量化
        dequantized = quantized * scale

        return dequantized

# 使用示例
# quantizer = AWQQuantizer(bits=4)
# quantized_weight = quantizer.quantize_with_activation(weight, activation_stats)

5. 算法对比

算法 精度 速度 复杂度 适用场景
PTQ 快速部署
QAT 高精度要求
GPTQ 大模型量化
AWQ 激活值敏感模型

6. 面试题

基础题

Q1: PTQ 和 QAT 的主要区别是什么?

A: PTQ 在训练后直接量化, QAT 在训练过程中模拟量化。 QAT 精度更高但需要重新训练。

Q2: GPTQ 的核心思想是什么?

A: GPTQ 基于 Hessian 信息优化量化参数,通过最小化量化误差来提高精度。

进阶题

Q3: 如何选择合适的量化算法?

A: 需要考虑精度要求、数据可用性、计算资源和模型类型等因素。

Q4: AWQ 相比其他算法的优势是什么?

A: AWQ 考虑激活值分布,对激活值敏感的模型效果更好。

7. 练习题

基础练习

  1. 实现简单的 PTQ
Python
# 练习: 实现简单的PTQ
class SimplePTQ:
    def __init__(self, bits=8):
        # 你的代码
        pass

    def quantize(self, weight):
        # 你的代码
        pass
  1. 实现量化层
Python
# 练习: 实现量化层
class QuantizeLayer(nn.Module):
    def __init__(self, bits=8):
        # 你的代码
        pass

    def forward(self, x):
        # 你的代码
        pass

进阶练习

  1. 实现 GPTQ
Python
# 练习: 实现GPTQ
class GPTQ:
    def __init__(self, bits=4, group_size=128):
        # 你的代码
        pass

    def quantize(self, weight, hessian):
        # 你的代码
        pass
  1. 实现 AWQ
Python
# 练习: 实现AWQ
class AWQ:
    def __init__(self, bits=4):
        # 你的代码
        pass

    def quantize(self, weight, activation_stats):
        # 你的代码
        pass

8. 常见稳妥做法

✅ 推荐做法

  1. 根据需求选择算法
  2. 快速部署选 PTQ
  3. 高精度选 QAT
  4. 大模型选 GPTQ

  5. 充分校准

  6. 使用代表性数据
  7. 充分校准
  8. 验证校准效果

  9. 测试验证

  10. 在多个数据集上测试
  11. 记录量化前后性能
  12. 评估实际应用效果

❌ 避免做法

  1. 盲目追求低精度
  2. 考虑应用需求
  3. 评估精度损失
  4. 平衡性能和精度

  5. 忽略校准质量

  6. 使用高质量校准数据
  7. 充分校准
  8. 验证校准效果

  9. 单一算法

  10. 尝试多种算法
  11. 对比效果
  12. 选择与目标约束最匹配的方案

9. 总结

本章详细介绍了各种量化算法:

  • PTQ: 训练后量化,简单快速
  • QAT: 量化感知训练,精度高
  • GPTQ: 基于 Hessian 优化,适合大模型
  • AWQ: 考虑激活值分布,效果稳定

掌握这些算法的原理和实现是面试的关键。

10. 下一步

继续学习03-量化精度损失评估,了解如何评估量化的精度损失。

⚠️ 核验说明(2026-04-03):本页已完成 2026-04-03 人工复核。量化公式已修正为更合理的非对称写法;GPTQ/AWQ 代码片段仍是教学化示意,真实项目应优先使用成熟库并做端到端评测。


最后更新日期: 2026-04-03