卷积神经网络(CNN)在图像分类任务中表现出色,而ResNet(残差网络)通过引入残差连接,进一步提升了模型的性能。本文将通过一个完整的实战案例,展示如何使用CNN和ResNet对图像进行分类,并提供详细的代码实现。


案例背景

假设我们有一个图像数据集,包含以下三类图像:
0: 猫
1: 狗
2: 鸟

我们的目标是训练一个模型,能够自动对图像进行分类。


代码实现

1. 环境准备

首先,安装所需的Python库:

pip install torch torchvision matplotlib

2. 数据准备

我们使用torchvision库加载CIFAR-10数据集,并将其简化为三类(猫、狗、鸟):

import torch
from torchvision import datasets, transforms
import matplotlib.pyplot as plt

# 数据预处理
transform = transforms.Compose([
    transforms.Resize((64, 64)),  # 调整图像大小
    transforms.ToTensor(),        # 转换为张量
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))  # 归一化
])

# 加载CIFAR-10数据集
train_dataset = datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
test_dataset = datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)

# 过滤出猫、狗、鸟三类
class_names = ['cat', 'dog', 'bird']
train_dataset.data = train_dataset.data[[i for i, label in enumerate(train_dataset.targets) if label in [3, 5, 2]]]
train_dataset.targets = [label for label in train_dataset.targets if label in [3, 5, 2]]
test_dataset.data = test_dataset.data[[i for i, label in enumerate(test_dataset.targets) if label in [3, 5, 2]]]
test_dataset.targets = [label for label in test_dataset.targets if label in [3, 5, 2]]

# 重新映射标签
label_map = {3: 0, 5: 1, 2: 2}
train_dataset.targets = [label_map[label] for label in train_dataset.targets]
test_dataset.targets = [label_map[label] for label in test_dataset.targets]

# 创建数据加载器
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=32, shuffle=True)
test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=32, shuffle=False)

# 可视化部分数据
def show_images(images, labels):
    plt.figure(figsize=(10, 5))
    for i in range(10):
        plt.subplot(2, 5, i+1)
        plt.imshow(images[i].permute(1, 2, 0))
        plt.title(class_names[labels[i]])
        plt.axis('off')
    plt.show()

# 显示部分训练数据
images, labels = next(iter(train_loader))
show_images(images, labels)

3. 模型定义

我们定义一个简单的CNN模型和一个ResNet模型:

import torch.nn as nn
import torch.nn.functional as F

# 定义简单的CNN模型
class SimpleCNN(nn.Module):
    def __init__(self):
        super(SimpleCNN, self).__init__()
        self.conv1 = nn.Conv2d(3, 16, 3, padding=1)
        self.conv2 = nn.Conv2d(16, 32, 3, padding=1)
        self.fc1 = nn.Linear(32 * 16 * 16, 256)
        self.fc2 = nn.Linear(256, 3)

    def forward(self, x):
        x = F.relu(self.conv1(x))
        x = F.max_pool2d(x, 2)
        x = F.relu(self.conv2(x))
        x = F.max_pool2d(x, 2)
        x = x.view(-1, 32 * 16 * 16)
        x = F.relu(self.fc1(x))
        x = self.fc2(x)
        return x

# 加载预训练的ResNet模型
from torchvision.models import resnet18

class ResNetModel(nn.Module):
    def __init__(self):
        super(ResNetModel, self).__init__()
        self.resnet = resnet18(pretrained=True)
        self.resnet.fc = nn.Linear(self.resnet.fc.in_features, 3)

    def forward(self, x):
        return self.resnet(x)

4. 模型训练

训练CNN模型:

# 定义设备
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

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

# 训练函数
def train(model, train_loader, criterion, optimizer, num_epochs=5):
    model.train()
    for epoch in range(num_epochs):
        running_loss = 0.0
        for i, (inputs, labels) in enumerate(train_loader):
            inputs, labels = inputs.to(device), labels.to(device)
            optimizer.zero_grad()
            outputs = model(inputs)
            loss = criterion(outputs, labels)
            loss.backward()
            optimizer.step()
            running_loss += loss.item()
            if (i+1) % 100 == 0:
                print(f"Epoch [{epoch+1}/{num_epochs}], Step [{i+1}/{len(train_loader)}], Loss: {running_loss/100:.4f}")
                running_loss = 0.0

# 训练模型
train(model, train_loader, criterion, optimizer, num_epochs=5)

5. 模型评估

评估CNN模型:

def evaluate(model, test_loader):
    model.eval()
    correct = 0
    total = 0
    with torch.no_grad():
        for inputs, labels in test_loader:
            inputs, labels = inputs.to(device), labels.to(device)
            outputs = model(inputs)
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
    print(f"Test Accuracy: {100 * correct / total:.2f}%")

# 评估模型
evaluate(model, test_loader)

6. 使用ResNet模型

训练和评估ResNet模型:

# 初始化ResNet模型
resnet_model = ResNetModel().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(resnet_model.parameters(), lr=0.001)

# 训练ResNet模型
train(resnet_model, train_loader, criterion, optimizer, num_epochs=5)

# 评估ResNet模型
evaluate(resnet_model, test_loader)

案例扩展

1. 数据增强

通过数据增强(如随机旋转、翻转、裁剪等)提高模型的泛化能力:

transform = transforms.Compose([
    transforms.RandomHorizontalFlip(),
    transforms.RandomRotation(10),
    transforms.Resize((64, 64)),
    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])

2. 更深的模型

尝试使用更深的ResNet模型(如ResNet50或ResNet101),并调整超参数以优化性能。

3. 迁移学习

在预训练的ResNet模型上进行迁移学习,冻结部分层并微调最后几层。


总结

本文通过一个完整的实战案例,展示了如何使用CNN和ResNet对图像进行分类。我们详细介绍了数据准备、模型定义、训练、评估的步骤,并提供了可运行的代码。希望本文能为读者在实际项目中应用深度学习模型提供实用的指导和启发。

Logo

更多推荐