# 导入必要的库
import torch
import torchvision
import torchvision.transforms as transforms
# 定义图像预处理方法,包括裁剪、转换为张量、归一化等
transform = transforms.Compose(
[transforms.CenterCrop(32), # 裁剪中心32*32区域
transforms.ToTensor(), # 转换为张量
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) # 归一化
# 加载训练集和测试集,使用torchvision内置的CIFAR10数据集
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,
shuffle=True, num_workers=0)
testset = torchvision.datasets.CIFAR10(root='./data', train=False,
download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4,
shuffle=False, num_workers=0)
# 定义类别标签,CIFAR10数据集有10个类别
classes = ('plane', 'car', 'bird', 'cat',
'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
# 定义CNN模型结构,包括两个卷积层和三个全连接层
class Net(torch.nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = torch.nn.Conv2d(3, 6, 5) # 输入通道数为3(RGB),输出通道数为6,卷积核大小为5*5
self.pool = torch.nn.MaxPool2d(2, 2) # 最大池化层,池化核大小为2*2,步长为2
self.conv2 = torch.nn.Conv2d(6, 16, 5) # 输入通道数为6,输出通道数为16,卷积核大小为5*5
self.fc1 = torch.nn.Linear(16 * 5 * 5, 120) # 全连接层,输入特征数为16*5*5(卷积后的特征图大小),输出特征数为120
self.fc2 = torch.nn.Linear(120, 84) # 全连接层,输入特征数为120,输出特征数为84
self.fc3 = torch.nn.Linear(84, 10) # 全连接层,输入特征数为84,输出特征数为10(类别数)
def forward(self, x):
x = self.pool(torch.nn.functional.relu(self.conv1(x))) # 卷积后激活再池化
x = self.pool(torch.nn.functional.relu(self.conv2(x))) # 卷积后激活再池化
x = x.view(-1, 16 * 5 * 5) # 将二维特征图展平成一维向量
x = torch.nn.functional.relu(self.fc1(x)) # 全连接后激活
x = torch.nn.functional.relu(self.fc2(x)) # 全连接后激活
x = self.fc3(x) # 全连接后输出
return x
# 创建模型实例
net = Net()
# 定义损失函数和优化器,使用交叉熵损失和随机梯度下降
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
# 训练模型,迭代2个epoch
for epoch in range(5):
running_loss = 0.0 # 记录累计损失
for i, data in enumerate(trainloader, 0):
# 获取一个批次的输入数据和标签
inputs, labels = data
# 将梯度清零
optimizer.zero_grad()
# 前向传播,反向传播,更新参数
outputs = net(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
# 打印统计信息
running_loss += loss.item()
if i % 2000 == 1999: # 每2000个批次打印一次平均损失
print('[%d, %5d] loss: %.3f' %
(epoch + 1, i + 1, running_loss / 2000))
running_loss = 0.0
print('Finished Training')
# 测试模型在测试集上的表现,计算准确率
correct = 0 # 正确预测的数量
total = 0 # 总共的数量
with torch.no_grad(): # 不需要计算梯度
for data in testloader:
images, labels = data # 获取一个批次的输入数据和标签
outputs = net(images) # 前向传播得到输出
_, predicted = torch.max(outputs.data, 1) # 获取预测的类别
total += labels.size(0) # 更新总数量
correct += (predicted == labels).sum().item() # 更新正确数量
print('Accuracy of the network on the 10000 test images: %d %%' % (
100 * correct / total))
# 查看每个类别的准确率
class_correct = list(0. for i in range(10)) # 每个类别正确预测的数量
class_total = list(0. for i in range(10)) # 每个类别总共的数量
with torch.no_grad(): # 不需要计算梯度
for data in testloader:
images, labels = data # 获取一个批次的输入数据和标签
outputs = net(images) # 前向传播得到输出
_, predicted = torch.max(outputs, 1) # 获取预测的类别
c = (predicted == labels).squeeze() # 计算每个样本是否预测正确
for i in range(4): # 遍历一个批次中的每个样本(这里批次大小为4)
label = labels[i] # 获取真实标签
class_correct[label] += c[i].item() # 更新该类别正确预测的数量
class_total[label] += 1 # 更新该类别总共的数量
# 打印每个类别的准确率
for i in range(10):
print('Accuracy of %5s : %2d %%' % (
classes[i], 100 * class_correct[i] / class_total[i]))