来啦,通过前面章节的内容,你已经学会如何定义一个神经网络,计算网络的损失值以及更新网络的权重。
可突然之间你意识到,
我该拿什么数据来训练呀
通常,我们碰到的无非就是图像数据、文本数据、音频数据和视频数据,我们可以使用标准的python
工具包以numpy array
的形式加载它们到内存当中。然后,你就可以方便的将它们转换成torch.*Tensor
数据格式了。
- 对于图像数据,
Pillow
、OpenCV
这两款工具包比较好用 - 对于音频数据,我们可以使用
scipy
和librosa
工具包 - 对于文本数据,原生的
Python
或Cython
就可以加载、或者使用NLTK
和SpaCy
针对图像视觉领域,我们创建了一个名为torchvision
的工具包,实现了Imagenet、CIFAR10、MNIST等常用数据集的加载接口。以及一些图像转换的方法,封装在torchvision.datasets
和torch.utils.data.DataLoader
模块里。
提供了如此便利的工具,我们再也不用写一些无聊的处理转换代码了。
本次教程,我们将会使用CIFAR10数据集。一共含有十个类别的图片,分别是飞机、汽车、鸟、猫、鹿、狗、青蛙、马、船、汽车。数据集中图片的格式大小是3x32x32
,即每张为32x32
的彩色图片。
cifar10
数据集
训练一个分类器
我们将按以下步骤训练一个图像分类器:
- 利用
torchvision
工具包加载训练数据和测试数据,并完成图像的归一化
。 - 定义一个卷积神经网络。
- 定义一个
损失函数
。 - 使用训练数据训练神经网络。
- 使用测试数据来
验证
已训练好的神经网络。
1.加载CIFAR10
数据并完成归一化
我们使用torchvision
工具包,用它来加载CIFAR10
简直不要太容易
import torch
import torchvision
import torchvision.transforms as transforms
torchvision
工具包加载的数据默认是PILImage
格式,范围是[0,1]
。我们将它转换到Tensors
格式下,取值范围是[-1,1]
。
If running on Windows and you get a BrokenPipeError, try setting
the num_worker of torch.utils.data.DataLoader() to 0.
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
Extracting ./data/cifar-10-python.tar.gz to ./data
Files already downloaded and verif
兴趣的使然,我们来打印一组刚加载的图像数据看看效果把。
import matplotlib.pyplot as plt
import numpy as np
# functions to show an image
def imshow(img):
img = img / 2 + 0.5 # unnormalize
npimg = img.numpy()
plt.imshow(np.transpose(npimg, (1, 2, 0)))
plt.show()
# get some random training images
dataiter = iter(trainloader)
images, labels = dataiter.next()
# show images
imshow(torchvision.utils.make_grid(images))
# print labels
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))
显示:
horse plane horse car
2.定义一个卷积神经网络
我们把之前章节写的神经网络照搬过来,把通道数改为3
(之前是灰度图像,通道数为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.定义一个损失函数和优化器
这次我们使用Cross-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): # loop over the dataset multiple times
running_loss = 0.0
for i, data in enumerate(trainloader, 0):
# get the inputs; data is a list of [inputs, labels]
inputs, labels = data
# zero the parameter gradients
optimizer.zero_grad()
# forward + backward + optimize
outputs = net(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
# print statistics
running_loss += loss.item()
if i % 2000 == 1999: # print every 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.272
[1, 4000] loss: 1.926
[1, 6000] loss: 1.724
[1, 8000] loss: 1.620
[1, 10000] loss: 1.549
[1, 12000] loss: 1.488
[2, 2000] loss: 1.407
[2, 4000] loss: 1.392
[2, 6000] loss: 1.381
[2, 8000] loss: 1.351
[2, 10000] loss: 1.327
[2, 12000] loss: 1.306
Finished Training
快速保存当前训练模型的方法:
PATH = './cifar_net.pth'
torch.save(net.state_dict(), PATH)
5.使用测试数据验证已训练的神经网络
我们用完整数据集在网络上训练了两次。接下来我们需要确认网络是否在数据集上学习到了有效的知识。
我们通过网络预测的图像分类类别来进行验证,通过比较网络输出的类别与实际的类别,如果正确的话,我们就把结果添加到正确的预测列表中。
首先,我们从测试集中挑拣几张图片进行显示:
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
接下里,我们加载已训练好的模型参数(注意:保存并重新加载模型参数并非必要操作,这里只是教大家如何来做):
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 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: 53 %
结果看起来比如乱猜的结果好很多(胡乱猜的化,准确率是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 : %2d %%' % (
classes[i], 100 * class_correct[i] / class_total[i]))
显示:
Accuracy of plane : 57 %
Accuracy of car : 85 %
Accuracy of bird : 34 %
Accuracy of cat : 41 %
Accuracy of deer : 34 %
Accuracy of dog : 49 %
Accuracy of frog : 79 %
Accuracy of horse : 53 %
Accuracy of ship : 62 %
Accuracy of truck : 42 %
那接下里,我们来看看怎么在GPU
上训练神经网络(速度更快哦)
在GPU
上进行训练
在GPU
上训练神经网络就像之前我们在GPU
上运行Tensor
一样简单。
首先我们把电脑中第一个支持CUDA
的显卡制定为我们的设备:
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# Assuming that we are on a CUDA machine, this should print a CUDA device:
print(device)
显示:
cuda:0
接下里的部分我们默认device
就是一个支持CUDA
的设备。下面我们使用的方法会递归的遍历模型里的所有参数和缓存,并将它们转换成CUDA
Tensors
:
net.to(device)
不要忘记输入的数据和标签值也要转化成GPU
Tensors
:
inputs, labels = data[0].to(device), data[1].to(device)
奇怪,为什么在GPU
上训练速度好像没有快多少啊啊啊?
提示:
请尝试加宽你的网络(第一层nn.Conv2d
网络的第2
个参数和第二层nn.Conv2d
网络的第1
个参数它们必须保持一致),看看训练速度是否有提升。
目标达成:
- 在更高的层次来理解
PyTorch
的Tensor
库和神经网络。 - 训练一个微神经网络来完成图像分类任务。