本课目标

从 MNIST 的"标准答案"走向真实图像任务:处理脏数据、数据增强、迁移学习,完成一个能落地的垃圾分类器。


MNIST 是实验室里的玩具——7 万张 28×28 的灰度图,干干净净。你工作中的图像项目呢?数据不够、照片模糊、类别极度不平衡。本讲用真实的垃圾分类任务,教你处理这三个"真实世界专属问题"。

核心内容

数据管道:目录即标签

data/garbage/
├── train/
│   ├── recyclable/  img001.jpg ...
│   ├── kitchen/     ...
│   ├── harmful/     ...
│   └── other/
└── val/  (同结构)

ImageFolder 自动从目录结构建索引——这是工程上最简单的标注格式,不需要额外的 CSV 或 JSON。

数据增强:让 1 张图变 10 张

train_transform = transforms.Compose([
    transforms.RandomResizedCrop(224, scale=(0.6, 1.0)),
    transforms.RandomHorizontalFlip(),
    transforms.ColorJitter(brightness=0.3, contrast=0.3),
    transforms.RandomRotation(15),
    transforms.ToTensor(),
    transforms.Normalize([0.485, 0.456, 0.406],
                         [0.229, 0.224, 0.225]),
])

增强的本质:告诉模型"翻转的可乐瓶还是可乐瓶,灯光暗的可乐瓶还是可乐瓶"——这些变化不改变类别。但验证集绝不随机增强,只做确定性的 resize + 归一化。这是新手最容易忽略的纪律。

为什么归一化用的是 [0.485, 0.456, 0.406] 这几个数?它们是 ImageNet 120 万张图的 RGB 通道均值和标准差——所有 ImageNet 预训练模型都用这组值训练的,你必须跟它保持一致的输入分布。

迁移学习:数据不够时的唯一出路

几千张图从零训练 CNN → 过拟合。迁移学习是解药:拿 ImageNet 预训练的 ResNet18,冻结骨干,只训分类头。

from torchvision import models

model = models.resnet18(weights='IMAGENET1K_V1')
for param in model.parameters():
    param.requires_grad = False
model.fc = nn.Linear(model.fc.in_features, 4)

预训练模型已经在 120 万张图上学过"边缘/纹理/形状"这些通用视觉特征。我们只教它"这些特征怎么组合成四类垃圾"——几千张图就能到 90%+。效果不够再解冻最后几层微调,学习率调小一个数量级。

类别不平衡

有害垃圾样本通常最少,但识别它的重要性最高。对策:WeightedRandomSampler 过采样少数类;或在 loss 函数里给少数类加权——让它"错判有害垃圾的代价更高"。

动手练习

  1. 搭建数据管道,对比"随机初始化 vs 迁移学习"的准确率差距
  2. 逐步解冻 ResNet 最后几层,找到性价比最高的微调位置