Pytorch:利用卷积神经网络进行CIFAR-10图像分类

CIFAR-10数据集共有60000张尺寸为3 × 32 × 32的图像,训练集图像50000张,测试集图像10000张。数据集大小约为100M,总共可以对里面的图像分成10类,本文将利用卷积神经网络对该数据集进行图像识别分类。

1.下载数据集

使用Pytorch提供的视觉工具包torchvision加载CIFAR-10数据集,第一次运行程序torchvision会自动下载CIFAR-10数据集, 数据集大小约为100M,需花费一些时间,如果已经下载好CIFAR-10数据集,那么可通过root参数指定。如下是该案例导入的包:

import torch as t
import torchvision as tv
import torchvision.transforms as transforms
from torchvision.transforms import ToPILImage

import matplotlib.pyplot as plt

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

from torch import optim

2.数据预处理


# 可以把Tensor转成Image,用于 ‌实现张量(Tensor)与 PIL 图像(PIL.Image)之间的格式转换‌
show = ToPILImage()

transform = transforms.Compose([
    transforms.ToTensor(),  # 转为Tensor
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),  # 归一化
])

# 训练集
trainset = tv.datasets.CIFAR10(
    root='./pytorch-book-cifar10/',
    train=True,
    download=True,
    transform=transform)

"""`Dataloader`是一个可迭代对象,它将`Dataset`返回的每一条数据样本拼接成一个batch,
   数据集 以batch_size个样本为一个批次地加载训练数据。
   同时提供多线程加速优化和数据打乱等操作。当程序对`Dataset`的所有数据遍历完一遍后,
   对`Dataloader`也完成了一次迭代
"""
trainloader = t.utils.data.DataLoader(
    trainset,
    batch_size=4,
    shuffle=True,
    num_workers=2)


# 测试集
testset = tv.datasets.CIFAR10(
    './pytorch-book-cifar10/',
    train=False,
    download=True,
    transform=transform)

testloader = t.utils.data.DataLoader(
    testset,
    batch_size=4,
    shuffle=False,
    num_workers=2
)

classes = ('plane', 'car', 'bird', 'cat', 'deer',
           'dog', 'frog', 'horse', 'ship', 'truck')
(data, label) = trainset[100]

#查看数据
print(classes[label])

# (data + 1) / 2目的是:还原被归一化的数据
show((data + 1) / 2).resize((100, 100))

dataiter = iter(trainloader)      # 生成迭代器
images, labels = dataiter.__next__()  # 返回4张图片及标签
print(' '.join('%11s' % classes[labels[j]] for j in range(4)))
show(tv.utils.make_grid((images + 1) / 2)).resize((400, 100))
# 显示图像
plt.imshow(show(tv.utils.make_grid((images + 1) / 2)).resize((400, 100)))
plt.axis('off')
plt.show()

3.定义网络模型

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        # 卷积层
        # 参数说明:
        #   in_channels=3  : 输入通道数(RGB图像为3通道)
        #   out_channels=6 : 输出通道数(卷积核数量)
        #   kernel_size=5  : 卷积核尺寸5x5
        # 输入假设为[32x32]图像时输出尺寸为28x28(计算公式:(32-5+0)/1 +1)
        self.conv1 = nn.Conv2d(3, 6, 5)          # [3,32,32] → [6,28,28]

        # 池化层(全局共用)   参数说明:
        #              kernel_size=2 : 池化窗口尺寸2x2
        #              stride=2      : 滑动步长为2(输出尺寸减半)
        self.pool = nn.MaxPool2d(2, 2)           # [6,28,28] → [6,14,14]

        # 第二个卷积层
        # 输入通道6来自conv1的输出,输出通道16
        # 池化后输入尺寸14x14 → 卷积输出10x10 → 再次池化后5x5
        self.conv2 = nn.Conv2d(6, 16, 5)         # [6,14,14] → [16,10,10]
                                                 # → 池化后 [16,5,5]
        # 全连接层
        self.fc1 = nn.Linear(16 * 5 * 5, 120)    # 输入维度修正为16*5*5=400
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)             # 假设输出10类

    def forward(self, x): # 定义前向传播流程
        # 卷积层处理:卷积 → ReLU激活 → 池化
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        # 全连接层处理:展平 → ReLU激活 → ReLU激活 → 全连接层
        x = x.view(-1, 16 * 5 * 5)               # 展平操作,参数-1表示自动计算batch_size维度
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

net = Net()

4.训练模型

