从零实现卷积神经网络:实验过程详解与代码注释指南

1次阅读
没有评论

共计 2402 个字符,预计需要花费 7 分钟才能阅读完成。

image.webp

核心概念

卷积神经网络 (CNN) 通过局部连接和权重共享高效处理图像数据。其核心组件工作原理如下:

  1. 卷积层:使用滑动窗口(卷积核)提取局部特征,计算公式为:
    $$(f * g)(x,y) = \sum_{i=-k}^{k}\sum_{j=-k}^{k} f(x+i,y+j)g(i,j)$$
    其中 $f$ 是输入,$g$ 是卷积核,$k$ 是核半径

  2. 池化层:降低特征图尺寸(常用最大池化),保留显著特征的同时减少计算量

从零实现卷积神经网络:实验过程详解与代码注释指南

实验设计

数据预处理

  1. 标准化 :将像素值从[0,255] 线性变换到[-1,1]
  2. 数据增强
  3. 随机水平翻转(概率 0.5)
  4. 随机旋转(±15 度)
  5. 颜色抖动(亮度 / 对比度调整)

模型架构

采用经典 LeNet- 5 改进结构:

  1. 输入层 → Conv1(3@32×32 → 6@28×28)
  2. MaxPool(6@28×28 → 6@14×14)
  3. Conv2(6@14×14 → 16@10×10)
  4. MaxPool(16@10×10 → 16@5×5)
  5. 全连接层(400 → 120 → 84 → 10)

超参数配置

  • 损失函数:交叉熵损失
  • 优化器:Adam(lr=0.001)
  • Batch Size:64
  • Epochs:50

代码实现

import torch
import torchvision
import torch.nn as nn
import torch.optim as optim
from torchvision import transforms

# 数据预处理管道
transform = transforms.Compose([transforms.RandomHorizontalFlip(),
    transforms.RandomRotation(15),
    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])

# 加载 CIFAR-10 数据集
trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True)

# 定义 CNN 模型
class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        # 卷积层 1:输入 3 通道,输出 6 通道,5x5 卷积核
        self.conv1 = nn.Conv2d(3, 6, 5)
        # 池化层:2x2 窗口,步长 2
        self.pool = nn.MaxPool2d(2, 2)
        # 卷积层 2:输入 6 通道,输出 16 通道,5x5 卷积核
        self.conv2 = nn.Conv2d(6, 16, 5)
        # 全连接层
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        x = self.pool(torch.relu(self.conv1(x)))
        x = self.pool(torch.relu(self.conv2(x)))
        x = x.view(-1, 16 * 5 * 5)
        x = torch.relu(self.fc1(x))
        x = torch.relu(self.fc2(x))
        x = self.fc3(x)
        return x

# 初始化模型、损失函数和优化器
model = CNN()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 训练循环
for epoch in range(50):
    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
        inputs, labels = data

        # 梯度清零
        optimizer.zero_grad()

        # 前向传播 + 反向传播 + 优化
        outputs = model(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        # 打印统计信息
        running_loss += loss.item()
        if i % 200 == 199:
            print(f'[{epoch + 1}, {i + 1:5d}] loss: {running_loss / 200:.3f}')
            running_loss = 0.0

结果分析

训练曲线

关键观察点:
1. 前 10 个 epoch 损失快速下降
2. 20-30epoch 出现验证集准确率平台期
3. 40epoch 后出现轻微过拟合(训练精度持续上升但验证精度停滞)

应对策略

  1. 早停机制:当验证损失连续 3 个 epoch 不下降时终止训练
  2. 增加 Dropout 层:在全连接层之间添加 dropout(p=0.5)
  3. L2 正则化:优化器添加 weight_decay 参数(建议 0.001)

避坑指南

  1. 学习率设置不当
  2. 现象:损失值震荡或下降缓慢
  3. 解决:使用学习率调度器(如 ReduceLROnPlateau)

  4. 批量归一化层缺失

  5. 现象:深层网络训练困难
  6. 解决:在卷积层后添加 nn.BatchNorm2d

  7. 数据未打乱

  8. 现象:模型收敛方向不稳定
  9. 解决:确保 DataLoader 的 shuffle=True

实验环境

  • GPU:NVIDIA RTX 3060 (12GB)
  • PyTorch:1.10.0+cu113
  • Python:3.8.10

总结

通过本实验,我们完整实现了 CNN 从数据准备到模型训练的全流程。关键收获包括:
1. 理解卷积 / 池化层的实际运作方式
2. 掌握 PyTorch 构建模型的标准化流程
3. 学会分析训练曲线识别过拟合

建议下一步尝试:
– 使用 ResNet 等现代架构提升准确率
– 添加 TensorBoard 可视化工具
– 在自定义数据集上测试模型泛化能力

正文完
 0
评论(没有评论)