Pytorch學習:TORCH.NN檔案翻譯
僅供個人學習使用,參考總結了一些檔案,在下面內容中會注釋,
文章目錄
- Pytorch學習:TORCH.NN檔案翻譯
- 前言
- WHAT IS TORCH.NN REALLY?
- 一· MNIST資料下載
- 二. 神經網路從無到有 (no torch.nn)
- 三. 使用torch.nn.functional
- 五. 使用 nn.Linear 重構代碼
- 六. 使用optim重構代碼
- 七. 使用 Dataset 處理資料
- 八. 使用 DataLoader 加載資料
- 九. 添加測驗集
- 十. 創建 fit() 和 get_data()
- 十一. 使用卷積神經網路
- 十二. 使用nn.Sequential 搭建網路
- 十三. 封裝 DataLoader
- 十四. 使用GPU
- 十五. 總結
前言
TORCH.NN檔案翻譯:幫助學習torch實作神經網路模型的搭建(https://pytorch.org/tutorials/beginner/nn_tutorial.html),
WHAT IS TORCH.NN REALLY?
PyTorch提供了設計優雅的模塊和類:torch.nn,torch.optim,Dataset,DataLoader 來幫助您創建和訓練神經網路,為了充分利用這些模塊和類,并針對你的問題進行自定義,你需要真正確切地了解他們在做什么,為了深入理解,我們將首先在 MNIST 資料集上訓練基本的神經網路,而不使用這些模塊中的任何特征;我們最初將只使用最基本的 PyTorch tensor函式,然后,我們將每次遞增地從torch.nn,torch.optim,Dataset或DataLoader中添加一個特性,準確地展示每一部分的功能,以及它如何使代碼更簡潔或更靈活,
本教程假設您已經安裝了PyTorch,并且熟悉tensor操作的基礎知識,(如果您熟悉Numpy陣列操作,您會發現這里使用的PyTorch tensor操作幾乎相同),
一· MNIST資料下載
我們將使用經典的 MNIST 手寫資料集(0到9之間的黑白影像),
我們將使用pathlib庫來處理路徑(Python3自帶的標準庫),并使用request下載資料集,我們將只在使用模塊時才匯入它們,這樣你就可以清楚地看到在每個點上使用了什么,
from pathlib import Path
import requests
DATA_PATH = Path("data")
PATH = DATA_PATH / "mnist"
PATH.mkdir(parents=True, exist_ok=True)
URL = "https://github.com/pytorch/tutorials/raw/master/_static/"
FILENAME = "mnist.pkl.gz"
if not (PATH / FILENAME).exists():
content = requests.get(URL + FILENAME).content
(PATH / FILENAME).open("wb").write(content)
該資料集采用 numpy 陣列格式,并且使用 pickle 存盤,pickle是一種特定于python的格式,用于序列化資料,
import pickle
import gzip
with gzip.open((PATH / FILENAME).as_posix(), "rb") as f:
((x_train, y_train), (x_valid, y_valid), _) = pickle.load(f, encoding="latin-1")
每個訓練圖片解析度都是28x28,并存盤為一個長度為784 (=28x28)的行,我們首先需要把它轉換成2d28x28的影像,
from matplotlib import pyplot
import numpy as np
pyplot.imshow(x_train[0].reshape((28, 28)), cmap="gray")
print(x_train.shape)

Out:
(50000,784)
PyTorch使用 torch.tensor,而不是 numpy 陣列,所以我們需要轉換資料型別,
import torch
x_train, y_train, x_valid, y_valid = map(
torch.tensor, (x_train, y_train, x_valid, y_valid)
)
n, c = x_train.shape
x_train, x_train.shape, y_train.min(), y_train.max()
print(x_train, y_train)
print(x_train.shape)
print(y_train.min(), y_train.max())
Out:
tensor([[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.],
...,
[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.]]) tensor([5, 0, 4, ..., 8, 4, 8])
torch.Size([50000, 784])
tensor(0) tensor(9)
二. 神經網路從無到有 (no torch.nn)
我們首先創建一個只使用 PyTorch tensor 運算的模型,我們假設你已經熟悉了神經網路的基礎知識,(如果你不是,你可以在 course.fast.ai中學習它們),
PyTorch提供了創建隨機或 zero-filled tensor 的方法,我們將使用這些方法為一個簡單的線性模型創建權重和偏差,這些只是普通張量,有一個特別的補充:我們告訴PyTorch它們需要一個梯度,這使得PyTorch記錄對張量所做的所有操作,以便在反向傳播程序中自動計算梯度,
對于 weights,我們在初始化之后設定requires_grad,因為我們不希望梯度中包含該步驟,(注意 PyTorch 中末尾的 _表示操作 在in-place中執行 ,)
NOTE
我們用 Xavier initialisation來初始化weights(通過乘以1/sqrt(n)),
weights = torch.randn(784, 10) / math.sqrt(784)
weights.requires_grad_()
bias = torch.zeros(10, requires_grad=True)
由于PyTorch能夠自動計算梯度,我們可以使用任何標準的Python函式(或可呼叫物件)作為模型,所以讓我們寫一個簡單的矩陣乘法和加法來創建一個簡單的線性模型,我們還需要一個激活函式,因此我們將撰寫log_softmax并使用它,請記住:盡管PyTorch提供了許多預先撰寫的 loss functions、activation functions等,但您可以使用普通python輕松撰寫自己的函式,PyTorch甚至會為你的函式自動創建快速的GPU或向量化CPU代碼,
def log_softmax(x):
return x - x.exp().sum(-1).log().unsqueeze(-1)
def model(xb):
return log_softmax(xb @ weights + bias)
在上面,@表示點積運算,我們將對一批資料(在本例中為64張影像)呼叫函式,這是一次向前傳遞,注意,在這個階段,我們的預測不會比隨機更好,因為我們從隨機權重開始的,
bs = 64 # batch size
xb = x_train[0:bs] # a mini-batch from x
preds = model(xb) # predictions
preds[0], preds.shape
print(preds[0], preds.shape)
Out:
tensor([-3.0996, -1.8860, -2.5834, -2.1306, -2.3398, -2.4134, -1.9404, -2.8883,
-2.5544, -1.9238], grad_fn=<SelectBackward>) torch.Size([64, 10])
如你所見,`preds`tensor不僅包含張量值,還包含一個梯度函式,稍后我們將使用它來做反向傳播, 我們把 negative log-likelihood 作為損失函式,又叫交叉熵損失函式(同樣,我們可以使用標準Python): ```python def nll(input, target): return -input[range(target.shape[0]), target].mean()
loss_func = nll
我們用隨機模型來檢查我們的損失,稍后就能看到再反向傳播之后是否會改善模型性能,,
```python
yb = y_train[0:bs]
print(loss_func(preds, yb))
Out:
tensor(2.3493, grad_fn=<NegBackward>)
我們用一個準確度函式來計算我們的模型的精度,對于每個預測,如果最大值的索引與目標值匹配,則預測是正確的,
def accuracy(out, yb):
preds = torch.argmax(out, dim=1)
return (preds == yb).float().mean()
我們檢查一下我們的隨機模型的準確性,這樣我們就可以看到我們的準確性是否會隨著損失的增加而提高,
print(accuracy(preds, yb))
Out:
tensor(0.0938)
現在我們回圈訓練模型,對于每一次迭代,我們都會這樣做:
- 選擇一小批資料(大小為一個batch size)
- 使用模型進行預測
- 計算損失函式
- 反向傳播更新引數 weights 和 bias
我們現在使用 torch.no_grad() 更新引數,以避免引數更新程序被記錄求導函式中,你可以在這里閱讀更多關于PyTorch的Autograd如何記錄操作的內容,
然后我們將梯度設定為0,以便為下一個回圈做好準備,否則,導數會在原來的基礎上累加(即loss.backward()將梯度添加到已經存盤的內容中,而不是替換它們),
TIP
您可以使用標準的 python 除錯器逐步執行PyTorch代碼,允許您在每一步檢查各種變數值,取消下面的set_trace() 注釋嘗試著去使用
from IPython.core.debugger import set_trace
lr = 0.5 # learning rate
epochs = 2 # how many epochs to train for
for epoch in range(epochs):
for i in range((n - 1) // bs + 1):
# set_trace()
start_i = i * bs
end_i = start_i + bs
xb = x_train[start_i:end_i]
yb = y_train[start_i:end_i]
pred = model(xb)
loss = loss_func(pred, yb)
loss.backward()
with torch.no_grad():
weights -= weights.grad * lr
bias -= bias.grad * lr
weights.grad.zero_()
bias.grad.zero_()
就是這樣:我們完全從頭開始創建和訓練了一個最小的神經網路(在這種情況下,是一個邏輯回歸,因為我們沒有隱藏層)
讓我們檢查一下損失和準確率,并與我們之前得到的進行比較,我們預計損失會減少,準確度會提高,事實也確實如此,
print(loss_func(model(xb), yb), accuracy(model(xb), yb))
Out:
tensor(0.0831, grad_fn=<NegBackward>) tensor(1.)
三. 使用torch.nn.functional
下面,我們將重構代碼,使之與以前一樣,只是我們將開始利用 PyTorch 的nn類,使其更簡潔和靈活,接下來的每一步,我們都應該使代碼,更短,更容易理解,和/或更靈活,
第一步 也是最簡單的步驟是用torch.nn.functional中的激活和損失函式替換撰寫的激活和損失函式(它通常被匯入到名稱空間 F 中:import torch.nn.functional as F),從而縮短代碼,這個模塊包含了 torch.nn 庫中的所有功能,(也包含大量的其它類),除了大量的損失函式和激活函式之外,還有很多創建神經網路的便捷函式,例如pooling函式,(也有用于卷積層、線性層等的函式)
如果使用 log likelihood loss 和 log softmax activation,那么Pytorch提供了一個F.cross_entropy函式,它結合了這兩個函式,所以我們甚至可以把激活函式與損失函式從模型中移除,
import torch.nn.functional as F
loss_func = F.cross_entropy
def model(xb):
return xb @ weights + bias
注意,我們不再呼叫log_softmax,讓我們確認一下我們的損失和精度是否和以前一樣,
print(loss_func(model(xb), yb), accuracy(model(xb), yb))
Out:
tensor(0.0831, grad_fn=<NllLossBackward>) tensor(1.)
# 四. 使用 nn.Module 重構代碼 **接下來**,我們將使用`nn.Module`和`nn.Parameter`,使訓練程序更清晰、更簡潔,我們創建子類 `nn.Module`(它本身是一個類,能夠跟蹤狀態),在本例中,我們希望創建一個為下一步保存權重、偏差和方法的類,`nn.Module` 有許多我們能呼叫的屬性和方法(如`.parameters()`和`.zero_grad())`,
NOTE
nn.Module(大寫M)是 PyTorch 特有的概念,也是我們將經常使用的類,不要將nn.Module與 Python 概念中的(小寫的m) module混淆,它是一個可以匯入的Python代碼檔案,
from torch import nn
class Mnist_Logistic(nn.Module):
def __init__(self):
super().__init__()
self.weights = nn.Parameter(torch.randn(784, 10) / math.sqrt(784))
self.bias = nn.Parameter(torch.zeros(10))
def forward(self, xb):
return xb @ self.weights + self.bias
我們現在使用的是一個物件而不是一個函式,所以我們首先必須實體化我們的模型,
model = Mnist_Logistic()
現在我們可以像以前一樣計算損失,注意nn.Module物件被當作函式來呼叫,Pytorch會自動呼叫物件內部的forward方法,
print(loss_func(model(xb), yb))
Out:
tensor(2.5339, grad_fn=<NllLossBackward>)
在之前的訓練中,我們必須按名稱更新每個引數的值,并分別手動清除每個引數的梯度,如下所示:
with torch.no_grad():
weights -= weights.grad * lr
bias -= bias.grad * lr
weights.grad.zero_()
bias.grad.zero_()
現在,我們可以利用model.parameters()和model.zero_grad()(它們都是由PyTorch為nn.Module定義的) 使這些步驟更簡潔,更不易出現忘記一些引數的錯誤,特別是當我們有一個更復雜的模型時,
with torch.no_grad():
for p in model.parameters(): p -= p.grad * lr
model.zero_grad()
現在我們將整個訓練程序寫進函式 fit中,
def fit():
for epoch in range(epochs):
for i in range((n - 1) // bs + 1):
start_i = i * bs
end_i = start_i + bs
xb = x_train[start_i:end_i]
yb = y_train[start_i:end_i]
pred = model(xb)
loss = loss_func(pred, yb)
loss.backward()
with torch.no_grad():
for p in model.parameters():
p -= p.grad * lr
model.zero_grad()
fit()
讓我們再次確認我們的損失是否減少了:
print(loss_func(model(xb), yb))
Out:
tensor(0.0831, grad_fn=<NllLossBackward>)
五. 使用 nn.Linear 重構代碼
我們將改用 Pytorch 類 nn.Linear創建線性層,而不是人工定義和初始化self.weights, self.bias,和計算xb @ self.weights + self.bias,Pytorch 有許多型別的預定義層,它們可以極大地簡化我們的代碼,而且通常也會使代碼運行得更快,
class Mnist_Logistic(nn.Module):
def __init__(self):
super().__init__()
self.lin = nn.Linear(784, 10)
def forward(self, xb):
return self.lin(xb)
我們實體化我們的模型,并以與之前相同的方式計算損失:
model = Mnist_Logistic()
print(loss_func(model(xb), yb))
Out:
tensor(2.4207, grad_fn=<NllLossBackward>)
我們仍然可以使用與之前相同的fit方法,
fit()
print(loss_func(model(xb), yb))
Out:
tensor(0.0814, grad_fn=<NllLossBackward>)
六. 使用optim重構代碼
Pytorch還有一個包含各種優化演算法的包torch.optim,我們可以使用優化器中的step方法來進一步執行步驟,而不是手動更新每個引數,這將讓我們替換之前手工編碼的優化步驟:
with torch.no_grad():
for p in model.parameters(): p -= p.grad * lr
model.zero_grad()
僅僅使用:
opt.step()
opt.zero_grad()
optiml .zero_grad()將梯度重置為0,我們需要在為下一個 minibatch 計算梯度之前呼叫它,
from torch import optim
我們將定義一個小函式來創建我們的模型和優化器,
def get_model():
model = Mnist_Logistic()
return model, optim.SGD(model.parameters(), lr=lr)
model, opt = get_model()
print(loss_func(model(xb), yb))
for epoch in range(epochs):
for i in range((n - 1) // bs + 1):
start_i = i * bs
end_i = start_i + bs
xb = x_train[start_i:end_i]
yb = y_train[start_i:end_i]
pred = model(xb)
loss = loss_func(pred, yb)
loss.backward()
opt.step()
opt.zero_grad()
print(loss_func(model(xb), yb))
)
Out:
tensor(2.3872, grad_fn=<NllLossBackward>)
tensor(0.0823, grad_fn=<NllLossBackward>)
七. 使用 Dataset 處理資料
PyTorch有一個抽象資料集類,資料集可以是任何具有__len__函式(由Python的標準len函式呼叫)和__getitem__函式作為其索引方式的資料集,這個教程創建一個自定義FacialLandmarkDataset類作為Dataset的子類的示例,
PyTorch 的 TensorDataset是一個包含張量的資料集,通過定義長度和索引方式,這也為我們提供了一種沿著張量的第一個維度進行迭代、索引和切片的方式,這將使我們更容易在同一行代碼中訪問自變數和因變數,
from torch.utils.data import TensorDataset
x_train和y_train都可以組合在一個TensorDataset中,這將更容易迭代和切片,
train_ds = TensorDataset(x_train, y_train)
以前,我們必須分別迭代x和y的一個batch.
xb = x_train[start_i:end_i]
yb = y_train[start_i:end_i]
現在,我們可以一起做這兩個步驟:
xb,yb = train_ds[i*bs : i*bs+bs]
model, opt = get_model()
for epoch in range(epochs):
for i in range((n - 1) // bs + 1):
xb, yb = train_ds[i * bs: i * bs + bs]
pred = model(xb)
loss = loss_func(pred, yb)
loss.backward()
opt.step()
opt.zero_grad()
print(loss_func(model(xb), yb))
Out:
tensor(0.0805, grad_fn=<NllLossBackward>)
八. 使用 DataLoader 加載資料
Pytorch的DataLoader負責批量加載資料,您可以在任何 Dataset 創建DataLoader,DataLoader 使Dataset 更容易在batches中迭代,不必使用train_ds[i*bs: i*bs+bs], DataLoader 會自動為我們提供每個小batch,
from torch.utils.data import DataLoader
train_ds = TensorDataset(x_train, y_train)
train_dl = DataLoader(train_ds, batch_size=bs)
之前我們讀取資料的方式:
for i in range((n-1)//bs + 1):
xb,yb = train_ds[i*bs : i*bs+bs]
pred = model(xb)
現在:
for xb,yb in train_dl:
pred = model(xb)
model, opt = get_model()
for epoch in range(epochs):
for xb, yb in train_dl:
pred = model(xb)
loss = loss_func(pred, yb)
loss.backward() #loss反向傳播
opt.step() #下一步求導
opt.zero_grad() #導數清零
print(loss_func(model(xb), yb))
Out:
tensor(0.0827, grad_fn=<NllLossBackward>)
通過使用nn.Module, nn.Parameter, Dataset和DataLoader, 我們的訓練模型已經得到了很大的改進,接下來讓我們開始模型的測驗部分,
九. 添加測驗集
在前面,我們只是嘗試建立一個合理的回圈訓練,用于我們的訓練集資料,實際上,您還應該有一個驗證集,以便識別是否過擬合,
打亂訓練資料防止batches間相關性和過擬合是很重要的,另一方面,不管是否打亂驗證集,驗證損失都是相同的,因為打亂需要額外的時間,所以打亂驗證資料是沒有意義的,
我們將為驗證集設定兩倍于訓練集的batches大小,這是因為驗證集不需要反向傳播,因此占用的記憶體更少(它不需要存盤梯度),我們利用這一點來使用更大的批處理規模,并更快地計算損失,
train_ds = TensorDataset(x_train, y_train)
train_dl = DataLoader(train_ds, batch_size=bs, shuffle=True)
valid_ds = TensorDataset(x_valid, y_valid)
valid_dl = DataLoader(valid_ds, batch_size=bs * 2)
我們將在每個epoch結束時計算并列印驗證損失,
(注意,我們總是在訓練之前呼叫model.train(),在測驗時呼叫model.eval(),這些操作由nn.BatchNorm2d和nn.Dropout來保證,)
model, opt = get_model()
for epoch in range(epochs):
model.train()
for xb, yb in train_dl:
pred = model(xb)
loss = loss_func(pred, yb)
loss.backward()
opt.step()
opt.zero_grad()
model.eval()
with torch.no_grad():
valid_loss = sum(loss_func(model(xb), yb) for xb, yb in valid_dl)
print(epoch, valid_loss / len(valid_dl))
Out:
0 tensor(0.3443)
1 tensor(0.2742)
十. 創建 fit() 和 get_data()
現在我們繼續改進,由于我們計算過訓練集和驗證集的損失兩次,我們將其放入自己的函式loss_batch中,該函式計算一個 batch 的損失,
我們為訓練集傳遞一個優化器,并使用它來執行 反向傳播,對于驗證集,我們沒有傳遞優化器,因此該方法不會執行反向傳播,
def loss_batch(model, loss_func, xb, yb, opt=None):
loss = loss_func(model(xb), yb)
if opt is not None:
loss.backward()
opt.step()
opt.zero_grad()
return loss.item(), len(xb)
fit運行必要的操作來訓練模型,并計算每個 epoch 的訓練和驗證損失,
import numpy as np
def fit(epochs, model, loss_func, opt, train_dl, valid_dl):
for epoch in range(epochs):
model.train()
for xb, yb in train_dl:
loss_batch(model, loss_func, xb, yb, opt)
model.eval()
with torch.no_grad():
losses, nums = zip(
*[loss_batch(model, loss_func, xb, yb) for xb, yb in valid_dl]
)
val_loss = np.sum(np.multiply(losses, nums)) / np.sum(nums)
print(epoch, val_loss)
get_data回傳訓練和驗證集的dataloaders,
def get_data(train_ds, valid_ds, bs):
return (
DataLoader(train_ds, batch_size=bs, shuffle=True),
DataLoader(valid_ds, batch_size=bs * 2),
)
現在,我們獲得資料加載器和擬合模型的整個程序可以在3行代碼中運行:
train_dl, valid_dl = get_data(train_ds, valid_ds, bs)
model, opt = get_model()
fit(epochs, model, loss_func, opt, train_dl, valid_dl)
Out:
0 0.36388149040937423
1 0.28438832886219023
您可以使用這些基本的3行代碼來訓練各種各樣的模型,讓我們看看能否用它們來訓練卷積神經網路(CNN)
十一. 使用卷積神經網路
我們現在要用三個卷積層來構建我們的神經網路,因為前面的函式都沒有假設模型的任何形式,所以我們可以使用它們來訓練CNN,而不需要任何修改,
我們將使用 Pytorch 預定義的 Conv2d類作為卷積層,我們定義一個有3個卷積層的CNN,每個卷積后面跟著一個ReLU,最后,我們加一個平均池化層,(注意,該 view 是 PyTorch 版本的 numpy reshape(變形))
class Mnist_CNN(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(1, 16, kernel_size=3, stride=2, padding=1)
self.conv2 = nn.Conv2d(16, 16, kernel_size=3, stride=2, padding=1)
self.conv3 = nn.Conv2d(16, 10, kernel_size=3, stride=2, padding=1)
def forward(self, xb):
xb = xb.view(-1, 1, 28, 28)
xb = F.relu(self.conv1(xb))
xb = F.relu(self.conv2(xb))
xb = F.relu(self.conv3(xb))
xb = F.avg_pool2d(xb, 4)
return xb.view(-1, xb.size(1))
lr = 0.1
動量是隨機梯度下降的一個引數,它也考慮了之前的更新,通常會使訓練更快,
model = Mnist_CNN()
opt = optim.SGD(model.parameters(), lr=lr, momentum=0.9)
fit(epochs, model, loss_func, opt, train_dl, valid_dl)
Out:
0 0.32859203605651854
1 0.257646977686882
十二. 使用nn.Sequential 搭建網路
torch.nn還有另一個可用來簡化代碼的類:Sequential,Sequential物件以順序的方式運行其包含的每個moudle,這是我們神經網路的一種更簡單的寫法,
要利用這一點,我們需要能夠輕松地從給定的函式定義一個custom layer,例如,PyTorch沒有 view 層,我們需要為我們的網路創建一個,Lambda將創建一個層,我們可以在使用Sequential定義網路時使用它,
class Lambda(nn.Module):
def __init__(self, func):
super().__init__()
self.func = func
def forward(self, x):
return self.func(x)
def preprocess(x):
return x.view(-1, 1, 28, 28)
使用Sequential創建的模型很簡單:
model = nn.Sequential(
Lambda(preprocess),
nn.Conv2d(1, 16, kernel_size=3, stride=2, padding=1),
nn.ReLU(),
nn.Conv2d(16, 16, kernel_size=3, stride=2, padding=1),
nn.ReLU(),
nn.Conv2d(16, 10, kernel_size=3, stride=2, padding=1),
nn.ReLU(),
nn.AvgPool2d(4),
Lambda(lambda x: x.view(x.size(0), -1)),
)
opt = optim.SGD(model.parameters(), lr=lr, momentum=0.9)
fit(epochs, model, loss_func, opt, train_dl, valid_dl)
Out:
0 0.4616120267510414
1 0.24237275631427765
十三. 封裝 DataLoader
我們的 CNN 相當簡潔,但它只適用于 MNIST ,因為
- 它假設輸入是一個28*28長的向量
- 假設最終的CNN卷積核大小為4*4(這是我們使用的平均池化層核的大小)
讓我們去掉這兩個假設,這樣我們的模型就可以處理任何 2d 單通道影像,首先,我們可以通過將資料預處理移動到生成器中來洗掉初始的 Lambda 層(資料預處理層代替Lambda層),
def preprocess(x, y):
return x.view(-1, 1, 28, 28), y
class WrappedDataLoader:
def __init__(self, dl, func):
self.dl = dl
self.func = func
def __len__(self):
return len(self.dl)
def __iter__(self):
batches = iter(self.dl)
for b in batches:
yield (self.func(*b))
train_dl, valid_dl = get_data(train_ds, valid_ds, bs)
train_dl = WrappedDataLoader(train_dl, preprocess)
valid_dl = WrappedDataLoader(valid_dl, preprocess)
接下來,我們可以用nn.AdaptiveAvgPool2d替換nn.AvgPool2d,它允許我們定義我們想要的輸出張量的維度,而不是我們的輸入張量,因此,我們的模型可以處理任何大小的輸入,
model = nn.Sequential(
nn.Conv2d(1, 16, kernel_size=3, stride=2, padding=1),
nn.ReLU(),
nn.Conv2d(16, 16, kernel_size=3, stride=2, padding=1),
nn.ReLU(),
nn.Conv2d(16, 10, kernel_size=3, stride=2, padding=1),
nn.ReLU(),
nn.AdaptiveAvgPool2d(1),
Lambda(lambda x: x.view(x.size(0), -1)),
)
opt = optim.SGD(model.parameters(), lr=lr, momentum=0.9)
我們來試試:
fit(epochs, model, loss_func, opt, train_dl, valid_dl)
Out:
0 0.3511354235649109
1 0.24440611186027528
十四. 使用GPU
你需要有一個GPU,
首先檢測設備是否正常支持GPU:
print(torch.cuda.is_available())
Out:
True
創建一個設備物件:
dev = torch.device(
"cuda") if torch.cuda.is_available() else torch.device("cpu")
讓我們更新preprocess,將batch移動到GPU:
def preprocess(x, y):
return x.view(-1, 1, 28, 28).to(dev), y.to(dev)
train_dl, valid_dl = get_data(train_ds, valid_ds, bs)
train_dl = WrappedDataLoader(train_dl, preprocess)
valid_dl = WrappedDataLoader(valid_dl, preprocess)
最后,我們可以將模型移動到GPU上
model.to(dev)
opt = optim.SGD(model.parameters(), lr=lr, momentum=0.9)
你應該會發現它現在運行得更快了:
fit(epochs, model, loss_func, opt, train_dl, valid_dl)
Out:
0 0.1826939118027687
1 0.17859829986691475
十五. 總結
現在我們有了一個通用的資料 pipline 和模型訓練方法,您可以使用 Pytorch 來訓練多種型別的模型,要了解現在訓練模型是多么簡單,請回顧一下本博客,
當然,還有很多東西需要添加,比如資料增強、超引數調優、監督訓練、遷移學習等等,這些特性可以在 fastai 庫中獲得,
我們在本教程的開始承諾,我們將通過示例解釋每個torch.nn,torch.optim,Dataset,和DataLoader,讓我們總結一下,
torch.nn
Module:創建一個像函式一樣的可呼叫的物件,但也可以包含各種網路狀態(如神經網路層權重),可以使用parameter獲取模型引數 (Parameter (s)),并可以使所有引數的梯度為零,并通過回圈它們來更新權重,等等,Parameter:一個張量的封裝器(打包模型中需要更新的引數),它告訴 Module 它有權重,需要在反向傳播期間更新,只有帶有requires_grad屬性集的張量才會被更新,functional:一個模塊(通常匯入到F中),包含激活函式、損失函式等,以及非狀態層,如卷積層和線性層,
torch.optim:包含諸如 SGD 之類的優化器,它在反向傳播中更新 Parameter 的權重,
Dataset:一個包含__len__和__getitem__等函式的抽象介面,包括 Pytorch 提供的類,如TensorDataset,
DataLoader:輸入任意的 Dataset 并按批(batch)迭代輸出資料,
腳本總運行時間:(0分37.323秒),
參考資料
WHAT IS TORCH.NN REALLY?
深入理解 TORCH.NN
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/275157.html
標籤:python
