筆者PyTorch的全部簡單教程請訪問:http://www.lxweimin.com/nb/48831659
PyTorch教程-4:PyTorch中網(wǎng)絡(luò)的訓(xùn)練與測試
基本原理
對于要訓(xùn)練的模型,首先我們需要定義其結(jié)構(gòu),實例化一個用于計算Loss的loss_function
和一個用于更新參數(shù)的optimizer
,之后的事情就比較簡單了,只要準備訓(xùn)練數(shù)據(jù),然后設(shè)定訓(xùn)練的代數(shù)(或者停止條件)就可以進行迭代的訓(xùn)練。最后保存模型。對于驗證的模型,只要將數(shù)據(jù)傳輸進訓(xùn)練好的模型中就能得到預(yù)測的結(jié)果,當然這個過程通常是不需要計算梯度的,所以通常在 torch.no_grad()
的條件下進行。
本節(jié)以CIFAR10的一個簡單訓(xùn)練為例進行說明
準備數(shù)據(jù)
PyTorch中提供了很好的數(shù)據(jù)接口,當然也可以配合其他的數(shù)據(jù)集與數(shù)據(jù)加載工具。對于本節(jié)中的圖像類型的數(shù)據(jù),PyTorch的 torchvision
模塊提供了很好的幫助,以及 torchvision.transforms
則對圖像的預(yù)處理與變換提供了很方便的方法。這些在后邊的內(nèi)容中會詳細地介紹,這里只給出一個簡單的加載數(shù)據(jù)的例子,并將CIFR10的數(shù)據(jù)分成訓(xùn)練集和測試集:
import torch
import torchvision
import torchvision.transforms as transforms
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')
PyTorch會自動為我們下載數(shù)據(jù)集,存儲在 ./data
的目錄下:
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 verified
100.0%
這樣我們的訓(xùn)練集和測試集就準備好了,后邊可以直接使用它們。
定義網(wǎng)絡(luò)結(jié)構(gòu)
這里我們對上一節(jié)中的網(wǎng)絡(luò)結(jié)構(gòu)稍作更改,最主要的部分在于輸入層,因為CFIAR10的數(shù)據(jù)是三通道的圖片,所以輸入層的通道數(shù)需要改為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()
定理損失函數(shù)與優(yōu)化器
這里也是上一節(jié)的內(nèi)容,由于是多分類任務(wù),我們使用交叉熵損失函數(shù)(Cross-Entropy Loss Function)和帶動量(momentum)的SGD:
import torch.optim as optim
loss_function = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(),lr=0.001,momentum=0.9)
訓(xùn)練網(wǎng)路
接下來,我們開始進行網(wǎng)絡(luò)的訓(xùn)練內(nèi)容,其實很簡單,只要寫一個循環(huán),然后按照batch從訓(xùn)練數(shù)據(jù)集中取出數(shù)據(jù),然后一直喂給網(wǎng)絡(luò),得到網(wǎng)絡(luò)的預(yù)測結(jié)果后,使用 loss_function
來計算損失,然后再將損失反向傳播得到所有參數(shù)的梯度,最后使用 optimizer
更新參數(shù)即可。
下邊的例子中,我們訓(xùn)練兩代。trainloader
(以及后邊要用的testloader
)都是使用PyTorch的DataLoader
實例化的可迭代對象,所以可以對其進行enumerate
操作:
注:python中的
enumerate
接收一個可迭代對象為參數(shù)(還可以有start
參數(shù)用于表明起始下標,即下面的0
),返回一個元組,元組第一位為迭代數(shù)(從0
開始),第二位即為可迭代對象的一項:
seasons = ['Spring', 'Summer', 'Fall', 'Winter']
print(list(enumerate(seasons)))
[(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]
一個例子:
epochs = 2
# running_loss to record the losses
running_loss = 0.0
for epoch in range(epochs):
for i,data in enumerate(trainloader,0):
# get input images and their labels
inputs, labels = data
# set optimizer buffer to 0
optimizer.zero_grad()
# forwarding
outputs = net(inputs)
# computing loss
loss = loss_function(outputs, labels)
# loss backward
loss.backward()
# update parameters using optimizer
optimizer.step()
# printing some information
running_loss += loss.item()
# for every 1000 mini-batches, print the loss
if i % 1000 == 0:
print("epoch {} - iteration {}: average loss {:.3f}".format(epoch+1, i+1, running_loss/1000))
running_loss = 0.0
print("Training Finished!")
epoch 1 - iteration 0: average loss 0.001
epoch 1 - iteration 1000: average loss 1.232
epoch 1 - iteration 2000: average loss 1.258
…
epoch 1 - iteration 12000: average loss 1.156
epoch 2 - iteration 0: average loss 0.554
epoch 2 - iteration 1000: average loss 1.100
epoch 2 - iteration 2000: average loss 1.113
…
epoch 2 - iteration 12000: average loss 1.093
Training Finished!
測試網(wǎng)絡(luò)
對訓(xùn)練好的網(wǎng)絡(luò)進行測試也很簡單,其實就是將待測試的數(shù)據(jù)輸入給已經(jīng)訓(xùn)練好的網(wǎng)絡(luò)得到預(yù)測的結(jié)果即可。與訓(xùn)練網(wǎng)絡(luò)不同的是,由于不需要計算梯度,所以測試網(wǎng)絡(luò)的代碼通常在 torch.no_grad()
下完成。
在本例中,由于是一個十分類任務(wù),所以網(wǎng)絡(luò)的輸出實際上是一個大小為10的向量,表示對于該圖片屬于哪個類的概率的預(yù)測,所以需要對輸出向量做max
操作得到取得最大值的類。比如下邊的代碼就給出了對于測試集計算準確率的例子:
correct = 0
total = 0
with torch.no_grad():
for data in testloader:
images, labels = data
outputs = net(images)
# predicted is the class where outputs get the maximal value
_, predicted = torch.max(outputs.data,1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print("Accuracy of the trained network over test set is {:.3f}%".format(correct/total*100))
Accuracy of the trained network over test set is 60.420%
在GPU上訓(xùn)練/測試網(wǎng)絡(luò)
對于更大更復(fù)雜的網(wǎng)絡(luò),我們希望能夠使用GPU進行訓(xùn)練或者測試,在PyTorch中,進行運算的對象必須在同一個預(yù)算設(shè)備上(CPU或者GPU,或者多塊GPU的同一塊上),很簡單,只要將訓(xùn)練的網(wǎng)絡(luò)和數(shù)據(jù)都發(fā)送到GPU上即可,這跟將一個Tensor發(fā)送到GPU上是一致的,只不過對于模型來說,只能使用to
函數(shù)來傳遞:
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(device)
cuda
這表示我們的GPU是可用的,使用to
就可以將模型和數(shù)據(jù)發(fā)送到GPU上,對上述的代碼進行如下的改寫即可(尤其不要忘記數(shù)據(jù)的發(fā)送):
net.to(device)
inputs, labels = data[0].to(device), data[1].to(device)