深度学习|零基础深度学习Pytorch教程,一个小时快速入门(四)

Pytorch官网有非常优秀的教程,其中有几篇小短文属于名为DEEP LEARNING WITH PYTORCH: A 60 MINUTE BLITZ这个小专栏的内容,考虑到大家阅读英文文献有点困难,笔者打算花些时间做一下翻译,同时结合自己的理解做一些内容调整,原文链接贴在这里点此跳转。承接之前的内容,点此跳转到第三部分。好的我们开始第四部分,这也是最后一个部分。
这一部分的内容让我们结合之前的知识进行简单的实战,学完这一部分就可以自己使用pytorch进行简单的模型搭建了,我们就直接开始了。
现在你已经知道了如何定义神经网络了,同时计算loss值并且更新神经网络的权重值。现在想请你思考一下。
数据是什么?
通常情况下,当你不得不去处理图片、文本、音频和视频数据时,你能使用标准的python包将数据导入到numpy数组中。然后你能够将数组转化为pytorch的tensor类型。
(1)对于图片,有Pillow,OpenCV的包可以使用。
(2)对于音频,有scipy和librosa的包可以使用。
(3)对于文本,无论是基于原始Python或Cython的加载,使用NLTK和SpaCy都是可以使用的
特别的对于视觉的处理,我们已经创建一个包叫做torchvision,可以用于对公共数据集的数据加载程序,例如ImageNet, CIFAR10, MNIST等等。对于图片的数据转化工具包括torchvision.datasets和torch.utils.data.DataLoader。
这写工具为我们提供了很大的便利,避免了写过多的重复代码,便于相关人员的使用。
对于这篇教程,我们将会使用CIFIA10的数据集。它包含各个种类,比如‘airplane’, ‘automobile’, ‘bird’, ‘cat’, ‘deer’, ‘dog’, ‘frog’, ‘horse’, ‘ship’, ‘truck’.。这些在CIFIA中的图片是3*32*32的大小的,3个(RGB)通道的32*32尺寸的图片。
深度学习|零基础深度学习Pytorch教程,一个小时快速入门(四)
文章图片

介绍完毕,接下来进行实战训练。
训练一个图片分类器
我们将按照步骤进行如下的操作:
(1)使用torchvision导入并且规范化CIFIA10的训练数据和测试数据。
(2)定义一个卷积神经网络
【深度学习|零基础深度学习Pytorch教程,一个小时快速入门(四)】(3)定义一个损失(loss)函数
(4)在训练数据集上训练一个神经网络
(5)在测试集上测试网络的效果
1.导入并且规范化CIFIA10的数据集
使用torchvision,用它来导入CIFAR10是非常简单的。

import torch import torchvision import torchvision.transforms as transforms

torchvision的数据集输出的范围是[0,1]。我们将他们转化为规范化的tensor格式范围区间是[-1,1]。
transform = transforms.Compose( [transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])batch_size = 4trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform) trainloader = torch.utils.data.DataLoader(trainset, batch_size=batch_size, 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=batch_size, shuffle=False, num_workers=2)classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

如果在Windows上运行,你得到一个BrokenPipeError,那么可以将 torch.utils.data.DataLoader()的num_workers设置为0。
上图输出结果为
Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz to ./data/cifar-10-python.tar.gz Extracting ./data/cifar-10-python.tar.gz to ./data Files already downloaded and verified

由于大家的计算机上还没有相关的数据集上,所以会连接网站进行下载。
下面让我们显示一下进行训练的图片。
import matplotlib.pyplot as plt import numpy as np# 显示一张图片的函数def imshow(img): img = img / 2 + 0.5# unnormalize 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(' '.join('%5s' % classes[labels[j]] for j in range(batch_size)))

输出结果为:
深度学习|零基础深度学习Pytorch教程,一个小时快速入门(四)
文章图片

cat planebirdship

2.定义一个卷积神经网络
从神经网络集中选取一个神经网络,调整网络的输入为一个3通道(指的是RGB)的图片,代替原来默认的单通道(灰度)的图片。
import torch.nn as nn import torch.nn.functional as Fclass Net(nn.Module): def __init__(self): super().__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 = torch.flatten(x, 1) # 将高维度打平为一维 x = F.relu(self.fc1(x)) x = F.relu(self.fc2(x)) x = self.fc3(x) return xnet = Net()

