1. 程式人生 > >使用pytorch構建神經網路的流程以及一些問題

使用pytorch構建神經網路的流程以及一些問題

使用PyTorch構建神經網路十分的簡單,下面是我總結的PyTorch構建神經網路的一般過程以及我在學習當中遇到的一些問題,期望對你有所幫助。

PyTorch構建神經網路的一般過程

下面的程式是PyTorch官網60分鐘教程上面構建神經網路的例子,版本0.4.1:

import torch
import torchvision
import torchvision.transforms as transforms
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

# 第一步:準備資料
# Compose是將兩個轉換的過程組合起來,ToTensor將numpy等資料型別轉換為Tensor,將值變為0到1之間
# Normalize用公式(input-mean)/std 將值進行變換。這裡mean=0.5,std=0.5,是將[0,1]區間轉換為[-1,1]區間
transform = transforms.Compose(
    [transforms.ToTensor(),
     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
     
# trainloader 是一個將資料集和取樣策略結合起來的,並提供在資料集上面迭代的方法
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=0)

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=0)

classes = ('plane', 'car', 'bird', 'cat',
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')



# 第二步:構建神經網路框架,繼承nn.Module類
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()



# 第三步:進行訓練
# 定義損失策略和優化方法
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

# 訓練神經網路
for epoch in range(4):
    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
        inputs, labels = data
        
        optimizer.zero_grad()
        
        # 訓練過程1:前向過程,計算輸入到輸出的結果
        outputs = net(inputs)
        
        # 訓練過程2:由結果和label計算損失
        loss = criterion(outputs, labels)

        # 訓練過程3:在圖的層次上面計算所有變數的梯度
        # 每次計算梯度的時候,其實是有一個動態的圖在裡面的,求導數就是對圖中的引數w進行求導的過程
        # 每個引數計算的梯度值儲存在w.grad.data上面,在引數更新時使用
        loss.backward()

        # 訓練過程4:進行引數的更新
        # optimizer不計算梯度,它利用已經計算好的梯度值對引數進行更新
        optimizer.step()

        running_loss += loss.item()  # item 返回的是一個數字
        if i % 2000 == 1999:
            print('[%d, %5d] loss: %.3f' %
                  (epoch+1, i+1, running_loss/2000))
            running_loss = 0.0
print('Finished Training')



# 第四步:在測試集上面進行測試
total = 0
correct = 0
with torch.no_grad():
    for data in testloader:
        images, label = data
        outputs = net(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == label).sum().item()

print("Accuracy of networkd on the 10000 test images: %d %%" % (100*correct/total))

這個例子說明了構建神經網路的四個步驟:1:準備資料集 。2:構建神經網路框架,實現神經網路的類。 3:在訓練集上進行訓練。 4:在測試集上面進行測試。

而在第三步的訓練階段,也可以分為四個步驟:1:前向過程,計算輸入到輸出的結果。2:由結果和labels計算損失。3:後向過程,由損失計算各個變數的梯度。4:優化器根據梯度進行引數的更新。

訓練過程中第loss和optim是怎麼聯絡在一起的

loss是訓練階段的第三步,計算引數的梯度。optim是訓練階段的第四步,對引數進行更新。在optimizer初始化的時候,optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

,獲取了引數的指標,可以對引數進行修改。當loss計算好引數的梯度以後,把值放在引數w.grad.data上面,然後optimizer直接利用這個值對引數進行更新。

以SGD為例,它進行step的時候的基本操作是這樣的:p.data.add_(-group['lr'], d_p),其中 d_p = p.grad.data

為什麼要進行梯度清零

在backward每次計算梯度的時候,會將新的梯度值加到原來舊的梯度值上面,這叫做梯度累加。下面的程式可以說明什麼是梯度累加:

import torch
x = torch.rand(2, requires_grad=True)
y = x.mean()  # y = (x_1 + x_2) / 2  所以求梯度後應是0.5

y.backward()
print(x.grad.data) # 輸出結果:tensor([0.5000, 0.5000])

y.backward()
print(x.grad.data) # 輸出結果:tensor([1., 1.])  說明進行了梯度累積

求解梯度過程和引數更新過程是分開的,這對於那些需要多次求導累計梯度,然後一次更新的神經網路可能是有幫助的,比如RNN,對於DNN和CNN不需要進行梯度累加,所以需要進行梯度清零。

如何使用GPU進行訓練

舊版本:

use_cuda = True if torch.cuda.is_available() else False  # 是否使用cuda
if use_cuda:
    model = model.cuda()  # 將模型的引數放入GPU
if use_cuda:
    inputs, labels = inputs.cuda(), labels.cuda()  # 將資料放入到GPU

0.4版本以後推薦新方法 to(device),

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
net.to(device)  #將模型的引數放入GPU中
inputs, labels = inputs.to(device), labels.to(device)  # 將資料放入到GPU中

參考:
Pytorch內部中optim和loss是如何互動的? - 羅若天的回答 - 知乎
pytorch學習筆記(二):gradient