訓練一個分類器
就是這個, 你已經看到了如何定義神經網絡, 計算損失并更新網絡的權重.
現在你可能會想,
數據呢?
一般來說, 當你不得不處理圖像, 文本, 音頻或者視頻數據時, 你可以使用標準的 Python 包將數據加載到一個 numpy 數組中. 然后你可以將這個數組轉換成一個?torch.*Tensor
.
- 對于圖像, 會用到的包有 Pillow, OpenCV .
- 對于音頻, 會用的包有 scipy 和 librosa.
- 對于文本, 原始 Python 或基于 Cython 的加載, 或者 NLTK 和 Spacy 都是有用的.
特別是對于?vision
, 我們已經創建了一個叫做?torchvision
, 其中有對普通數據集如 Imagenet, CIFAR10, MNIST 等和用于圖像數據的轉換器, 即?torchvision.datasets
?和?torch.utils.data.DataLoader
.
這提供了巨大的便利, 避免了編寫重復代碼.
在本教程中, 我們將使用 CIFAR10 數據集. 它有: ‘airplane’, ‘automobile’, ‘bird’, ‘cat’, ‘deer’,‘dog’, ‘frog’, ‘horse’, ‘ship’, ‘truck’ 這些類別. CIFAR10 中的圖像大小為 3x32x32 , 即 32x32 像素的 3 通道彩色圖像.
cifar10
訓練一個圖像分類器
我們將按順序執行以下步驟:
- 加載 CIFAR10 測試和訓練數據集并規范化?
torchvision
- 定義一個卷積神經網絡
- 定義一個損失函數
- 在訓練數據上訓練網絡
- 在測試數據上測試網絡
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')
讓我們展示一些訓練圖像, 只是為了好玩 (0.0).
import matplotlib.pyplot as plt import numpy as np# 定義函數來顯示圖像def imshow(img):img = img / 2 + 0.5 # 非標準化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)))
2. 定義一個卷積神經網絡
從神經網絡部分復制神經網絡, 并修改它以獲取 3 通道圖像(而不是定義的 1 通道圖像).
from torch.autograd import Variable import torch.nn as nn import torch.nn.functional as Fclass 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 xnet = Net()
3. 定義一個損失函數和優化器
我們使用交叉熵損失函數( CrossEntropyLoss )和隨機梯度下降( SGD )優化器.
import torch.optim as optimcriterion = nn.CrossEntropyLoss() optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
4. 訓練網絡
這是事情開始變得有趣的時候. 我們只需循環遍歷數據迭代器, 并將輸入提供給網絡和優化器.
for epoch in range(2): # 循環遍歷數據集多次running_loss = 0.0for i, data in enumerate(trainloader, 0):# 得到輸入數據inputs, labels = data# 包裝數據inputs, labels = Variable(inputs), Variable(labels)# 梯度清零optimizer.zero_grad()# forward + backward + optimizeoutputs = net(inputs)loss = criterion(outputs, labels)loss.backward()optimizer.step()# 打印信息running_loss += loss.data[0]if i % 2000 == 1999: # 每2000個小批量打印一次print('[%d, %5d] loss: %.3f' %(epoch + 1, i + 1, running_loss / 2000))running_loss = 0.0print('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)))
好的, 現在讓我們看看神經網絡認為這些例子是什么:
outputs = net(Variable(images))
輸出的是10個類別的能量. 一個類別的能量越高, 則可以理解為網絡認為越多的圖像是該類別的. 那么, 讓我們得到最高能量的索引:
_, predicted = torch.max(outputs.data, 1)print('Predicted: ', ' '.join('%5s' % classes[predicted[j]]for j in range(4)))
結果看起來不錯.
讓我們看看網絡如何在整個數據集上執行.
correct = 0 total = 0 for data in testloader:images, labels = dataoutputs = 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))
訓練的準確率遠比隨機猜測(準確率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 = dataoutputs = 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] += 1for i in range(10):print('Accuracy of %5s : %2d %%' % (classes[i], 100 * class_correct[i] / class_total[i]))
好的, 接下來呢?
我們如何在 GPU 上運行這些神經網絡?
在 GPU 上訓練
就像你如何將一個張量傳遞給GPU一樣, 你將神經網絡轉移到GPU上. 這將遞歸遍歷所有模塊, 并將其參數和緩沖區轉換為CUDA張量:
net.cuda()
請記住, 您必須將輸入和目標每一步都發送到GPU:
inputs, labels = Variable(inputs.cuda()), Variable(labels.cuda())
如果發現在 GPU 上并沒有比 CPU 提速很多, 實際上是因為網絡比較小, GPU 沒有完全發揮自己的真正實力.
練習:?嘗試增加網絡的寬度(第一個?nn.Conv2d
?的參數2和第二個?nn.Conv2d
?的參數1 它們需要是相同的數字), 看看你得到什么樣的加速.
目標達成:
- 深入了解PyTorch的張量庫和神經網絡.
- 訓練一個小的神經網絡來分類圖像.
在多個GPU上進行訓練
如果你希望使用所有 GPU 來看更多的 MASSIVE 加速, 請查看可選?可選: 數據并行.