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尺寸的图片。
介绍完毕,接下来进行实战训练。
训练一个图片分类器
我们将按照步骤进行如下的操作:
(1)使用torchvision导入并且规范化CIFIA10的训练数据和测试数据。
(2)定义一个卷积神经网络
(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 = 4
trainset = 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)))
输出结果为:
cat plane bird ship
2.定义一个卷积神经网络
从神经网络集中选取一个神经网络,调整网络的输入为一个3通道(指的是RGB)的图片,代替原来默认的单通道(灰度)的图片。
import torch.nn as nn
import torch.nn.functional as F
class 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 x
net = Net()
3.定义一个损失函数和优化器
让我们使用一个分类器Corss-Entropy(交叉熵)的损失函数和带有动量的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]
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.0
print('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)))
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: frog ship ship ship
结果看起来似乎很不错。
让我们看看网络在整个数据集上是怎么执行的吧。
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 car is: 66.7 %
Accuracy for class bird is: 22.7 %
Accuracy for class cat is: 52.7 %
Accuracy for class deer is: 59.1 %
Accuracy for class dog is: 28.9 %
Accuracy for class frog is: 70.8 %
Accuracy for class horse is: 57.6 %
Accuracy for class ship is: 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的库,然后训练更多的神经网络。