3. 模型量化基础
任务目标
本案例使用PyTorch实现后训练量化(Post-Training Quantization, PTQ),将一个预训练好的全精度CNN模型(ResNet-18)量化为INT8动态量化模型,并在CIFAR-10验证集上对比量化前后的Top-1准确率,评估精度损失是否在可接受范围内。
环境与依赖
- Python 3.8+
- PyTorch ≥ 1.12(需包含
torch.ao.quantization模块) - torchvision ≥ 0.13
- numpy
实现原理
后训练量化无需重新训练模型,直接对预训练权重进行量化。本案例采用动态量化(Dynamic Quantization),即权重在推理前量化为INT8,激活值在运行时动态量化。该方法无需校准数据集,实现简单,适合CPU端部署。核心步骤为:加载预训练模型 → 融合特定层 → 配置量化配置 → 转换模型 → 评估对比。
关键完整代码
import torch
import torch.nn as nn
import torchvision
import torchvision.transforms as transforms
from torch.ao.quantization import quantize_dynamic, QConfig, default_dynamic_qconfig
# 1. 数据加载(CIFAR-10验证集)
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))
])
val_dataset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)
val_loader = torch.utils.data.DataLoader(val_dataset, batch_size=128, shuffle=False, num_workers=2)
# 2. 加载预训练模型(ResNet-18,CIFAR-10适配版)
model = torchvision.models.resnet18(num_classes=10)
model.load_state_dict(torch.load('resnet18_cifar10.pth', map_location='cpu'))
model.eval()
# 3. 定义评估函数
def evaluate(model, loader):
correct, total = 0, 0
with torch.no_grad():
for images, labels in loader:
outputs = model(images)
_, predicted = torch.max(outputs, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
return 100.0 * correct / total
# 4. 量化前精度
fp32_acc = evaluate(model, val_loader)
print(f"FP32 Top-1 Accuracy: {fp32_acc:.2f}%")
# 5. 动态量化(仅量化Linear层)
quantized_model = quantize_dynamic(
model,
{nn.Linear}, # 只量化全连接层
dtype=torch.qint8
)
# 6. 量化后精度
int8_acc = evaluate(quantized_model, val_loader)
print(f"INT8 Dynamic Top-1 Accuracy: {int8_acc:.2f}%")
# 7. 精度损失评估
acc_loss = fp32_acc - int8_acc
print(f"Accuracy Drop: {acc_loss:.2f}%")
print(f"Relative Drop: {acc_loss / fp32_acc * 100:.2f}%")
运行方式
- 下载预训练权重文件
resnet18_cifar10.pth(训练脚本见案例2,或使用任意CIFAR-10预训练ResNet-18权重替换加载路径); - 将上述代码保存为
ptq_demo.py; - 执行命令:
python ptq_demo.py。
验证方法与通过条件
- 程序正常输出FP32与INT8的Top-1准确率,无报错;
- INT8准确率与FP32相比,下降幅度通常不超过2%(相对损失不超过5%);
- 量化后模型大小约为原来的1/4(可通过
torch.save后对比文件大小验证); - 若精度下降超过5%,检查预训练权重是否收敛良好,或改用
qnnpack后端进行静态量化。
常见问题
- 量化后推理变慢?动态量化在CPU小批量上可能不如FP32快,这是正常现象;若需加速,应使用静态量化并配合INT8算子优化。
- 模型包含BatchNorm层?动态量化不需要融合BN层,但静态量化前需调用
torch.ao.quantization.fuse_modules进行Conv-BN-ReLU融合。 - 如何验证量化模型确实使用INT8?打印
quantized_model.fc的权重dtype,应为torch.qint8。