#定义损失函数和优化器
criterion = nn.CrossEntropyLoss() # 交叉熵损失函数
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
#训练网络,轮次可以自定义
for epoch in range(2):
    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
        # 输入数据
        inputs, labels = data

        # 梯度清零
        optimizer.zero_grad()

        # forward + backward
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()

        # 更新参数
        optimizer.step()

        # 打印log信息
        running_loss += loss.item()
        if i % 2000 == 1999:  # 每2000个batch打印一下训练状态
            print('[%d, %5d] loss: %.3f' \
                  % (epoch + 1, i + 1, running_loss / 2000))
            running_loss = 0.0
print('Finished Training')

5.在测试集上评估


#将测试图片输入到网络中,计算它的label,然后与实际的label进行比较
dataiter = iter(testloader)
images, labels = dataiter.__next__() # 一个batch返回4张图片

print('实际的label: ', ' '.join(\
            '%08s'%classes[labels[j]] for j in range(4)))
show(tv.utils.make_grid(images / 2 - 0.5)).resize((400, 100))
#计算网络预测的分类结果
# 计算图片在每个类别上的分数
outputs = net(images)
# 得分最高的那个类
_, predicted = t.max(outputs.data, 1)

print('预测结果: ', ' '.join('%5s' % classes[predicted[j]] for j in range(4)))

correct = 0  # 预测正确的图片数
total = 0  # 总共的图片数

