ADVANCE Day41

@浙大疏锦行

📘 Day 41 实战作业:CNN 的逆袭 ------ 卷积、增强与调度

1. 作业综述

核心目标

  1. 构建 CNN :使用 Conv2d + BatchNorm + MaxPool 的经典组合,替换掉原本的 Linear 层。
  2. 数据增强:通过随机翻转、裁剪等手段"扩充"数据集,让模型见多识广。
  3. 动态学习率 :使用 ReduceLROnPlateau 调度器,在 Loss 降不下去时自动调小学习率。

涉及知识点

  • 卷积层 : nn.Conv2d (提取局部特征)。
  • 池化层 : nn.MaxPool2d (特征降维)。
  • 归一化 : nn.BatchNorm2d (加速收敛,防止梯度消失)。
  • 调度器 : optim.lr_scheduler (动态调整 LR)。

场景类比

  • MLP: 像是一个只会死记硬背的学生。
  • CNN: 像是一个会找规律(边缘、形状)的侦探。
  • 数据增强: 像是模拟考试时故意把卷子印歪、弄脏,训练学生的适应能力。
  • LR Scheduler: 像是开车快到终点时,自动挂入低速挡,开得更稳。

步骤 1:数据增强 (Data Augmentation)

核心原理

神经网络很容易"记背"训练集。如果我们把图片水平翻转一下、稍微转个角度,对人来说还是同一张图,但对模型来说就是全新的数据。

这能极大地防止过拟合。

任务

  1. 定义增强版的 train_transform(包含随机翻转、随机裁剪)。
  2. 定义标准的 test_transform(只做归一化,不要做翻转!)。
  3. 加载 CIFAR-10 数据。
py 复制代码
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt

# 设置中文字体(可选)
plt.rcParams["font.family"] = ["SimHei"]
plt.rcParams['axes.unicode_minus'] = False

# 1. 定义数据预处理
# 训练集:需要"加料" (数据增强)
train_transform = transforms.Compose([
    transforms.RandomHorizontalFlip(p=0.5),  # 50%概率水平翻转
    transforms.RandomCrop(32, padding=4),    # 随机裁剪 (模拟物体位置变动)
    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])

# 测试集:只要标准化即可,绝对不要乱动!
test_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])

# 2. 加载数据
train_dataset = datasets.CIFAR10(root='./data', train=True, download=True, transform=train_transform)
test_dataset = datasets.CIFAR10(root='./data', train=False, download=True, transform=test_transform)

train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=1000, shuffle=False)

print(f"训练集大小: {len(train_dataset)} | 测试集大小: {len(test_dataset)}")
复制代码
训练集大小: 50000 | 测试集大小: 10000

步骤 2:搭建 CNN 模型

结构设计

我们将使用 3 个卷积块 (Conv Block),每个块包含:
Conv2d -> BatchNorm2d -> ReLU -> MaxPool2d

维度推演 (CIFAR-10 输入 32x32)

  1. Block 1 :
    • Conv (3->32) -> (32, 32, 32) (padding=1 保持大小)
    • Pool (2x2) -> (32, 16, 16)
  2. Block 2 :
    • Conv (32->64) -> (64, 16, 16)
    • Pool (2x2) -> (64, 8, 8)
  3. Block 3 :
    • Conv (64->64) -> (64, 8, 8)
    • Pool (2x2) -> (64, 4, 4)
  4. Flatten : 64 × 4 × 4 = 1024 64 \times 4 \times 4 = 1024 64×4×4=1024
  5. FC : 1024 → 64 → 10 1024 \to 64 \to 10 1024→64→10

任务

实现 CNN 类。注意 BatchNorm 通常放在 Conv 和 ReLU 之间。

