PyTorch Tutorials 4 训练一个分类器

%matplotlib inline

训练一个分类器

上一讲中已经看到如何去定义一个神经网络,计算损失值和更新网络的权重。
你如今可能在想下一步。python

关于数据?

通常状况下处理图像、文本、音频和视频数据时,可使用标准的Python包来加载数据到一个numpy数组中。
而后把这个数组转换成 torch.*Tensor数组

  • 图像可使用 Pillow, OpenCV
  • 音频可使用 scipy, librosa
  • 文本可使用原始Python和Cython来加载,或者使用 NLTK或
    SpaCy 处理

特别的,对于图像任务,咱们建立了一个包
torchvision,它包含了处理一些基本图像数据集的方法。这些数据集包括
Imagenet, CIFAR10, MNIST 等。除了数据加载之外,torchvision 还包含了图像转换器,
torchvision.datasetstorch.utils.data.DataLoader网络

torchvision包不只提供了巨大的便利,也避免了代码的重复。函数

在这个教程中,咱们使用CIFAR10数据集,它有以下10个类别
:‘airplane’, ‘automobile’, ‘bird’, ‘cat’, ‘deer’,
‘dog’, ‘frog’, ‘horse’, ‘ship’, ‘truck’。CIFAR-10的图像都是
3x32x32大小的,即,3颜色通道,32x32像素。学习

训练一个图像分类器

依次按照下列顺序进行:测试

  1. 使用torchvision加载和归一化CIFAR10训练集和测试集
  2. 定义一个卷积神经网络
  3. 定义损失函数
  4. 在训练集上训练网络
  5. 在测试集上测试网络优化

  6. 读取和归一化 CIFAR10

使用torchvision能够很是容易地加载CIFAR10。code

import torch
import torchvision
import torchvision.transforms as transforms

torchvision的输出是[0,1]的PILImage图像,咱们把它转换为归一化范围为[-1, 1]的张量。orm

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


100%|███████████████████████████████████████████████████████████████▉| 170483712/170498071 [30:34<00:00, 144484.30it/s]

Files already downloaded and verified


170500096it [30:50, 144484.30it/s]

咱们展现一些训练图像。视频

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)))


# 获取随机数据
dataiter = iter(trainloader)
images, labels = dataiter.next()

# 展现图像
imshow(torchvision.utils.make_grid(images))
# 显示图像标签
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))
ship   dog   dog plane

2. 定义一个卷积神经网络

从以前的神经网络一节复制神经网络代码,并修改成输入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. 定义损失函数和优化器

咱们使用交叉熵做为损失函数,使用带动量的随机梯度降低。

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

        # 梯度置0
        optimizer.zero_grad()

        # 正向传播,反向传播,优化
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        # 打印状态信息
        running_loss += loss.item()
        if i % 2000 == 1999:    # 每2000批次打印一次
            print('[%d, %5d] loss: %.3f' %
                  (epoch + 1, i + 1, running_loss / 2000))
            running_loss = 0.0

print('Finished Training')
[1,  2000] loss: 2.168
[1,  4000] loss: 1.848
[1,  6000] loss: 1.663
[1,  8000] loss: 1.573
[1, 10000] loss: 1.529
[1, 12000] loss: 1.458
[2,  2000] loss: 1.412
[2,  4000] loss: 1.390
[2,  6000] loss: 1.352
[2,  8000] loss: 1.317
[2, 10000] loss: 1.306
[2, 12000] loss: 1.299
Finished Training

5. 在测试集上测试网络

咱们在整个训练集上进行了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

让咱们看看神经网络认为以上图片是什么。

outputs = net(images)

输出是10个标签的能量。
一个类别的能量越大,神经网络越认为它是这个类别。因此让咱们获得最高能量的标签。

_, predicted = torch.max(outputs, 1)

print('Predicted: ', ' '.join('%5s' % classes[predicted[j]]
                              for j in range(4)))
Predicted:    cat  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: 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 : 46 %
Accuracy of   car : 61 %
Accuracy of  bird : 33 %
Accuracy of   cat : 39 %
Accuracy of  deer : 43 %
Accuracy of   dog : 54 %
Accuracy of  frog : 76 %
Accuracy of horse : 47 %
Accuracy of  ship : 75 %
Accuracy of truck : 60 %

下一步?

咱们如何在GPU上运行神经网络呢?

在GPU上训练

把一个神经网络移动到GPU上训练就像把一个Tensor转换GPU上同样简单。而且这个操做会递归遍历有所模块,并将其参数和缓冲区转换为CUDA张量。

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

# 确认咱们的电脑支持CUDA,而后显示CUDA信息:

print(device)

本节的其他部分假定device是CUDA设备。

而后这些方法将递归遍历全部模块并将模块的参数和缓冲区
转换成CUDA张量:

net.to(device)

记住:inputs 和 targets 也要转换。

inputs, labels = inputs.to(device), labels.to(device)

为何咱们没注意到GPU的速度提高不少?那是由于网络很是的小。

实践:
尝试增长你的网络的宽度(第一个nn.Conv2d的第2个参数,第二个nn.Conv2d的第一个参数,它们须要是相同的数字),看看你获得了什么样的加速。

实现的目标:

  • 深刻了解了PyTorch的张量库和神经网络
  • 训练了一个小网络来分类图片

译者注:后面咱们教程会训练一个真正的网络,使识别率达到90%以上。

多GPU训练

若是你想使用全部的GPU获得更大的加速,
请查看数据并行处理

下一步?

  • :doc:训练神经网络玩电子游戏 </intermediate/reinforcement_q_learning>
  • 在ImageNet上训练最好的ResNet
  • 使用对抗生成网络来训练一我的脸生成器
  • 使用LSTM网络训练一个字符级的语言模型
  • 更多示例
  • 更多教程
  • 在论坛上讨论PyTorch
  • Slack上与其余用户讨论
相关文章
相关标签/搜索