PyTorch教程-4:PyTorch中網(wǎng)絡(luò)的訓(xùn)練與測試

筆者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)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容