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上與其餘用戶討論
相關文章
相關標籤/搜索