3.定义一个损失函数和优化器
让我们使用一个分类器Corss-Entropy(交叉熵)的损失函数和带有动量的SGD(随机快速下降法)。
import torch.optim as optimcriterion = 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] inputs, labels = data# 零是元素梯度 optimizer.zero_grad()# forward + backward + optimize outputs = net(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step()# 打印统计数据 running_loss += loss.item() if i % 2000 == 1999:# 每2000个mini-batches打印一次 print('[%d, %5d] loss: %.3f' % (epoch + 1, i + 1, running_loss / 2000)) running_loss = 0.0print('Finished Training')

输出结果为:
[1,2000] loss: 2.128 [1,4000] loss: 1.793 [1,6000] loss: 1.649 [1,8000] loss: 1.555 [1, 10000] loss: 1.504 [1, 12000] loss: 1.444 [2,2000] loss: 1.379 [2,4000] loss: 1.344 [2,6000] loss: 1.336 [2,8000] loss: 1.327 [2, 10000] loss: 1.294 [2, 12000] loss: 1.280 Finished Training

让我们快速的保存模型,便以下次可以直接继续从这里的参数进行训练。
PATH = './cifar_net.pth' torch.save(net.state_dict(), PATH)

更多关于保存的细节可以观看原文档。
5.在测试集上测试网络
我们已经在训练集上训练了2轮网络了(训练网络的那个位置大循环是2次),但是我们需要检验是否网络已经取得了不错的学习效果。
我们通过检测标签的分类情况和神经网络的输出结果,检测与真实值的误差。如果预测正确,我们会在正确预测集中增加一个样本。
好的,第一步,让我们显示一下来自测试集中的一组图片。
dataiter = iter(testloader) images, labels = dataiter.next()# 打印图片 imshow(torchvision.utils.make_grid(images)) print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))

深度学习|零基础深度学习Pytorch教程,一个小时快速入门(四)
文章图片

GroundTruth:catshipship 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:frogshipshipship

结果看起来似乎很不错。
让我们看看网络在整个数据集上是怎么执行的吧。
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: 54 %

从结果来看,这个似乎比碰运气好多了,碰运气是十个里面挑一个也就是10%的正确率,看来神经网络是有点东西的。
让我们看看哪些类别的测试结果表现的更好。
# 准备为每一种类型计算预测结果 correct_pred = {classname: 0 for classname in classes} total_pred = {classname: 0 for classname in classes}# again no gradients needed with torch.no_grad(): for data in testloader: images, labels = data outputs = net(images) _, predictions = torch.max(outputs, 1) # collect the correct predictions for each class for label, prediction in zip(labels, predictions): if label == prediction: correct_pred[classes[label]] += 1 total_pred[classes[label]] += 1# 打印每一种类型的准确值 for classname, correct_count in correct_pred.items(): accuracy = 100 * float(correct_count) / total_pred[classname] print("Accuracy for class {:5s} is: {:.1f} %".format(classname, accuracy))

输出结果为:
Accuracy for class plane is: 59.4 % Accuracy for class caris: 66.7 % Accuracy for class birdis: 22.7 % Accuracy for class catis: 52.7 % Accuracy for class deeris: 59.1 % Accuracy for class dogis: 28.9 % Accuracy for class frogis: 70.8 % Accuracy for class horse is: 57.6 % Accuracy for class shipis: 67.4 % Accuracy for class truck is: 62.2 %

好的,下面怎么做呢,我们可以再试一试GPU加速。
在GPU上训练
就像将tensor转换到GPU上一样,这里我们将网络转换到GPU上。
如果我们可以获取CUDA(英伟达的库这里的更多细节,后面会继续写相关教程),我们可以首先定义我们的设备为第一个可见cuda设备。
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")# 假设我们有一个装有CUDA的机器应当打印对应机器设备代号:print(device)

输出结果为:
cuda:0

剩下的部分假设我们的设备上已经安装好了CUDA。
这些方法会递归遍历所有模块,并将它们的参数和缓冲区转换为CUDA的tensor。
net.to(device)

记住,你必须在每一步都将输入和目标发送给GPU 参与运算。
inputs, labels = data[0].to(device), data[1].to(device)

如果在测试过程中,你并没有注意到与GPU获取了更多的加速效果。主要是因为你的网络可能很小。
试着增加你的网络的宽度,看看你得到什么样的加速。
通过这次的学习,你可以建立一个小的网络进行图片的分类,下面你可以进一步理解PyTorch的库,然后训练更多的神经网络。

    推荐阅读