深度学习入门:CNN 图像分类实战

← 返回博客首页
Python PyTorch Deep Learning CNN MNIST 计算机视觉

💡 项目背景

手写数字识别是深度学习领域的"Hello World",也是入门计算机视觉的经典项目。本项目使用 PyTorch 深度学习框架,从零开始构建卷积神经网络(CNN),实现对 MNIST 手写数字数据集的自动识别。通过这个项目,你将掌握深度学习的基本原理和实战技能。

🎯 学习目标

📚 理论基础

什么是卷积神经网络(CNN)?

CNN 是一种专门用于处理网格状数据(如图像)的深度学习模型。它通过以下核心组件提取特征:

MNIST 数据集介绍

数据规模:60,000 张训练图像 + 10,000 张测试图像

图像规格:28×28 像素的灰度图像

类别标签:0-9 共 10 个数字

应用场景:手写体识别、邮政编码识别、支票处理等

🚀 环境准备

安装依赖

# 安装 PyTorch(根据你的 CUDA 版本选择) pip install torch torchvision torchaudio # 如果使用 CPU 版本 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu # 验证安装 python -c "import torch; print(torch.__version__)" python -c "import torch; print('CUDA available:', torch.cuda.is_available())"

💻 完整实现

第一步:导入必要的库

import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader from torchvision import datasets, transforms import matplotlib.pyplot as plt import numpy as np

第二步:定义 CNN 网络结构

class CNN(nn.Module): def __init__(self): super(CNN, self).__init__() # 卷积层 1 # 输入:[batch_size, 1, 28, 28] # 输出:[batch_size, 32, 28, 28] self.conv1 = nn.Sequential( nn.Conv2d( in_channels=1, # 输入通道数(灰度图) out_channels=32, # 输出通道数(滤波器数量) kernel_size=3, # 卷积核大小 stride=1, # 步长 padding=1 # 填充,保持尺寸不变 ), nn.ReLU(), # 激活函数 nn.MaxPool2d(2) # 池化层,尺寸减半 → [batch_size, 32, 14, 14] ) # 卷积层 2 # 输入:[batch_size, 32, 14, 14] # 输出:[batch_size, 64, 14, 14] → 池化后 [batch_size, 64, 7, 7] self.conv2 = nn.Sequential( nn.Conv2d(32, 64, 3, 1, 1), nn.ReLU(), nn.MaxPool2d(2) ) # 全连接层 # 输入:[batch_size, 64*7*7] # 输出:[batch_size, 10] self.fc = nn.Sequential( nn.Linear(64 * 7 * 7, 128), # 隐藏层 nn.ReLU(), nn.Dropout(0.5), # Dropout 防止过拟合 nn.Linear(128, 10) # 输出层(10 个数字类别) ) def forward(self, x): # 卷积和池化 x = self.conv1(x) x = self.conv2(x) # 展平,准备进入全连接层 x = x.view(x.size(0), -1) # [batch_size, 64*7*7] # 全连接层 output = self.fc(x) return output

第三步:数据加载与预处理

# 定义数据转换 transform = transforms.Compose([ transforms.ToTensor(), # 转换为 Tensor transforms.Normalize((0.1307,), (0.3081,)) # 标准化 ]) # 下载并加载训练集 train_dataset = datasets.MNIST( root='./data', train=True, download=True, transform=transform ) train_loader = DataLoader( dataset=train_dataset, batch_size=64, shuffle=True # 打乱数据 ) # 加载测试集 test_dataset = datasets.MNIST( root='./data', train=False, download=True, transform=transform ) test_loader = DataLoader( dataset=test_dataset, batch_size=1000, shuffle=False )

第四步:定义训练流程

# 初始化模型、损失函数和优化器 model = CNN() criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(model.parameters(), lr=0.001) # 训练配置 EPOCHS = 5 DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu') model.to(DEVICE) def train_epoch(model, loader, optimizer, criterion, device): model.train() total_loss = 0 correct = 0 for batch_idx, (data, target) in enumerate(loader): data, target = data.to(device), target.to(device) # 前向传播 output = model(data) loss = criterion(output, target) # 反向传播 optimizer.zero_grad() loss.backward() optimizer.step() total_loss += loss.item() pred = output.argmax(dim=1) correct += pred.eq(target).sum().item() avg_loss = total_loss / len(loader) accuracy = 100. * correct / len(loader.dataset) return avg_loss, accuracy # 开始训练 for epoch in range(1, EPOCHS + 1): loss, acc = train_epoch(model, train_loader, optimizer, criterion, DEVICE) print(f'Epoch {epoch}: Loss={loss:.4f}, Accuracy={acc:.2f}%')

第五步:模型评估

def evaluate(model, loader, criterion, device): model.eval() test_loss = 0 correct = 0 with torch.no_grad(): for data, target in 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() avg_loss = test_loss / len(loader) accuracy = 100. * correct / len(loader.dataset) return avg_loss, accuracy # 在测试集上评估 test_loss, test_acc = evaluate(model, test_loader, criterion, DEVICE) print(f'\nTest Results: Loss={test_loss:.4f}, Accuracy={test_acc:.2f}%')

第六步:可视化预测结果

def visualize_predictions(model, dataset, num_samples=10): model.eval() fig, axes = plt.subplots(2, 5, figsize=(12, 5)) axes = axes.flatten() for i in range(num_samples): img, label = dataset[i] # 添加批次维度 img_tensor = img.unsqueeze(0).to(DEVICE) # 预测 with torch.no_grad(): output = model(img_tensor) prediction = output.argmax(dim=1).item() # 显示图像 axes[i].imshow(img.squeeze(), cmap='gray') axes[i].set_title(f'Pred: {prediction}\nTrue: {label}') axes[i].axis('off') plt.tight_layout() plt.savefig('predictions.png', dpi=300) plt.show() # 可视化预测结果 visualize_predictions(model, test_dataset)

🔧 模型优化技巧

1. 学习率调整

# 使用学习率调度器 scheduler = optim.lr_scheduler.StepLR( optimizer, step_size=10, # 每 10 个 epoch 调整一次 gamma=0.1 # 学习率衰减为原来的 0.1 倍 ) # 在每个 epoch 结束后调用 scheduler.step()

2. 数据增强

# 增加数据变换,提升泛化能力 transform_augmented = transforms.Compose([ transforms.RandomRotation(10), # 随机旋转 ±10 度 transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ])

3. Batch Normalization

# 在卷积层后添加 BN 层 self.conv1 = nn.Sequential( nn.Conv2d(1, 32, 3, 1, 1), nn.BatchNorm2d(32), # 加速收敛,提高稳定性 nn.ReLU(), nn.MaxPool2d(2) )

📊 实验结果

基础模型性能:

  • 训练 5 个 Epoch 后,测试集准确率达到 98.5%
  • 单次推理时间:< 1ms(CPU)
  • 模型大小:< 1MB

优化后性能:

  • 使用数据增强和 BN 后,准确率提升至 99.2%
  • 训练 10 个 Epoch 后可达 99.5%

🎓 总结与拓展

核心知识点回顾

下一步学习方向

📦 项目地址:GitHub - pytorch_pro

📚 推荐资源:

  • PyTorch 官方教程:https://pytorch.org/tutorials/
  • 深度学习花书:《Deep Learning》by Ian Goodfellow
  • 吴恩达 Coursera 课程:《Convolutional Neural Networks》