共计 2631 个字符,预计需要花费 7 分钟才能阅读完成。
引言
CIFAR100 作为经典的细粒度图像分类数据集,包含 100 个类别的 60000 张 32×32 小尺寸图片。由于类别多、样本少,模型极易出现训练准确率(如 85%)远高于验证准确率(如 60%)的典型过拟合现象。本文将分享我在解决该问题时积累的实战经验。
技术方案详解
1. 数据增强策略对比
-
基础增强组合:
transform_train = transforms.Compose([transforms.RandomCrop(32, padding=4), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize((0.5071, 0.4867, 0.4408), (0.2675, 0.2565, 0.2761)) ])通过随机裁剪和水平翻转增加数据多样性,这是最基础的解决方案
-
高级混合增强:
# CutMix 实现示例 def cutmix(data, target, alpha=1.0): indices = torch.randperm(data.size(0)) shuffled_data = data[indices] shuffled_target = target[indices] lam = np.random.beta(alpha, alpha) bbx1, bby1, bbx2, bby2 = rand_bbox(data.size(), lam) data[:, :, bbx1:bbx2, bby1:bby2] = shuffled_data[:, :, bbx1:bbx2, bby1:bby2] # 调整 lambda 保证在 bbox 内 lam = 1 - ((bbx2 - bbx1) * (bby2 - bby1) / (data.size()[-1] * data.size()[-2])) return data, target, shuffled_target, lamMixUp 和 CutMix 通过图像混合创造新样本,能更有效抑制过拟合但实现较复杂
2. 模型正则化技术
-
Dropout 层配置:
class ResNetWithDropout(nn.Module): def __init__(self, pretrained=True): super().__init__() self.base = torchvision.models.resnet18(pretrained=pretrained) self.base.fc = nn.Sequential(nn.Dropout(0.5), # 全连接层前加入 Dropout nn.Linear(512, 100) )建议在全连接层前加入 p =0.5 的 Dropout,CNN 部分通常不加
-
权重衰减设置:
optimizer = torch.optim.SGD(model.parameters(), lr=0.1, weight_decay=5e-4, # 常用值范围 1e- 4 到 1e-3 momentum=0.9 )L2 正则化系数需与学习率搭配调整,过大可能导致模型欠拟合
3. 早停法实现
best_val_acc = 0
patience = 5
counter = 0
for epoch in range(100):
# ... 训练过程...
if val_acc > best_val_acc:
best_val_acc = val_acc
counter = 0
torch.save(model.state_dict(), 'best_model.pth')
else:
counter += 1
if counter >= patience:
print(f'Early stopping at epoch {epoch}')
break
完整代码示例
# 数据加载
train_loader = torch.utils.data.DataLoader(
datasets.CIFAR100('data', train=True, download=True,
transform=transform_train),
batch_size=128, # 适中 batch size 有利于泛化
shuffle=True
)
# 模型定义
model = ResNetWithDropout(pretrained=False)
model = model.to(device)
# 训练循环
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.1, weight_decay=5e-4)
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)
for epoch in range(100):
model.train()
for inputs, targets in train_loader:
inputs, targets = inputs.to(device), targets.to(device)
# 使用 CutMix 增强
inputs, targets_a, targets_b, lam = cutmix(inputs, targets)
outputs = model(inputs)
loss = criterion(outputs, targets_a) * lam + criterion(outputs, targets_b) * (1 - lam)
optimizer.zero_grad()
loss.backward()
optimizer.step()
scheduler.step()
实验结果对比
| 方法 | 验证准确率 | 过拟合程度 |
|---|---|---|
| 原始 ResNet18 | 58.2% | 高 |
| + 基础数据增强 | 63.7% | 中 |
| +CutMix+Dropout | 67.5% | 低 |

(示例图:优化后模型训练 / 验证曲线更接近)
避坑指南
- BN 层与数据增强的配合:
- 使用 BatchNorm 时,验证阶段需设置
model.eval()固定统计量 -
CutMix/MixUp 可能影响 BN 统计,建议在最后几个 epoch 关闭混合增强
-
超参数平衡技巧:
- 当增加 weight_decay 时,应适当提高学习率补偿
- 数据增强强度与 Dropout 概率需反向调整,避免双重抑制
开放性问题
- 对于只有 CIFAR100 10% 数据的小样本场景:
- 是否需要减少正则化强度?
-
如何选择迁移学习的冻结层数?
-
模型蒸馏方向:
- 教师模型选择是否越复杂越好?
- 温度参数 τ 如何影响细粒度分类效果?
这些问题的探索将帮助我们更深入理解过拟合本质。在实际项目中,建议根据具体业务需求选择合适的组合方案。
正文完
