使用pytorch構建神經網路的流程以及一些問題
阿新 • • 發佈:2019-01-07
使用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)
以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