py 复制代码
class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        
        # --- Block 1 ---
        self.conv1 = nn.Conv2d(3, 32, kernel_size=3, padding=1)
        self.bn1 = nn.BatchNorm2d(32) # 批归一化
        self.relu1 = nn.ReLU()
        self.pool1 = nn.MaxPool2d(2)  # 32x32 -> 16x16
        
        # --- Block 2 ---
        self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)
        self.bn2 = nn.BatchNorm2d(64)
        self.relu2 = nn.ReLU()
        self.pool2 = nn.MaxPool2d(2)  # 16x16 -> 8x8

        # --- Block 3 ---
        self.conv3 = nn.Conv2d(64, 64, kernel_size=3, padding=1)
        self.bn3 = nn.BatchNorm2d(64)
        self.relu3 = nn.ReLU()
        self.pool3 = nn.MaxPool2d(2)  # 8x8 -> 4x4
        
        # --- 全连接层 ---
        self.flatten = nn.Flatten()
        self.fc1 = nn.Linear(64 * 4 * 4, 64) # 算出 1024
        self.drop = nn.Dropout(0.5)          # 防止过拟合
        self.fc2 = nn.Linear(64, 10)         # 10分类
        
    def forward(self, x):
        # x: (Batch, 3, 32, 32)
        x = self.pool1(self.relu1(self.bn1(self.conv1(x))))
        x = self.pool2(self.relu2(self.bn2(self.conv2(x))))
        x = self.pool3(self.relu3(self.bn3(self.conv3(x))))
        
        x = self.flatten(x) # (Batch, 1024)
        x = self.drop(self.relu1(self.fc1(x))) # 复用 relu1 也可以,省点内存
        x = self.fc2(x)
        return x

# 实例化
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = CNN().to(device)
print("✅ 模型构建完成!")
复制代码
✅ 模型构建完成!

步骤 3:引入学习率调度器

核心痛点

训练后期,Loss 往往会在一个地方震荡,降不下去。这时候如果把学习率(步长)调小一点,模型可能就会"滑"进坑底(最优解)。

解决方案
optim.lr_scheduler.ReduceLROnPlateau

翻译过来就是:"如果 Loss 在高原上(Plateau)下不去了,就减少 LR"。

任务

  1. 定义 Adam 优化器。
  2. 定义 Scheduler:监测 min 模式,耐心值 patience=3(3轮不降就减),因子 factor=0.5(每次减半)。
py 复制代码
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 定义调度器
# 修正说明:移除了 verbose=True,防止报错
scheduler = optim.lr_scheduler.ReduceLROnPlateau(
    optimizer,
    mode='min',    # 我们希望 Loss 越小越好
    patience=3,    # 容忍 3 个 epoch 指标不下降
    factor=0.5     # 触发后,lr = lr * 0.5
)

步骤 4:训练与对比

任务

我们需要微调 Day 40 的训练代码:

  1. 在每个 Epoch 结束时,计算测试集 Loss。
  2. 关键一步 :调用 scheduler.step(test_loss),告诉调度器现在的表现如何。

预期结果

MLP 只有 50% 左右。

CNN 加上这些技巧后,应该能轻松达到 75% - 80%,甚至更高。

py 复制代码
# 复用 Day 40 的 train 和 test 函数(这里直接定义整合版)
def train_and_test(epochs):
    train_accs = []
    test_accs = []
    
    for epoch in range(1, epochs + 1):
        # --- 训练阶段 ---
        model.train()
        correct = 0
        total = 0
        for data, target in train_loader:
            data, target = data.to(device), target.to(device)
            optimizer.zero_grad()
            output = model(data)
            loss = criterion(output, target)
            loss.backward()
            optimizer.step()
            
            # 统计训练准确率
            pred = output.argmax(dim=1)
            correct += pred.eq(target).sum().item()
            total += target.size(0)
        
        train_acc = 100. * correct / total
        train_accs.append(train_acc)

        # --- 测试阶段 ---
        model.eval()
        test_loss = 0
        correct = 0
        with torch.no_grad():
            for data, target in test_loader:
                data, target = data.to(device), target.to(device)
                output = model(data)
                test_loss += criterion(output, target).item()
                pred = output.argmax(dim=1)
                correct += pred.eq(target).sum().item()
        
        test_loss /= len(test_loader)
        test_acc = 100. * correct / len(test_loader.dataset)
        test_accs.append(test_acc)
        
        # --- 关键:更新调度器 ---
        # 告诉它现在的验证集 Loss,让它决定要不要降学习率
        scheduler.step(test_loss)
        
        print(f"Epoch {epoch}: Train Acc: {train_acc:.2f}% | Test Acc: {test_acc:.2f}% | LR: {optimizer.param_groups[0]['lr']:.6f}")

    return train_accs, test_accs

# 开始训练 15 轮
history_train, history_test = train_and_test(15)

