文章目录
分类器任务和数据介绍
- 构建一个将不同图像进行分类的神经网络分类器,对输入的图片进行判别并完成分类
- 案例采用CIFAR10数据集作为原始图片数据
- CIFAR10数据集介绍:数据集中每张图片的尺寸是3*32*32,代表彩色3通道
- CIFAR10数据集总共有10种不同的分类,分别是"airplane",“automobile”,“bird”,“car”,“deer”,“dog”,“frog”,“house”,“ship”,“truck”
- CIFAR10数据集的样例如下图:
训练分类器步骤
- 使用torchvision下载CIFAR10数据集
- 定义卷积神经网络
- 定义损失函数
- 在训练集上训练模型
- 在测试集上测试模型
1使用torchvision下载CIFAR10数据集
- 导入torchvision包来辅助下载数据集
import torch import torchvision import torchvision.transforms as transformas
- 下载数据集并对图片进行调整,因为torchvision数据集的输出是PILImage格式,数据域在[0,1],需要将其转换为标准数据域[-1,1]的张量格式
# 定义数据变换 transform = transforms.Compose( [transforms.ToTensor(), # 将图片转换为Tensor transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) # 归一化 # 下载CIFAR10数据集 # #训练集 trainset=torchvision.datasets.CIFAR10(root='F:\人工智能\自然语言模型处理\data',train=True,download=True,transform=transform) # 测试集 testset=torchvision.datasets.CIFAR10(root='F:\人工智能\自然语言模型处理\data',train=False,download=True,transform=transform)
- 结果
Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz to F:\人工智能\自然语言模型处理\data\cifar-10-python.tar.gz Extracting F:\人工智能\自然语言模型处理\data\cifar-10-python.tar.gz to F:\人工智能\自然语言模型处理\data Files already downloaded and verified
- 结果
- 注意:
- 如果是在Windows系统下载运行代码,并且报错信息为"BrokenPipeError",可以尝试将torch.utils.data.DataLoader()中的num_workers设置为0
2 展示若干训练集的图片
# 定义batch_size和num_workers
batch_size = 4
num_workers = 0 # Windows系统不支持多进程,所以num_workers应设为0
#练习集 封装成DataLoader的形式 batch_size 按照批次传 shuffle 将数据打散 num_workers 线程
# 创建DataLoader对象
trainloader=torch.utils.data.DataLoader(trainset,batch_size=batch_size,shuffle=True,num_workers=num_workers)
testloader=torch.utils.data.DataLoader(testset,batch_size=batch_size,shuffle=False,num_workers=num_workers)
# 定义类别名称
classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
展示图片的方式
- 方式一
import matplotlib.pyplot as plt
import numpy as np
# 获取一些随机训练图像
dataiter = iter(trainloader)
images, labels = next(dataiter) #注意点,由于版本不同,应该使用next()来迭代dataiter对象
# 展示图像和标签
#ncols 生成图片数量
fig, axes = plt.subplots(figsize=(10, 5), ncols=4)
for i in range(4):
ax = axes[i]
ax.imshow(np.transpose(images[i], (1, 2, 0)))
ax.set_title(classes[labels[i]])# 设置图片的标签
ax.axis('off')
plt.show()
# 打印标签label
print(' '.join('%10s'% classes[labels[i]]for i in range(4)))
- 方法二
# 构建展示图片的函数
def imshow(img):
img=img/2+0.5
# 将tensor转化成numpy格式的数据
npimg=img.numpy()
# 生成一张图片 位于trainset第一张图片
# img = torchvision.utils.make_grid(trainset[1][0]).numpy()
# #维度转换
plt.imshow(np.transpose(npimg,(1,2,0)))
plt.show()
# 展示图片
imshow(torchvision.utils.make_grid(images))
# 打印标签label
print(' '.join('%10s'% classes[labels[i]]for i in range(4)))
出现 DLL load failed 错误## 标题
-
问题(Windows环境+pycharm+jupyter notebook)
libiomp5md.dll文件存在多个
向系统申请允许在多个进程中加载同一个动态链接库,从而避免出现“DLL load failed”等错误。
方式一:删除或者修改以下文件的名字(任一环境中)
方式二:输入以下命令,在系统调用绘图库时,允许同时加载库
import os os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"
-
结果
3 定义卷积神经网络
- 仿照2.1节中的类来构造此处的类,唯一的区别是采用3个通道3-channel
import torch.nn as nn import torch.nn.functional as F class Net(nn.Module): def __init__(self): super(Net, self).__init__() # 定义2个卷积层 self.conv1 = nn.Conv2d(3, 6, 5) # 卷积层 self.conv2 = nn.Conv2d(6, 16, 5) # 池化层 self.pool = nn.MaxPool2d(2, 2) # 全连接层 self.fc1 = nn.Linear(16 * 5 * 5, 120) self.fc2 = nn.Linear(120, 84) self.fc3 = nn.Linear(84, 10) def forward(self, x): # 激活 x = self.pool(F.relu(self.conv1(x))) x = self.pool(F.relu(self.conv2(x))) # 变换x的形状以适配全连接层的输入 x = x.view(-1, 16 * 5 * 5) x = F.relu(self.fc1(x)) x = F.relu(self.fc2(x)) x = self.fc3(x) return x net = Net()
- 结果
Net( (conv1): Conv2d(3, 6, kernel_size=(5, 5), stride=(1, 1)) (pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1)) (fc1): Linear(in_features=400, out_features=120, bias=True) (fc2): Linear(in_features=120, out_features=84, bias=True) (fc3): Linear(in_features=84, out_features=10, bias=True) )
4 定义损失函数
- 采用交叉熵损失函数和随机梯度下降优化器
import torch.optim as optim # 定义损失函数,选用交叉熵损失函数 criterion = nn.CrossEntropyLoss() # 定义优化器,选用随机梯度下降优化器 # parameters 网络里面所有可训练的参数 # lr 学习率 optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
- 结果
SGD ( Parameter Group 0 dampening: 0 differentiable: False foreach: None lr: 0.001 maximize: False momentum: 0.9 nesterov: False weight_decay: 0 )
- 结果
5 在GPU上训练模型
- 为了真正利用Pytorch中Tensor的优秀属性, 加速模型的训练, 我们可以将训练过程转移到GPU上进行
# 在GPU上训练模型 device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") print(device)
- 首先要定义设备, 如果CUDA是可用的则被定义成GPU, 否则被定义成CPU
# 将模型转移到GPU上 net.to(device) # 将损失函数转移到GPU上 criterion.to(device) # 将输入的图片张量和标签张量转移到GPU上 # inputs, labels = data[0].to(device), data[1].to(device)
6 在训练集上训练模型
- 采用基于梯度下降的优化算法, 都需要很多个轮次的迭代训练
# import sys # # 将 sys.stdout 重定向到 sys.__stdout__ # sys.stdout = sys.__stdout__ # 整个训练集 遍历两轮 for epoch in range(2): # loop over the dataset multiple times # 记录损失 running_loss = 0.0 # # 记录批次数 # batch_count = 0 # 每一个批次取数据进行训练 for i, data in enumerate(trainloader, 0): # data中包含输入图像张量inputs, 标签张量labels # inputs, labels = data # 将输入的图片张量和标签张量转移到GPU上 inputs, labels = data[0].to(device), data[1].to(device) # 首先将优化器梯度归零 optimizer.zero_grad() # 输入图像张量进网络, 得到输出张量outputs outputs = net(inputs) # 利用网络的输出outputs和标签labels计算损失值 loss = criterion(outputs, labels) # 反向传播+参数更新, 是标准代码的标准流程 loss.backward() optimizer.step() # 打印轮次和损失值 running_loss += loss.item() # batch_count += 1 if (i+1) % 2000 == 0: print('[%d, %5d] loss: %.3f' % (epoch + 1, i+1, running_loss / 2000)) running_loss = 0.0 # sys.stdout.flush() # 刷新输出缓冲区 print('Number of batches:', len(trainloader)) # 在每个epoch结束后打印损失值 # print('[%d] loss: %.3f' % (epoch + 1, running_loss / len(trainloader))) running_loss = 0.0 # sys.stdout.flush() # 刷新输出缓冲区 print('Finished Training')
- 结果
[1, 2000] loss: 1.252 [1, 4000] loss: 1.245 [1, 6000] loss: 1.230 [1, 8000] loss: 1.234 [1, 10000] loss: 1.204 [1, 12000] loss: 1.221 Number of batches: 12500 [2, 2000] loss: 1.134 [2, 4000] loss: 1.139 [2, 6000] loss: 1.158 [2, 8000] loss: 1.121 [2, 10000] loss: 1.135 [2, 12000] loss: 1.139 Number of batches: 12500 Finished Training
- 结果
7 保存模型
# 首先设定模型的保存路径
PATH = './cifar_net.pth'
# 保存模型的状态字典
torch.save(net.state_dict(), PATH)
8 在测试集上测试模型
- 第一步, 展示测试集中的若干图片
dataiter = iter(testloader) images, labels = next(dataiter) # 打印原始图片 imshow(torchvision.utils.make_grid(images)) # 打印真实的标签 print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))
-
结果
GroundTruth: cat ship ship plane
-
- 第二步, 加载模型并对测试图片进行预测
# 加载模型参数,在测试阶段 # 首先实例化模型的类对象 net = Net() # 加载训练阶段保存好的模型的状态字典 net.load_state_dict(torch.load(PATH)) #重要 # 利用模型对图片进行预测 outputs = net(images) # 模型共有10个类别的输出, 选取模型中概率最大的那个类别作为预测值 _, predicted = torch.max(outputs, 1) #贪心算法 # 打印预测标签的结果 print('Predicted: ', ' '.join('%5s' % classes[predicted[j]] for j in range(4)))
- 结果
Predicted: cat car car plane
- 结果
- 接下来看一下在全部测试集上的表现
# 在整个测试集上测试模型的准确率 correct = 0 total = 0 with torch.no_grad(): for data in testloader: images, labels = data outputs = net(images) _, predicted = torch.max(outputs.data, 1) # +4 total += labels.size(0) # 真 1 错 0 correct += (predicted == labels).sum().item() print('Accuracy of the network on the 10000 test images: %d %%' % (100 * correct / total))
- 结果
Accuracy of the network on the 10000 test images: 53 %
📌分析结果: 对于拥有10个类别的数据集, 随机猜测的准确率是10%, 模型达到了53%, 说明模型学到了真实的东西
9 准确率计算
- 为了更加细致的看一下模型在哪些类别上表现更好, 在哪些类别上表现更差, 我们分类别的进行准确率计算
# 分别测试不同类别的模型准确率 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) # squeeze() 把多余的维度进行清理 c = (predicted == labels).squeeze() for i in range(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]))
- 结果
Accuracy of plane : 52 % Accuracy of car : 86 % Accuracy of bird : 49 % Accuracy of cat : 22 % Accuracy of deer : 39 % Accuracy of dog : 48 % Accuracy of frog : 44 % Accuracy of horse : 76 % Accuracy of ship : 69 % Accuracy of truck : 47 %
- 结果