卷积神经网络(CNN)

Admin
发布于 2026-05-27 / 1 阅读
0
0
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader

# Setup device
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print("Using device:", device)

# Transformations applied on each image
transform = transforms.Compose([
    transforms.ToTensor(),  # Convert images to tensor
    transforms.Normalize((0.5,), (0.5,))  # Normalize the images
])

# Load the dataset
train_dataset = datasets.FashionMNIST(root='./data', train=True, download=True, transform=transform)
test_dataset = datasets.FashionMNIST(root='./data', train=False, download=True, transform=transform)

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

# Define the CNN model
class FashionCNN(nn.Module):
    def __init__(self):
        super(FashionCNN, self).__init__()
        self.layer1 = nn.Sequential(
            nn.Conv2d(1, 64, kernel_size=3, padding=1),  # Input: 1 x 28 x 28, Output: 64 x 28 x 28
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2))  # Output: 64 x 14 x 14

        self.layer2 = nn.Sequential(
            nn.Conv2d(64, 64, kernel_size=3),  # Output: 64 x 12 x 12
            nn.ReLU(),
            nn.MaxPool2d(2))  # Output: 64 x 6 x 6

        self.fc1 = nn.Linear(64 * 6 * 6, 128)
        self.fc2 = nn.Linear(128, 10)  # 10 classes

    def forward(self, x):
        out = self.layer1(x)
        out = self.layer2(out)
        out = out.view(out.size(0), -1)  # Flatten the output
        out = self.fc1(out)
        out = self.fc2(out)
        return out

# Instantiate the model, loss function, and optimizer
model = FashionCNN().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# Function to train the model
def train(epoch):
    model.train()
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad()
        output = model(data)
        loss = criterion(output, target)
        loss.backward()
        optimizer.step()
    print(f'Train Epoch: {epoch} -- Loss: {loss.item():.6f}')

# Training the model
for epoch in range(1, 50):  # Train for 50 epochs
    train(epoch)


# Evaluation
model.eval()
with torch.no_grad():
    correct = 0
    total = 0
    for images, labels in test_loader:
        images, labels = images.to(device), labels.to(device)
        outputs = model(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

    print(f'Accuracy of the network on the 10000 test images: {100 * correct / total}%')

这段代码实现了一个基于PyTorch的**卷积神经网络(CNN)**,用于对**Fashion-MNIST数据集**进行图像分类。

下面我将从**实现原理**、**用途**和**注意事项**三个方面为你详细解释这段代码。

---

### 一、 实现原理(代码逐块解析)

这段代码遵循了深度学习图像分类的标准流程:数据准备 -> 模型构建 -> 模型训练 -> 模型评估。

1. 设备配置

```python

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

```

自动检测是否有可用的NVIDIA GPU。如果有,则使用GPU加速计算;如果没有,则回退到CPU。

2. 数据预处理与加载

- transforms.ToTensor():将PIL图像或NumPy数组转换为PyTorch张量,并将像素值从 [0, 255] 归一化到 [0.0, 1.0]。

- transforms.Normalize((0.5,), (0.5,)):将像素值进一步缩放到 [-1.0, 1.0] 之间,计算公式为 (x - 0.5) / 0.5。这有助于模型更快收敛。

- datasets.FashionMNIST:下载并加载Fashion-MNIST数据集(包含10类服饰灰度图,每张图大小为 1x28x28)。

- DataLoader:将数据集划分为批次batch_size=1000 表示每次喂给模型1000张图片shuffle=True 表示在训练集上打乱数据顺序,增强模型泛化能力。

3. 定义CNN模型

- Layer 1:包含一个卷积层(输入通道1,输出通道64,3x3卷积核,padding=1保证尺寸不变)、一个ReLU激活函数和一个2x2最大池化层。图像尺寸变化1x28x28 -> 64x28x28 -> 64x14x14

- Layer 2:包含一个卷积层(输入通道64,输出通道64,3x3卷积核,无padding)、一个ReLU激活函数和一个2x2最大池化层。图像尺寸变化64x14x14 -> 64x12x12 -> 64x6x6

- 全连接层(FC)out.view(out.size(0), -1) 将多维特征展平为一维向量(64 6 6 = 2304)fc1 将2304维特征降维至128维fc2 将128维特征映射到10个输出节点,对应10个分类。

4. 实例化模型、损失函数与优化器

- CrossEntropyLoss:交叉熵损失函数,适用于多分类问题,内部集成了Softmax操作。

- Adam:一种自适应学习率优化器lr=0.001 是默认且常用的学习率。

5. 训练循环

- model.train():将模型设置为训练模式(启用Dropout和BatchNorm等层的训练行为,虽然本模型没用到)。

- 前向传播计算输出 -> 计算损失 -> 梯度清零zero_grad) -> 反向传播计算梯度backward) -> 更新权重step)。

6. 评估模型

- model.eval():将模型设置为评估模式。

- torch.no_grad():禁用梯度计算,节省内存并加快计算速度。

- torch.max(outputs.data, 1):获取输出张量在维度1(类别维度)上的最大值及其索引,该索引即为预测的类别。

---

### 二、 用途

1. 图像分类任务:主要用于识别和分类服饰图像,如T恤、裤子、套衫、裙子、包、鞋子等(共10类)。

2. 深度学习入门与基准测试:Fashion-MNIST 是传统 MNIST(手写数字)的现代替代品,难度比手写数字稍大,非常适合用来验证CNN架构的有效性或作为计算机视觉的入门项目。

3. 教学与原型验证:这段代码结构完整且精简,常被用于教学演示,帮助初学者理解PyTorch中数据加载、模型定义、训练和评估的完整Pipeline。

---

### 三、 注意事项与改进建议

1. 注释与代码不一致

- 代码第46行注释写着 Train for 15 epochs,但实际代码是 for epoch in range(1, 50):,实际训练了49个epoch。建议将注释与代码保持一致。

2. 缺少验证集监控(容易过拟合)

- 当前代码只打印了训练集的Loss,并在训练完全结束后才在测试集上评估。这无法观察模型在训练过程中是否发生过拟合。**建议**:在每个epoch结束后,不仅打印Train Loss,还在测试集上计算Validation Loss/Accuracy,或者引入早停机制。

3. 模型容量与数据量的匹配

- 网络中使用了64个卷积核,对于Fashion-MNIST这种简单数据集可能参数偏多,训练49个epoch极有可能出现过拟合(训练集准确率极高,测试集准确率不再提升甚至下降)。可以考虑减少通道数(如32)或引入 nn.Dropout 层来防止过拟合。

4. Batch Size 较大

- batch_size=1000 占用显存较大。虽然大Batch Size训练更快,但有时会导致模型泛化性能下降(陷入局部最优)。如果显存允许可以保留,否则建议设置为 64 或 128,通常能得到更好的泛化效果。

5. 学习率调度器

- 固定的学习率 lr=0.001 在后期可能导致Loss震荡。建议添加学习率衰减策略,如 torch.optim.lr_scheduler.StepLR,在训练后期降低学习率以精细调整模型权重。

6. 未保存模型

- 代码训练完毕后直接输出了准确率,但没有将模型参数保存到本地。建议在评估完成后添加 torch.save(model.state_dict(), 'fashion_cnn.pth'),以便后续直接加载使用,避免重复训练。


评论