zoukankan      html  css  js  c++  java
  • 【PyTorch深度学习60分钟快速入门 】Part4:训练一个分类器

    太棒啦!到目前为止,你已经了解了如何定义神经网络、计算损失,以及更新网络权重。不过,现在你可能会思考以下几个方面:

    0x01 数据集

    通常,当你需要处理图像、文本、音频或视频数据时,你可以使用标准的python包将数据加载到numpy数组中。然后你可以将该数组转换成一个torch.*Tensor

    • 对于图像,Pillow、OpenCV这些包将有所帮助。
    • 对于音频,可以使用scipy和librosa包。
    • 对于文本,无论是基于原始的Python还是Cython的加载,或者NLTK和SpaCy都将有所帮助。

    具体对于图像来说,我们已经创建了一个名为torchvision的包,它为像Imagenet、CIFAR10、MNIST等公共数据集提供了数据加载器,并为图像提供了数据转换器,即torchvision.datasetstorch.utils.data.DataLoader

    这提供了极大的便利,避免了编写样板代码。

    对于本教程,我们将使用CIFAR10数据集。它包含以下10个分类:飞机、汽车、鸟、猫、鹿、狗、青蛙、马、轮船和卡车。CIFAR-10数据集中的图像大小为3x32x32,即大小为32x32像素的3通道彩色图像。

    0x02 训练一个图像分类器

    我们将按顺序执行以下步骤:

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

    1. 加载和归一化CIFAR10

    使用torchvision可以很容易地加载CIFAR10。

    import torch
    import torchvision
    import torchvision.transforms as transforms
    

    torchvision数据集的输出结果为像素值在[0,1]范围内的PILImage图像。我们将它们转换成标准化范围[-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')
    

    输出结果:

    Files already downloaded and verified
    Files already downloaded and verified
    

    为了增添一些乐趣,我们来展示一些训练图片:

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

    输出结果:

    frog  ship  bird truck
    

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

    从前面“神经网络”一节中拷贝神经网络并对其进行修改,使它接受3通道的图像(而不是原先定义的单通道图像)。

    from torch.autograd import Variable
    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):  # loop over the dataset multiple times
    
        running_loss = 0.0
        for i, data in enumerate(trainloader, 0):
            # get the inputs
            inputs, labels = data
    
            # wrap them in Variable
            inputs, labels = Variable(inputs), Variable(labels)
    
            # 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.data[0]
            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.204
    [1,  4000] loss: 1.855
    [1,  6000] loss: 1.677
    [1,  8000] loss: 1.577
    [1, 10000] loss: 1.508
    [1, 12000] loss: 1.485
    [2,  2000] loss: 1.403
    [2,  4000] loss: 1.392
    [2,  6000] loss: 1.355
    [2,  8000] loss: 1.332
    [2, 10000] loss: 1.300
    [2, 12000] loss: 1.282
    Finished Training
    

    5. 在测试数据上测试网络

    我们已经利用训练数据集对网络训练了2次。但是,我们需要检查网络是否已经学到了什么。

    我们将通过预测神经网络输出的类标签来检查它,并根据实际情况对其进行检查。如果预测是正确的,那么我们将该样本添加到正确的预测列表中。

    OK!第一步,让我们展示测试集中的一个图像,以便于我们熟悉它。

    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(Variable(images))
    

    输出结果是10个类的能量值。如果一个类的能量值越高,那么网络就越可能认为图像是该特定类。所以,我们来获取最高能量值对应的索引:

    _, predicted = torch.max(outputs.data, 1)
    
    print('Predicted: ', ' '.join('%5s' % classes[predicted[j]]
                                  for j in range(4)))
    

    输出结果:

    Predicted:    cat   car   car  ship
    

    结果看起来相当不错。

    下面,我们看一下该网络在整个数据集上的表现。

    correct = 0
    total = 0
    for data in testloader:
        images, labels = data
        outputs = net(Variable(images))
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum()
    
    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%的准确率(随机从10个类中挑选一个类)。看起来似乎该网络学到了一些东西。

    下面,我们看一下到底是哪些类别表现的很好,哪些类别表现的不好:

    class_correct = list(0. for i in range(10))
    class_total = list(0. for i in range(10))
    for data in testloader:
        images, labels = data
        outputs = net(Variable(images))
        _, predicted = torch.max(outputs.data, 1)
        c = (predicted == labels).squeeze()
        for i in range(4):
            label = labels[i]
            class_correct[label] += c[i]
            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 : 43 %
    Accuracy of   car : 67 %
    Accuracy of  bird : 27 %
    Accuracy of   cat : 60 %
    Accuracy of  deer : 44 %
    Accuracy of   dog : 36 %
    Accuracy of  frog : 64 %
    Accuracy of horse : 56 %
    Accuracy of  ship : 55 %
    Accuracy of truck : 73 %
    

    Ok,下一步我们将学习如何在GPU上运行神经网络。

    0x03 在GPU上训练

    将神经网络转移到GPU上,就像将一个张量转移到GPU上一样。这将递归地遍历所有模块,并将它们的参数和缓冲器转换为CUDA张量:

    net.cuda()
    

    记住,你还必须将每一步的输入和目标都发送到GPU上:

    inputs, labels = Variable(inputs.cuda()), Variable(labels.cuda())
    

    为什么与CPU相比,我没有看到速度的明显提升?那是因为你的网络实在是太小了。

    练习: 尝试增加网络的宽度(第一个nn.Conv2d的参数2,以及第二个nn.Conv2d的参数1,它们必须为同一个数字),然后看下速度提升效果。

    实现的目标:

    • 以更高的角度理解PyTorch的Tensor库和神经网络
    • 训练一个小型的神经网络来对图像进行分类

    0x04 在多个GPU上训练

    如果你想使用所有GPU来得到速度更大的提升,可以阅读下一节“数据并行性”。

    0x05 扩展阅读

  • 相关阅读:
    swagger2 Could not resolve pointer: /definitions
    mybatis-plus使用Oracle函数生成主键
    WebLogic安装及部署
    windows下用bat启动jar包,修改cmd标题(title)
    Windows下修改Tomcat黑窗口标题
    jar包注册为Linux服务
    Redis我想入门——数据类型
    Redis我想入门——启动
    JAVA8给我带了什么——Optional和CompletableFuture
    JAVA8给我带了什么——并行流和接口新功能
  • 原文地址:https://www.cnblogs.com/leejack/p/8388776.html
Copyright © 2011-2022 走看看