大模型好用但难部署。模型压缩让我们在保持性能的同时大幅减少模型大小和推理延迟。
GPT-3: 1750亿参数 → 350GB(FP32) → 手机装不下
目标:减少内存、加速推理、降低功耗、部署到边缘设备
| 方法 | 原理 | 压缩比 | 精度损失 |
|---|---|---|---|
| 剪枝 | 移除不重要的权重/通道 | 2-10x | 小 |
| 量化 | 降低数值精度(FP32→INT8) | 2-4x | 小-中 |
| 知识蒸馏 | 小模型学大模型 | 任意 | 中 |
| 低秩分解 | 矩阵分解为小矩阵 | 2-5x | 小 |
FP32 → INT8 映射:
q = round(x / scale + zero_point)
x_dequant = (q - zero_point) × scale
scale = (xmax - xmin) / (qmax - qmin)
训练后量化(PTQ):训练完后直接量化,简单快速
量化感知训练(QAT):训练时模拟量化,精度更好
import torch
import torch.nn as nn
# 模型压缩实验
class SmallModel(nn.Module):
def __init__(self):
super().__init__()
self.net = nn.Sequential(
nn.Linear(20, 128), nn.ReLU(),
nn.Linear(128, 64), nn.ReLU(),
nn.Linear(64, 10),
)
def forward(self, x):
return self.net(x)
# 训练原始模型
torch.manual_seed(42)
X = torch.randn(500, 20)
y = torch.randint(0, 10, (500,))
model = SmallModel()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
loss_fn = nn.CrossEntropyLoss()
for epoch in range(100):
optimizer.zero_grad()
loss = loss_fn(model(X), y)
loss.backward()
optimizer.step()
# 原始精度
orig_acc = (model(X).argmax(1) == y).float().mean().item()
orig_size = sum(p.numel() * p.element_size() for p in model.parameters())
print(f"原始模型: acc={orig_acc:.2%}, 大小={orig_size/1024:.1f}KB")
# 量化:手动实现INT8
def quantize_tensor(tensor):
xmin, xmax = tensor.min(), tensor.max()
scale = (xmax - xmin) / 255
zero_point = round(-xmin / scale)
q = torch.clamp(torch.round(tensor / scale + zero_point), 0, 255).to(torch.uint8)
return q, scale, zero_point
def dequantize_tensor(q, scale, zero_point):
return (q.float() - zero_point) * scale
# 量化模型参数
print("\n=== 量化效果 ===")
for name, param in model.named_parameters():
q, scale, zp = quantize_tensor(param.data)
deq = dequantize_tensor(q, scale, zp)
error = (param.data - deq).abs().mean().item()
orig_bytes = param.numel() * 4
quant_bytes = param.numel() # INT8 = 1 byte
print(f"{name}: 量化误差={error:.6f}, 大小 {orig_bytes}→{quant_bytes} ({quant_bytes/orig_bytes:.0%})")
# 剪枝:移除小权重
print("\n=== 剪枝效果 ===")
for sparsity in [0.3, 0.5, 0.7, 0.9]:
model_copy = SmallModel()
model_copy.load_state_dict(model.state_dict())
# 按幅度剪枝
total_params = 0
pruned_params = 0
for name, param in model_copy.named_parameters():
threshold = torch.quantile(param.data.abs(), sparsity)
mask = param.data.abs() > threshold
param.data *= mask.float()
total_params += param.numel()
pruned_params += (mask == 0).sum().item()
acc = (model_copy(X).argmax(1) == y).float().mean().item()
print(f"稀疏度={sparsity:.0%}: 实际剪枝={pruned_params/total_params:.1%}, acc={acc:.2%}")
# 知识蒸馏概念
print("\n=== 知识蒸馏 ===")
print("教师模型: 原始SmallModel (3层)")
print("学生模型: 更小网络 (2层)")
student = nn.Sequential(nn.Linear(20, 32), nn.ReLU(), nn.Linear(32, 10))
student_params = sum(p.numel() for p in student.parameters())
teacher_params = sum(p.numel() for p in model.parameters())
print(f"教师参数量: {teacher_params:,}, 学生参数量: {student_params:,}")
print(f"压缩比: {teacher_params/student_params:.1f}x")
本课的核心知识点构成了训练技巧学习的重要一环。让我们从更高维度审视这些知识之间的关系:
⚠️ 初学者常犯的错误:
💡 最佳实践:
深度学习是一个整体——每一课的知识都不是孤立的:
💡 继续深入的学习路径:
使用torch.quantization实现完整的PTQ流程。
实现QAT,对比PTQ的精度损失。
训练一个教师模型,蒸馏到更小的学生模型。
⚠️ 需要避免的典型错误:
💡 提升模型性能的系统方法:
理解本课内容需要将其置于更大的知识网络中。每个核心概念都不是孤立的,它们相互关联、相互支撑:
建立正确的直觉比记住公式更重要:
1. 维度直觉:高维空间中,数据分布与低维直觉截然不同(维度诅咒)
2. 信息瓶颈:神经网络逐层压缩信息,保留任务相关的、丢弃无关的
3. 流形假设:高维数据实际分布在低维流形上,学习即发现流形结构
4. 偏差-方差权衡:简单模型欠拟合(高偏差),复杂模型过拟合(高方差)
本课作为训练技巧阶段的核心内容,与前后课程紧密关联:
| 关联课程 | 关联点 | 协同效应 |
|---|---|---|
| 前序课程 | 提供了本课的基础知识 | 循序渐进的理解 |
| 后续课程 | 本课内容是后续的基础 | 逐步构建能力 |
| 平行课程 | 同一阶段的互补知识 | 多角度深入理解 |
| 实战项目 | 综合运用所有知识 | 理论与实践结合 |
💡 准备面试时,确保能回答以下问题:
掌握本课内容后,可以通过以下方式继续深入:
让大模型变小——你已经掌握了模型部署的关键技能!