# 绘图
plt.plot(history_train, label='Train Acc')
plt.plot(history_test, label='Test Acc')
plt.legend()
plt.title("CNN vs MLP: The Victory")
plt.show()
复制代码
Epoch 1: Train Acc: 31.82% | Test Acc: 50.36% | LR: 0.001000
Epoch 2: Train Acc: 41.40% | Test Acc: 57.07% | LR: 0.001000
Epoch 3: Train Acc: 46.18% | Test Acc: 61.39% | LR: 0.001000
Epoch 4: Train Acc: 49.16% | Test Acc: 57.30% | LR: 0.001000
Epoch 5: Train Acc: 50.43% | Test Acc: 65.32% | LR: 0.001000
Epoch 6: Train Acc: 52.66% | Test Acc: 65.70% | LR: 0.001000
Epoch 7: Train Acc: 53.87% | Test Acc: 64.44% | LR: 0.001000
Epoch 8: Train Acc: 54.67% | Test Acc: 66.59% | LR: 0.001000
Epoch 9: Train Acc: 55.83% | Test Acc: 68.96% | LR: 0.001000
Epoch 10: Train Acc: 56.08% | Test Acc: 66.93% | LR: 0.001000
Epoch 11: Train Acc: 57.03% | Test Acc: 70.92% | LR: 0.001000
Epoch 12: Train Acc: 57.81% | Test Acc: 71.53% | LR: 0.001000
Epoch 13: Train Acc: 58.73% | Test Acc: 72.65% | LR: 0.001000
Epoch 14: Train Acc: 59.00% | Test Acc: 73.23% | LR: 0.001000
Epoch 15: Train Acc: 59.23% | Test Acc: 73.32% | LR: 0.001000

🎓 Day 41 总结:深度学习的"三板斧"

今天我们终于在 CIFAR-10 上打了一场翻身仗!准确率从 50% 飙升到了 70%+。

这归功于深度学习的"三板斧":

  1. 架构优化 (Architecture) : 用 CNN 提取空间特征,而不是粗暴的 MLP 展平。
  2. 数据增强 (Augmentation): 用随机翻转、裁剪让模型"见多识广",提升泛化能力。
  3. 训练策略 (Strategy) : 用 Scheduler 动态调整学习率,在训练后期"精细操作"。

Next Level :

虽然简单的 CNN 效果不错,但想达到 90% 以上,我们需要更深、更精妙的网络结构(如 ResNet)。

明天(Day 42),我们将学习如何**"站在巨人的肩膀上"** ------ 使用 迁移学习 (Transfer Learning),直接调用别人训练好的顶尖模型来为我们工作!

相关推荐
Deepoch2 分钟前
Deepoc 具身模型开发板在田间除草机器人自主作业中的技术应用
人工智能·机器人·具身模型·deepoc·除草机器人
Muyuan19983 分钟前
22.让 RAG Agent 更像真实产品:聊天页面优化、PDF 上传、知识库重建与检索片段展示
python·django·pdf·fastapi
ai大模型中转api测评8 分钟前
解密 GPT-5.5:原生多模态架构如何重定义 AI 逻辑推理与精准制图
大数据·人工智能·gpt·架构·api
冷雨夜中漫步11 分钟前
Claude Code源码分析——Claude Code Agent Loop 详细设计文档
java·开发语言·人工智能·ai
xixixi7777714 分钟前
英伟达Agent专用全模态模型出击,仿冒AI智能体泛滥成灾,《AI伦理安全指引》即将落地——AI治理迎来“技术-风险-规范”三重奏
人工智能·5g·安全·ai·大模型·英伟达·智能体
程序员-小李14 分钟前
uv 学习总结:从零到一掌握现代化 Python 工具链
python·学习·uv
直奔標竿16 分钟前
Java开发者AI转型第二十六课!Spring AI 个人知识库实战(五)——联网搜索增强实战
java·开发语言·人工智能·spring boot·后端·spring
数据皮皮侠AI20 分钟前
中国城市可再生能源数据集(2005-2021)|顶刊 Sci Data 11 种能源面板
大数据·人工智能·笔记·能源·1024程序员节
Python大数据分析@22 分钟前
CLI一键采集,使用Python搭建TikTok电商爬虫Agent
开发语言·爬虫·python
G311354227324 分钟前
如何用 QClaw 龙虾做一个规律作息健康助理 Agent
大数据·人工智能·ai·云计算