#在整个测试集上的效果
# 由于测试的时候不需要求导,可以暂时关闭autograd,提高速度,节约内存
with t.no_grad():
    for data in testloader:
        images, labels = data
        outputs = net(images)
        _, predicted = t.max(outputs, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum()

print('10000张测试集中的准确率为: %f %%' % (100 * correct // total))

6.一键粘贴运行代码。

import torch as t
import torchvision as tv
import torchvision.transforms as transforms
from torchvision.transforms import ToPILImage

import matplotlib.pyplot as plt

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

from torch import optim

# 可以把Tensor转成Image,Jupyter可直接显示Image对象
show = ToPILImage()
# 第一次运行程序torchvision会自动下载CIFAR-10数据集,
# 数据集大小约为100M,需花费一些时间,
# 如果已经下载好CIFAR-10数据集,那么可通过root参数指定
# 定义对数据的预处理
transform = transforms.Compose([
    transforms.ToTensor(),  # 转为Tensor
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),  # 归一化
])
# 训练集
trainset = tv.datasets.CIFAR10(
    root='./pytorch-book-cifar10/',
    train=True,
    download=True,
    transform=transform)

trainloader = t.utils.data.DataLoader(
    trainset,
    batch_size=4,
    shuffle=True,
    num_workers=2)
#`Dataloader`是一个可迭代对象,它将`Dataset`返回的每一条数据样本拼接成一个batch,
# 数据集 以batch_size个样本为一个批次地加载训练数据。
# 同时提供多线程加速优化和数据打乱等操作。当程序对`Dataset`的所有数据遍历完一遍后,
# 对`Dataloader`也完成了一次迭代
# 测试集
testset = tv.datasets.CIFAR10(
    './pytorch-book-cifar10/',
    train=False,
    download=True,
    transform=transform)

testloader = t.utils.data.DataLoader(
    testset,
    batch_size=4,
    shuffle=False,
    num_workers=2
)

classes = ('plane', 'car', 'bird', 'cat', 'deer',
           'dog', 'frog', 'horse', 'ship', 'truck')
(data, label) = trainset[100]
print(classes[label])

# (data + 1) / 2目的是:还原被归一化的数据
show((data + 1) / 2).resize((100, 100))

dataiter = iter(trainloader)      # 生成迭代器
images, labels = dataiter.__next__()  # 返回4张图片及标签
print(' '.join('%11s' % classes[labels[j]] for j in range(4)))
show(tv.utils.make_grid((images + 1) / 2)).resize((400, 100))
# 显示图像
plt.imshow(show(tv.utils.make_grid((images + 1) / 2)).resize((400, 100)))
plt.axis('off')
plt.show()
#定义网络
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        # 卷积层
        # 参数说明:
        #   in_channels=3  : 输入通道数(RGB图像为3通道)
        #   out_channels=6 : 输出通道数(卷积核数量)
        #   kernel_size=5  : 卷积核尺寸5x5
        # 输入假设为[32x32]图像时输出尺寸为28x28(计算公式:(32-5+0)/1 +1)
        self.conv1 = nn.Conv2d(3, 6, 5)          # [3,32,32] → [6,28,28]

        # 池化层(全局共用)   参数说明:
        #              kernel_size=2 : 池化窗口尺寸2x2
        #              stride=2      : 滑动步长为2(输出尺寸减半)
        self.pool = nn.MaxPool2d(2, 2)           # [6,28,28] → [6,14,14]

        # 第二个卷积层
        # 输入通道6来自conv1的输出,输出通道16
        # 池化后输入尺寸14x14 → 卷积输出10x10 → 再次池化后5x5
        self.conv2 = nn.Conv2d(6, 16, 5)         # [6,14,14] → [16,10,10]
                                                 # → 池化后 [16,5,5]
        # 全连接层
        self.fc1 = nn.Linear(16 * 5 * 5, 120)    # 输入维度修正为16*5*5=400
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)             # 假设输出10类

    def forward(self, x): # 定义前向传播流程
        # 卷积层处理:卷积 → ReLU激活 → 池化
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        # 全连接层处理:展平 → ReLU激活 → ReLU激活 → 全连接层
        x = x.view(-1, 16 * 5 * 5)               # 展平操作,参数-1表示自动计算batch_size维度
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

net = Net()
print(net)
#定义损失函数和优化器
criterion = nn.CrossEntropyLoss() # 交叉熵损失函数
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
#训练网络,10论时间太长可以改小点
for epoch in range(10):
    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
        # 输入数据
        inputs, labels = data

        # 梯度清零
        optimizer.zero_grad()

        # forward + backward
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()

        # 更新参数
        optimizer.step()

        # 打印log信息
        running_loss += loss.item()
        if i % 2000 == 1999:  # 每2000个batch打印一下训练状态
            print('[%d, %5d] loss: %.3f' \
                  % (epoch + 1, i + 1, running_loss / 2000))
            running_loss = 0.0
print('Finished Training')

#将测试图片输入到网络中,计算它的label,然后与实际的label进行比较
dataiter = iter(testloader)
images, labels = dataiter.__next__() # 一个batch返回4张图片

print('实际的label: ', ' '.join(\
            '%08s'%classes[labels[j]] for j in range(4)))
show(tv.utils.make_grid(images / 2 - 0.5)).resize((400, 100))
#计算网络预测的分类结果
# 计算图片在每个类别上的分数
outputs = net(images)
# 得分最高的那个类
_, predicted = t.max(outputs.data, 1)

print('预测结果: ', ' '.join('%5s' % classes[predicted[j]] for j in range(4)))

correct = 0  # 预测正确的图片数
total = 0  # 总共的图片数

#在整个测试集上的效果
# 由于测试的时候不需要求导,可以暂时关闭autograd,提高速度,节约内存
with t.no_grad():
    for data in testloader:
        images, labels = data
        outputs = net(images)
        _, predicted = t.max(outputs, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum()

print('10000张测试集中的准确率为: %f %%' % (100 * correct // total))









PyTorch框架下,仿照MNIST手写体数字识别来处理更复杂的CIFAR-10数据集是一个常见的深度学习任务。CIFAR-10包含了60000张32x32彩色图像,分为10个类别,这比MNIST更具挑战性,因为图像分辨率更高、颜色更多且类别也增加了。 首先,我们需要导入必要的库并加载CIFAR-10数据集: ```python import torch import torchvision from torchvision import transforms transform = transforms.Compose( [transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) train_dataset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform) test_dataset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform) train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=128, shuffle=True, num_workers=2) test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=128, shuffle=False, num_workers=2) ``` 接着,我们可以定义一个基于ResNet或VGG这样的卷积神经网络模型,用于图像分类: ```python import torch.nn as nn import torch.nn.functional as F class ConvNet(nn.Module): def __init__(self): super(ConvNet, self).__init__() # ... 定义卷积层、池化层、全连接层等 def forward(self, x): # ... 定义前向传播过程 # 实例化模型 model = ConvNet() ``` 然后进行训练: ```python device = torch.device("cuda" if torch.cuda.is_available() else "cpu") model.to(device) criterion = nn.CrossEntropyLoss() optimizer = torch.optim.Adam(model.parameters(), lr=0.001) for epoch in range(num_epochs): # 更改num_epochs为你想要的迭代次数 for images, labels in train_loader: images, labels = images.to(device), labels.to(device) optimizer.zero_grad() outputs = model(images) loss = criterion(outputs, labels) loss.backward() optimizer.step() # 记录并展示训练信息 # ... # 测试模型 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"测试准确率: {correct / total}") ``` 在这个过程中,你可以调整网络结构、优化器参数以及训练策略来优化性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值