一、项目概述
这个项目旨在基于 PyTorch 框架构建和训练一个卷积神经网络(CNN),用于对 CIFAR-100 数据集进行分类。CIFAR-100 是一个更复杂的数据集,包含 100 类不同的图像,每类图像有 600 张,总计 60,000 张彩色图像。
主要步骤
-
数据预处理和加载:
- 加载 CIFAR-100 数据集,进行数据增强和归一化处理,以适应神经网络的输入。
- 使用 DataLoader 创建数据加载器,支持高效的批量数据处理。
-
模型构建:
- 使用
构建一个卷积神经网络,网络结构包括多个卷积层、ReLU 激活函数、最大池化层、展平层和全连接层。
- 增加模型复杂度,以适应 CIFAR-100 的多类别分类任务。
- 使用
-
训练和验证:
- 训练过程中使用交叉熵损失函数和 Adam 优化器,模型的参数通过反向传播更新。
- 训练完成后,使用验证集数据评估模型的泛化能力,记录损失和准确率。
-
模型保存:
- 在每个 epoch 结束时,保存验证集准确率最高的模型权重,确保保存的模型具有最佳性能。
-
结果可视化:
- 绘制并保存训练和验证过程中的损失和准确率曲线,以便后续分析。
二、数据集介绍
概述
CIFAR-100 数据集是由加拿大多伦多大学的 Alex Krizhevsky 和 Geoffrey Hinton 在 2009 年推出的,用于训练机器学习和计算机视觉算法。CIFAR-100 是 CIFAR 系列中的一员,另一个常用的数据集是 CIFAR-10。与 CIFAR-10 的 10 个类别不同,CIFAR-100 包含 100 个不同的类别,每类图像都相对少量,但总共包含 60,000 张彩色图像。
数据集组成
-
类别:100 个类别,每个类别有 600 张图像。
- 500 张图像用于训练。
- 100 张图像用于测试。
- 图像尺寸:每张图像为 32x32 像素,彩色图像(3 个通道,RGB)。
- 总样本数量:共 60,000 张图像,其中 50,000 张用于训练,10,000 张用于测试。
类别组织
CIFAR-100 的 100 个类别进一步分为 20 个超类(superclasses),每个超类下包含 5 个细类。每个图像都有一个 "fine" 标签(对应具体类别)和一个 "coarse" 标签(对应超类)。
例如,"superclass" 和 "fine" 类别的示例:
-
超类 (Superclass): “鱼类 (fish)”
- 细类 (Fine classes):
- 鳗鱼 (aquarium fish)
- 鲸鱼 (whale)
- 鲸鲨 (shark)
- 剑鱼 (swordfish)
- 比目鱼 (flatfish)
- 细类 (Fine classes):
数据集特点
- 复杂度高:相比 CIFAR-10 的 10 个类别,CIFAR-100 的类别数目多达 100 个,因此分类任务更具挑战性。
- 多样性:数据集中包含了不同类型的物体,涵盖了动植物、交通工具、日常用品等多个领域。
- 图像质量:图像为 32x32 像素,这种较小的尺寸对图像分类算法提出了较高的要求,需要模型具备较强的特征提取能力。
三、完整代码
import torch
import as nn
import as optim
import torchvision
import as transforms
from import DataLoader
from tqdm import tqdm
import as plt
# 设置设备(使用GPU如果可用,否则使用CPU)
device = ('cuda' if .is_available() else 'cpu')
# 数据预处理,转为Tensor并归一化
transform = ([
(), # 转为Tensor
((0.5,), (0.5,)) # 归一化
])
# 加载Fashion-MNIST训练集
trainset = (root='./data', train=True, download=True, transform=transform)
trainloader = DataLoader(trainset, batch_size=100, shuffle=True, num_workers=2)
# 加载Fashion-MNIST测试集
testset = (root='./data', train=False, download=True, transform=transform)
testloader = DataLoader(testset, batch_size=100, shuffle=False, num_workers=2)
# 构建简单的卷积神经网络
class SimpleCNN():
def __init__(self):
super(SimpleCNN, self).__init__()
= (
nn.Conv2d(1, 32, kernel_size=3, padding=1), # 卷积层1
(), # 激活函数
nn.MaxPool2d(kernel_size=2, stride=2), # 池化层1
nn.Conv2d(32, 64, kernel_size=3, padding=1), # 卷积层2
(), # 激活函数
nn.MaxPool2d(kernel_size=2, stride=2), # 池化层2
nn.Conv2d(64, 128, kernel_size=3, padding=1), # 卷积层3
(), # 激活函数
nn.MaxPool2d(kernel_size=2, stride=2), # 池化层3
(), # 展平
(128*3*3, 256), # 全连接层1
(), # 激活函数
(256, 10) # 全连接层2(输出层)
)
def forward(self, x):
return (x)
# 定义训练和验证的主函数
def main():
# 实例化网络并移动到设备(GPU或CPU)
net = SimpleCNN().to(device)
# 定义损失函数和优化器
criterion = ()
optimizer = ((), lr=0.001)
# 训练和验证
num_epochs = 30 # 训练的轮数
best_acc = 0.0 # 初始化最佳准确率
train_losses = [] # 用于保存训练损失
test_losses = [] # 用于保存测试损失
train_accuracies = [] # 用于保存训练准确率
test_accuracies = [] # 用于保存测试准确率
for epoch in range(num_epochs):
() # 设置网络为训练模式
running_loss = 0.0
correct = 0
total = 0
# 使用tqdm显示训练进度条
train_bar = tqdm(trainloader, desc=f'Training Epoch {epoch+1}/{num_epochs}')
for inputs, labels in train_bar:
inputs, labels = (device), (device)
optimizer.zero_grad() # 梯度清零
outputs = net(inputs) # 前向传播
loss = criterion(outputs, labels) # 计算损失
() # 反向传播
() # 优化器更新参数
running_loss += ()
_, predicted = (1) # 获取预测结果
total += (0)
correct += (labels).sum().item()
# 更新进度条信息
train_bar.set_postfix(loss=running_loss/(len(train_bar)*trainloader.batch_size), acc=100.*correct/total)
train_loss = running_loss / len(trainloader)
train_acc = 100. * correct / total
train_losses.append(train_loss)
train_accuracies.append(train_acc)
() # 设置网络为评估模式
test_loss = 0.0
correct = 0
total = 0
with torch.no_grad(): # 禁用梯度计算
# 使用tqdm显示验证进度条
test_bar = tqdm(testloader, desc=f'Validating Epoch {epoch+1}/{num_epochs}')
for inputs, labels in test_bar:
inputs, labels = (device), (device)
outputs = net(inputs)
loss = criterion(outputs, labels)
test_loss += ()
_, predicted = (1)
total += (0)
correct += (labels).sum().item()
test_bar.set_postfix(loss=test_loss/(len(test_bar)*testloader.batch_size), acc=100.*correct/total)
test_loss = test_loss / len(testloader)
test_acc = 100. * correct / total
test_losses.append(test_loss)
test_accuracies.append(test_acc)
# 保存最好的模型
if test_acc > best_acc:
best_acc = test_acc
(net.state_dict(), 'best_model.pth')
# 绘制损失和准确率曲线并保存
(figsize=(12, 5))
# 绘制损失曲线
(1, 2, 1)
(range(num_epochs), train_losses, label='Training Loss')
(range(num_epochs), test_losses, label='Validation Loss')
('Epoch')
('Loss')
()
('Loss Curve')
# 绘制准确率曲线
(1, 2, 2)
(range(num_epochs), train_accuracies, label='Training Accuracy')
(range(num_epochs), test_accuracies, label='Validation Accuracy')
('Epoch')
('Accuracy')
()
('Accuracy Curve')
# 保存图像
('loss_accuracy_curves.png')
()
if __name__ == "__main__":
main()
四、总结
本项目使用 PyTorch 框架构建了一个卷积神经网络(CNN)模型,针对 CIFAR-100 数据集进行了图像分类任务的实现。CIFAR-100 数据集具有 100 个类别,每个类别包含 600 张图像,是一个复杂的、多类别的图像分类任务。通过这个项目,我们对如何使用深度学习技术来处理图像分类问题有了深入的理解。我们不仅熟悉了使用 PyTorch 构建和训练卷积神经网络的全过程,还掌握了如何处理更复杂的多类别图像分类问题。尽管项目取得了一定的成功,但也暴露了在更大规模数据集上训练的挑战,如需要更多的计算资源、更深的网络结构或更复杂的模型调整策略。
未来的工作可以包括:
- 尝试使用预训练模型进行迁移学习,以提升模型性能。
- 引入更先进的网络架构,如 ResNet 或 DenseNet,以进一步提高分类准确率。
- 优化数据预处理流程,探索更有效的数据增强方法。
这个项目为后续更复杂的深度学习任务打下了坚实的基础。通过持续学习和实验,不断提升模型的性能和效率,将能够在实际应用中取得更好的结果。