PyTorch深度学习60分钟快速上手(四),训练分类器。

    xiaoxiao2022-07-14  140

    训练分类器

    现在我们已经知道如何定义网络结构,计算损失以及更新网络权重了,那么,

    如何准备数据?

    一般来说,处理图像、文本、语音或视频数据,我们可以直接使用Python的标准包将数据加载成numpy数组,然后将它转成torch.*Tensor。

    对于图片数据,可以使用Pillow,OpenCV;对于音频数据,可以使用scipy和librosa。对于文本数据,可以使用基于原生Python或Cython的加载方式,或者使用NLTK和SpaCy包。

    特别针对视觉处理,PyTorch提供了torchvision这个包,能够加载常用的数据集,比如ImageNet,CIFAR10,MNIST等,并且还有图像转换功能,我们可以使用这两个包来实现这些功能:torchvision.datasets,torch.utils.data.DataLoader。

    这样就为我们提供了极大的便利而不需要编写样板代码。

    这里我们将使用CIFAR10数据集,包含这些类别:“airplane”,“automobile”,“bird”,”cat“,”deer“,”dog“,”frog“,”horse“,”ship“,”truck“,图片大小为3 * 32 * 32,即3通道的32 * 32图像。

    训练图像分类器

    使用torchvision加载、归一化CIFAR10训练、测试数据定义卷积神经网络定义损失函数使用训练数据训练网络使用测试数据测试网络

    1. 加载、归一化CIFAR10

    使用torchvision能非常便捷地加载CIFAR10。

    import torch import torchvision import torchvision.transforms as transforms

    torchvision数据集的输出是PILImage类型的数据,范围[0,1]。然后我们将数据归一化到[-1,1]。

    transform = transforms.Compose( [transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) 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=2) 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=2) classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck') Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz to ./data/cifar-10-python.tar.gz Files already downloaded and verified

    我们先来看几张图片:

    import matplotlib.pyplot as plt import numpy as np # 显示图像函数 def imshow(img): img = img / 2 + 0.5 # 去归一化 npimg = img.numpy() plt.imshow(np.transpose(npimg, (1, 2, 0))) plt.show() # 随机获取训练图像 dataiter = iter(trainloader) images, labels = dataiter.next() # 显示图像 imshow(torchvision.utils.make_grid(images)) # print labels print(' '.join('%5s' % classes[labels[j]] for j in range(4)))

    plane truck car ship

    2. 定义卷积神经网络

    这里使用之前介绍的网络结构,但是将原本的1通道输入改成3通道输入:

    import torch.nn as nn import torch.nn.functional as F class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.conv1 = nn.Conv2d(3, 6, 5) self.pool = nn.MaxPool2d(2, 2) self.conv2 = nn.Conv2d(6, 16, 5) 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.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()

    3. 定义损失函数和优化器

    使用交叉熵损失和带动量的SGD:

    import torch.optim as optim criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

    4. 训练网络

    这里就是很有趣的地方啦,只需要简单的遍历数据集,将数据送给网络并且进行优化:

    for epoch in range(2): # 多次遍历数据集 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 个batch打印一次 print('[%d, ]] loss: %.3f' % (epoch + 1, i + 1, running_loss / 2000)) running_loss = 0.0 print('Finished Training')

    在GPU上训练网络

    类似于将tensor放到GPU上,网络也同样能放到GPU上。 现在我们将第1块可用的CUDA设备定义为device:

    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") # 如果机器上有CUDA设备,那么这里应该会打印出对应的CUDA设备: print(device) cuda:0

    然后使用下面的方法,将会递归遍历所有的模块,将它们的参数,缓存都转成CUDA tensor。

    net.to(device)

    记住同时也要将输入等也放到device设备上:

    inputs, labels = inputs.to(device), labels.to(device) [1, 2000] loss: 2.225 [1, 4000] loss: 1.894 [1, 6000] loss: 1.676 [1, 8000] loss: 1.568 [1, 10000] loss: 1.513 [1, 12000] loss: 1.468 [2, 2000] loss: 1.424 [2, 4000] loss: 1.360 [2, 6000] loss: 1.348 [2, 8000] loss: 1.336 [2, 10000] loss: 1.310 [2, 12000] loss: 1.289 Finished Training

    5. 使用测试数据测试网络

    我们在数据集上迭代训练了2次,现在我们要看看网络是否学习到了东西。

    我们需要使用网络的输出来得到预测的类别,并且和ground-truth进行对比,,如果预测正确,我们将这个样本加入正确预测的列表中。

    现在和之前一样,我们看看测试数据:

    dataiter = iter(testloader) images, labels = dataiter.next() # print images imshow(torchvision.utils.make_grid(images)) print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))

    GroundTruth: cat ship ship plane

    现在我们来看看网络的预测结果:

    outputs = net(images)

    网络的输出是这10个类的置信度,一个类的置信度越高,说明网络认为图片属于这一类的可能性越大。所以我们获取最大置信度的类的下标:

    _, predicted = torch.max(outputs, 1) print('Predicted: ', ' '.join('%5s' % classes[predicted[j]] for j in range(4))) Predicted: frog ship 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) total += labels.size(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: 55 %

    这看起来比随机猜的结果要好多了,随机猜测的准确率为10%,所以看起来网络学习到了一些东西。

    那么,我们现在想看看网络在哪些类别上分类准确,哪些又没那么好:

    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): label = labels[i] class_correct[label] += c[i].item() class_total[label] += 1 for i in range(10): print('Accuracy of %5s : - %%' % ( classes[i], 100 * class_correct[i] / class_total[i])) Accuracy of plane : 52 % Accuracy of car : 68 % Accuracy of bird : 39 % Accuracy of cat : 26 % Accuracy of deer : 46 % Accuracy of dog : 46 % Accuracy of frog : 62 % Accuracy of horse : 80 % Accuracy of ship : 76 % Accuracy of truck : 64 %

    微信扫一扫关注公众号:机器工匠,回复关键字“cifar”获取代码和数据。

    最新回复(0)