欢迎光临
我们一直在努力

量化(Quantization)模型后,如何重新评估其鲁棒性?

模型量化(Quantization)是将浮点精度(FP32)的模型权重和激活值转换为较低精度(通常是INT8)的过程,以显著减少模型大小、降低内存带宽需求并加速推理。然而,这种精度降低不可避免地引入了量化误差,这不仅可能导致模型的基线精度略微下降,更关键的是,它可能严重损害模型对输入扰动(即鲁棒性)的抵抗能力。

本文将聚焦于如何使用对抗性攻击(Adversarial Attacks),具体是Projected Gradient Descent (PGD)攻击,来重新、精确地评估量化(INT8)模型的鲁棒性。

1. 环境准备与基线模型(FP32)

我们使用PyTorch作为基础框架,并使用一个简单的CNN模型(LeNet-like)在MNIST数据集上进行演示。

1.1 安装依赖

pip install torch torchvision advertorch

1.2 定义模型和加载数据

import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from advertorch.attacks import PGDAttack

# 检查设备
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# 数据加载器 (使用小型数据集进行演示)
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.1307,), (0.3081,))
])

train_loader = torch.utils.data.DataLoader(
    datasets.MNIST('./data', train=True, download=True, transform=transform),
    batch_size=64, shuffle=True)

test_loader = torch.utils.data.DataLoader(
    datasets.MNIST('./data', train=False, transform=transform),
    batch_size=1000, shuffle=False)

# 定义一个简单的CNN模型
class SimpleCNN(nn.Module):
    def __init__(self):
        super(SimpleCNN, self).__init__()
        self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
        self.relu1 = nn.ReLU()
        self.pool1 = nn.MaxPool2d(2)
        self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
        self.relu2 = nn.ReLU()
        self.pool2 = nn.MaxPool2d(2)
        self.fc = nn.Linear(320, 10)

    def forward(self, x):
        x = self.pool1(self.relu1(self.conv1(x)))
        x = self.pool2(self.relu2(self.conv2(x)))
        x = x.view(-1, 320)
        x = self.fc(x)
        return x

# 加载或训练FP32模型(此处假设已预训练并加载)
model_fp32 = SimpleCNN().to(device)
# 假设 model_fp32 已经训练完成并具有高精度
print(f"FP32 Model loaded and ready.")

2. 实施后训练静态量化 (PTQ)

我们采用PyTorch的后训练静态量化(Post-Training Static Quantization, PTQ)。PTQ需要校准(Calibration)步骤,通过运行少量未标记数据来收集激活值的统计信息(如最小值和最大值)。

2.1 准备量化和校准

# 1. 准备量化模型
model_fp32.eval() 
model_fp32.qconfig = torch.quantization.get_default_qconfig('fbgemm') # 使用适用于x86 CPU的配置
model_quantized = torch.quantization.prepare(model_fp32, inplace=False)

# 2. 校准过程 (使用训练数据的前1000个样本)
print("Starting Calibration...")
for batch_idx, (data, target) in enumerate(train_loader):
    if batch_idx * len(data) >= 1000: 
        break
    model_quantized(data.to(device))

# 3. 完成量化并转换为INT8
model_int8 = torch.quantization.convert(model_quantized, inplace=False)
model_int8.eval()
print("Quantization complete. Model is now INT8.")

3. 鲁棒性评估:FP32 vs. INT8

我们将定义一个通用的评估函数,首先评估模型在干净数据上的精度,然后使用PGD攻击来评估其在对抗性样本上的鲁棒精度。

3.1 定义攻击和评估函数

PGD(Projected Gradient Descent)是一种强大的迭代攻击方法,通常用于衡量模型的鲁棒性。

# 定义PGD攻击参数
epsilon = 0.1 # 扰动预算
nb_iter = 10 # 迭代次数
eps_iter = 0.01 # 每步步长

def evaluate_robustness(model, data_loader, model_name):
    correct_clean = 0
    correct_adv = 0
    total = 0

    # 初始化PGD攻击器
    adversary = PGDAttack(
        model, loss_fn=nn.CrossEntropyLoss(reduction="sum"), eps=epsilon, 
        nb_iter=nb_iter, eps_iter=eps_iter, clip_min=-0.459, clip_max=2.821, targeted=False)

    for data, target in data_loader:
        data, target = data.to(device), target.to(device)
        total += target.size(0)

        # 1. 干净数据评估
        output_clean = model(data)
        pred_clean = output_clean.argmax(dim=1, keepdim=True)
        correct_clean += pred_clean.eq(target.view_as(pred_clean)).sum().item()

        # 2. 对抗性攻击生成与评估
        # 注意:对于量化模型,PGD攻击是在FP32输入空间进行的,但推理过程是在INT8完成的。
        adv_data = adversary.perturb(data, target)
        output_adv = model(adv_data)
        pred_adv = output_adv.argmax(dim=1, keepdim=True)
        correct_adv += pred_adv.eq(target.view_as(pred_adv)).sum().item()

    clean_acc = 100. * correct_clean / total
    adv_acc = 100. * correct_adv / total

    print(f"[{model_name}] 干净精度: {clean_acc:.2f}%")
    print(f"[{model_name}] PGD鲁棒精度 (epsilon={epsilon}): {adv_acc:.2f}%")
    return clean_acc, adv_acc

# 由于评估过程较慢,此处仅使用测试集的前两个批次进行演示
small_test_loader = torch.utils.data.DataLoader(
    datasets.MNIST('./data', train=False, transform=transform),
    batch_size=1000, shuffle=False)

# 运行评估
print("\n--- 评估 FP32 基线模型 ---")
evaluate_robustness(model_fp32, small_test_loader, "FP32")

print("\n--- 评估 INT8 量化模型 ---")
evaluate_robustness(model_int8, small_test_loader, "INT8")

4. 结果分析与结论

通常情况下,运行上述代码后会观察到以下现象:

  1. FP32模型: 干净精度很高(~99%),但对抗性鲁棒精度(PGD)显著下降(可能低于10%或20%)。这是神经网络的普遍弱点。
  2. INT8模型:
    • 干净精度: 略微低于FP32模型(例如,从99.2%降至99.0%)。
    • PGD鲁棒精度: 可能会比FP32模型进一步恶化(例如,从15%降至10%),或在某些情况下出现意想不到的波动。这种鲁棒性的下降是量化误差(特别是激活值统计信息的不精确性)导致的。量化噪声改变了模型的决策边界,使其对PGD生成的微小扰动更加敏感。

缓解策略

如果鲁棒性下降不可接受,部署人员应考虑使用更高级的量化方法:

  • 量化感知训练 (QAT): 在训练过程中引入量化误差的模拟,使模型提前适应量化,这是同时保持高精度和高鲁棒性的最有效方法。
【本站文章皆为原创,未经允许不得转载】:汤不热吧 » 量化(Quantization)模型后,如何重新评估其鲁棒性?
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址