深入理解 TORCH.NN
原文地址:WHAT IS TORCH.NN REALLY?
本人英語(yǔ)學(xué)渣,如有錯(cuò)誤請(qǐng)及時(shí)指出以便更正,使用的源碼可點(diǎn)擊原文地址進(jìn)行下載。
pytorch提供了許多優(yōu)雅的類和模塊幫助我們構(gòu)建與訓(xùn)練網(wǎng)絡(luò),比如 torch.nn, torch.optim,Dataset等。為了充分利用這些模塊的功能,靈活操作它們解決各種不同的問題,我們需要更好地理解當(dāng)我們調(diào)用這些模塊時(shí)它們到底干了些什么,為此,我們首先不調(diào)用這些模塊實(shí)現(xiàn)MNIST手寫字識(shí)別,僅使用最基本的 pytorch 張量函數(shù)。然后,我們逐漸增加 torch.nn, torch.optim, Dataset, or DataLoader,具體地展示每個(gè)模塊具體干了些什么,展示這些模塊是怎樣使代碼變得更加優(yōu)雅靈活。
此教程適用范圍:熟悉pytorch的張量操作
加載 MNIST 數(shù)據(jù)集
我們使用經(jīng)典的 MNIST 數(shù)據(jù)集,一個(gè)包含了0-9數(shù)字的二值圖像庫(kù)。
還會(huì)用到 pathlib 庫(kù)用于目錄操作,一個(gè)python3自帶的標(biāo)準(zhǔn)庫(kù)。使用 requests 下載數(shù)據(jù)集。當(dāng)用到一個(gè)模塊時(shí)才會(huì)進(jìn)行導(dǎo)入,而不會(huì)一開始全部導(dǎo)入,以便更好地理解每個(gè)步驟。
from pathlib import Path import requestsDATA_PATH = Path('data') PATH = DATA_PATH / "mnist"PATH.mkdir(parents=True,exit_ok=True)URL = "http://deeplearning.net/data/mnist/" FILENAME = "mnist.pkl.gz"if not (PATH / FILENAME).exists():content = requests.get(URL + FILENAME).content(PATH / FILENAME).open("wb").write(content)該數(shù)據(jù)集采用numpy數(shù)組格式,并使用pickle存儲(chǔ),pickle是一種特定于python的格式,用于序列化數(shù)據(jù)。
import pickle import gzipwith gzip.open((PATH / FILENAME).as_posix(),"rb") as f:((x_train,y_train),(x_valid,y_valid),_) = pickle.load(f,encoding="latin-1")每張訓(xùn)練圖片分辨率為 28x28, 被存儲(chǔ)為 784(=28x28) 的一行。我們輸出看一下數(shù)據(jù),首先需要轉(zhuǎn)換回 28x28的圖像。
form matplotlib import pyplot import numpy as nppyplot.imshow(x_train[0].reshape((28,28)),cmap="gray") print(x_train.shape) out: (50000,784)PyTorch使用 torch.tensor ,所以我們需要對(duì)numpy類型數(shù)據(jù)進(jìn)行轉(zhuǎn)換
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())從頭創(chuàng)建神經(jīng)網(wǎng)絡(luò)(不使用torch.nn)
讓我們僅僅使用 pytorch 中的張量操作來(lái)創(chuàng)建模型,假設(shè)你已經(jīng)熟悉神經(jīng)網(wǎng)絡(luò)的基礎(chǔ)知識(shí)(不熟悉請(qǐng)參考corse.fast.ai )
pytorch提供了很多創(chuàng)建張量的操作,我們將用這些方法來(lái)初始化權(quán)值weights和偏置 bais來(lái)創(chuàng)建一個(gè)線性模型。這些只是常規(guī)張量,有一個(gè)非常特別的補(bǔ)充:我們告訴PyTorch這些張量需要支持求導(dǎo)(requires_grad=True)。這樣PyTorch將記錄在張量上完成的所有操作,以便它可以在反向傳播過程中自動(dòng)計(jì)算梯度!
對(duì)于權(quán)值weights,我們?cè)俪跏蓟?strong>之后再設(shè)置 requires_grad,因?yàn)槲覀儾幌脒@一步包含在梯度的計(jì)算中(注:pytorch中以 _ 結(jié)尾的操作都是在原變量中(in-place)執(zhí)行的)
import mathweights = torch.randn(780,10) / math.sqrt(784) weights.requires_grad_() bias = torch.zeros(10, requires_grad=True)多虧了pytorch的自動(dòng)求導(dǎo)功能,我們可以使用python的所有標(biāo)準(zhǔn)函數(shù)來(lái)構(gòu)建模型。 我們這兒利用矩陣乘法,加法來(lái)構(gòu)建線性模型。我們編寫 log_softmax函數(shù)作為激活函數(shù)。 雖然pytorch提供了大量寫好的損失函數(shù),激活函數(shù),你依然可以自由地編寫自己的函數(shù)替代它們。 pytorch 甚至支持創(chuàng)建自己的 GPU函數(shù)或者CPU矢量函數(shù)。
def log_softmax(x):return x - x.exp().sum(-1).log().unsqueeze(-1)def model(xb):return log_softmax(xb @ weights + bias) # python的廣播機(jī)制上面的 @ 符號(hào)表示向量的點(diǎn)乘,接下來(lái)我們會(huì)調(diào)用一批數(shù)據(jù)(batch,64張圖片)輸入此模型。
bs = 64 # batch size xb = x_train[0:bs] # a mini-batch from x preds = model(xb) # predictions print(preds[0],preds.shape)out:
tensor([-2.4513, -2.5024, -2.0599, -3.1052, -3.2918, -2.2665, -1.9007, -2.2588,-2.0149, -2.0287], grad_fn=<SelectBackward>) torch.Size([64, 10])正如我們看到的,preds 張量不僅包含了一組張量,還包含了求導(dǎo)函數(shù)。反向傳播的時(shí)候會(huì)用到此函數(shù)。讓我們使用標(biāo)準(zhǔn)的python語(yǔ)句接著來(lái)實(shí)現(xiàn) negative log likelihood loss 損失函數(shù)(譯者加:也被稱為交叉熵?fù)p失函數(shù)):
def nll(input,target):return -input[range(target.shape[0]),target].mean()loss_func = nll現(xiàn)在用我們的損失函數(shù)來(lái)檢查我們隨機(jī)初始化的模型,待會(huì)就能看到再反向傳播之后是否會(huì)改善模型性能。
yb = y_train[0:bs] print(loss_func(preds,yb))out:
tensor(2.3620, grad_fn=<NegBackward>)接下來(lái)定義一個(gè)計(jì)算準(zhǔn)確度的函數(shù)
def accuracy(out,yb):preds = torch.argmax(out,dim=1) # 得到最大值的索引return (preds == yb).float().mean()檢查模型的準(zhǔn)確度:
print(accuracy(preds, yb))out:
tensor(0.0938)現(xiàn)在我們開始循環(huán)訓(xùn)練模型,每一步我們執(zhí)行以下操作:
- 選擇一批數(shù)據(jù)(a batch)
- 使用模型進(jìn)行預(yù)測(cè)
- 計(jì)算損失函數(shù)
- 反向傳播更新參數(shù) weights 和 bias
我們現(xiàn)在使用 torch.no_grad() 更新參數(shù),以避免參數(shù)更新過程被記錄入求導(dǎo)函數(shù)中。
然后我們清零導(dǎo)數(shù),以便開始下一輪循環(huán),否則導(dǎo)數(shù)會(huì)在原來(lái)的基礎(chǔ)上累加,而非替代原來(lái)的數(shù)
from IPython.core.debugger import set_tracelr = 0.5 # learning rate epochs = 2 # how many epochs to train forfor epoch in range(epochs):for i in range((n - 1) // bs + 1):# set_trace()start_i = i * bsend_i = start_i + bsxb = 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 * lrbias -= bias.grad * lrweights.grad.zero_()bias.grad.zero_()目前為止,我們從頭創(chuàng)建一個(gè)迷你版的神經(jīng)網(wǎng)絡(luò)
讓我們來(lái)檢查一下?lián)p失和準(zhǔn)確率,并于迭代更新參數(shù)之前進(jìn)行比較,我們期望得到更小的損失于更高的準(zhǔn)確率。
print(loss_func(model(xb), yb), accuracy(model(xb), yb))out:
tensor(0.0822, grad_fn=<NegBackward>) tensor(1.)使用 torch.nn.functional 簡(jiǎn)化代碼
現(xiàn)在我們使用torch.nn.functional重構(gòu)之前的代碼,這樣會(huì)使代碼變得更加簡(jiǎn)潔與靈活,更易理解。
首先最簡(jiǎn)單的一步是,用 torch.nn.functional( 為了方便后面統(tǒng)一稱作F) 中帶有的損失函數(shù)來(lái)代替我們自己編寫的函數(shù),使得代碼變得更簡(jiǎn)短。這些函數(shù)都包包含于模塊 torch.nn里面,除了大量的損失函數(shù)與激活函數(shù),里面還包含了大量用于構(gòu)建網(wǎng)絡(luò)的函數(shù)。
如果我們的網(wǎng)絡(luò)中使用 negative log likelihood loss 作為損失函數(shù), log softmax activation 作為激活函數(shù) (即我們上面實(shí)現(xiàn)的損失函數(shù)與激活函數(shù))。在pytorch中我們直接使用函數(shù) F.cross_entropy 便可實(shí)現(xiàn)上面兩個(gè)函數(shù)的功能。所以我們可以用此函數(shù)代替上面實(shí)現(xiàn)的激活函數(shù)與損失函數(shù)。
import torch.nn.functional as Floss_func = F.cross_entropydef model(xb):return xb @ weights + bias讓我測(cè)試一下是否和上面自己實(shí)現(xiàn)的函數(shù)效果一致:
print(loss_func(model))out:
tensor(0.0822, grad_fn=<NllLossBackward>) tensor(1.)引入 nn.Module 重構(gòu)代碼
接下來(lái)我們引入 nn.Module和nn.Parameter 改進(jìn)代碼。我們創(chuàng)建 nn.Module的子類。這個(gè)例子中我們創(chuàng)建一個(gè)包含權(quán)重,偏置,以及包含前向傳播的類。nn.Module含有許多的屬性與方法可供調(diào)用 (比如: .parameters .zero_grad())
from torch import nnclass Mnist_Logistic(nn.Module):def __init__(self):super().__init__()sefl.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接下來(lái)實(shí)例化我們的模型:
model = Mnist_Logistic()現(xiàn)在我們可以和之前一樣使用損失函數(shù)了。注意:nn.Module 對(duì)象可以像函數(shù)一樣調(diào)用,但實(shí)際上是自動(dòng)調(diào)用了對(duì)象內(nèi)部的函數(shù) forward
print(loss_func(model(xb),yb))out:
tensor(2.2082, grad_fn=<NllLossBackward>)在之前,我們必須進(jìn)行如下得操作對(duì)權(quán)重,偏置進(jìn)行更新,梯度清零:
with torch.no_grad():weights -= weights.grad * lrbias -= bias.grad * lrweights.grad.zero_()bias.grad.zero_()現(xiàn)在我們可以充分利用 nn.Module 的方法屬性更簡(jiǎn)單地完成這些操作,如下所示:
with torch.no_grad():for p in model.parameters(): p -= p.grad * lrmodel.zero_grad()現(xiàn)在我們將整個(gè)訓(xùn)練過程寫進(jìn)函數(shù) fit中。
def fit():for epoch in range(epoches):for i in range((n - 1) // bs + 1):start_i = i * bsend_i = start_i + bsxb = 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 * lrmodel.zero_grad() fit()讓我們?cè)僖淮未_認(rèn)損失情況:
print(loss_func(model(xb),yb))out:
tensor(0.0812, grad_fn=<NllLossBackward>)引入 nn.Linear 重構(gòu)代碼
比起手動(dòng)定義 權(quán)重 與 偏置,并且使用 self.weights和 self.bias 來(lái)計(jì)算 xb @ self.weights + self.bias的方式,我們可以使用pytorch中的 nn.Linear來(lái)定義線性層,他自動(dòng)為我們實(shí)現(xiàn)以上權(quán)重參數(shù)的定義以及計(jì)算的過程。除了線性模型之外,pytorch還有一系列的其它網(wǎng)絡(luò)層供我們使用,大大簡(jiǎn)化了我們的編程過程。
class Mnist_Logistic(nn.Module):def __init__(self):super().__init__()self.lin = nn.Linear(784,10)def forward(self,xb):return self.lin(xb)同上面一樣實(shí)例化模型,計(jì)算損失
model = Mnist_Logistic() print(loss_func(model(xb),yb))out:
tensor(2.2731, grad_fn=<NllLossBackward>)訓(xùn)練,并查看訓(xùn)練之后的損失
fit()print(loss_func(model(xb), yb))out:
tensor(0.0820, grad_fn=<NllLossBackward>)引入 optim 重構(gòu)代碼
接下來(lái)使用torch.optim改進(jìn)訓(xùn)練過程,而不用手動(dòng)更新參數(shù)
之前的手動(dòng)優(yōu)化過程如下:
with torch.no_grad():for p in model.parameters(): p -= p.grad * lrmodel.zero_grad()使用如下代碼替代手動(dòng)的參數(shù)更新:
opt.step() # optim.zero_grad() resets the gradient to 0 and we need to call it # before computing the gradient for the next minibatch. opt.zero_grad()結(jié)合之前的完整跟新代碼如下:
from torch import optimdef 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(epoches):for i in range((n-1)//bs + 1):start_i = i *bsend_i = start_i + bsxb = 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.3785, grad_fn=<NllLossBackward>) tensor(0.0802, grad_fn=<NllLossBackward>)引入 Dataset 處理數(shù)據(jù)
pytorch定義了 Dataset 類,其中主要包含了 __len__ 函數(shù)與 __getitem__函數(shù)。此教程以創(chuàng)建 FacialLandmarkDataset 為例詳細(xì)地介紹了Dataset類的使用。
pytorch的 TensorDataset 是一個(gè)包含張量的數(shù)據(jù)集。通過定義長(zhǎng)度索引等方式,使我們更好地利用索引,切片等方法迭代數(shù)據(jù)。這會(huì)讓我們很容易地在一行代碼中獲取我們地?cái)?shù)據(jù)。
form torch.utils.data import TensorDatasetx_train y_train可以被組合進(jìn)一個(gè)TensorDataset中,這會(huì)使得迭代切片更加簡(jiǎn)單。
train_ds = TensorDataset(x_train,y_train)之前我們獲取數(shù)據(jù)的方法如下:
xb = x_train[start_i:end_i] yb = y_train[start_i:end_i]現(xiàn)在我們可以使用更簡(jiǎn)單的方法:
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.0817, grad_fn=<NllLossBackward>)引入DataLoader加載數(shù)據(jù)
DataLoader 用于批量加載數(shù)據(jù),你可以用他來(lái)加載任何來(lái)自 Dataset的數(shù)據(jù),它使得數(shù)據(jù)的批量加載十分容易。
from torch.utils.data import DataLoadertrain_ds = TensorDataset(x_train,y_train) train_dl = DataLoader(train_ds, batch_size=bs)之前我們讀取數(shù)據(jù)的方式:
for i in range((n-1)//bs + 1):xb,yb = train_ds[i*bs : i*bs+bs]pred = model(xb)現(xiàn)在使用dataloader加載數(shù)據(jù):
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()opt.step()opt.zero_grad()print(loss_func(model(xb), yb))out:
tensor(0.0817, grad_fn=<NllLossBackward>)目前為止訓(xùn)練模型部分我們就已經(jīng)完成了,通過使用nn.Module, nn.Parameter, DataLoader, 我們的訓(xùn)練模型以及得到了很大的改進(jìn)。接下來(lái)讓我們開始模型的測(cè)試部分。
添加測(cè)試集
在前一部分,我們嘗試了使用訓(xùn)練集訓(xùn)練網(wǎng)絡(luò)。實(shí)際工作中,我們還會(huì)使用測(cè)試集來(lái)觀察訓(xùn)練的模型是否過擬合。
打亂數(shù)據(jù)的分布有助于減小每一批(batch)數(shù)據(jù)間的關(guān)聯(lián),有利于模型的泛化。但對(duì)于測(cè)試集來(lái)說(shuō),是否打亂數(shù)據(jù)對(duì)結(jié)果并沒有影響,反而會(huì)花費(fèi)多余的時(shí)間,所以我們沒有必要打亂測(cè)試集的數(shù)據(jù)。
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)在每訓(xùn)練完一輪數(shù)據(jù)(epoch)后我們輸出測(cè)試得到的損失值。
(注:如下代碼中,我們調(diào)用model.train()和model.eval表示進(jìn)入訓(xùn)練模式與測(cè)試模式,以保證模型運(yùn)行的準(zhǔn)確性)
out:
0 tensor(0.3456) 1 tensor(0.2988)創(chuàng)建 fit() 和 get_data() 優(yōu)化代碼
我們?cè)倮^續(xù)做一點(diǎn)改進(jìn)。因?yàn)槲覀冊(cè)儆?jì)算訓(xùn)練損失和驗(yàn)證損失時(shí)執(zhí)行了兩次相同的操作,所以我們用一個(gè)計(jì)算每一個(gè)batch損失的函數(shù)封裝這部分代碼。
我們?yōu)橛?xùn)練集添加優(yōu)化器,并執(zhí)行反向傳播。對(duì)于訓(xùn)練集我們不添加優(yōu)化器,當(dāng)然也不會(huì)執(zhí)行反向傳播。
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執(zhí)行每一個(gè)epoch過程中訓(xùn)練和驗(yàn)證的必要操作
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.sum(np.multiply(losses, nums)). np.sum(nums))print(epoch, val_loss)現(xiàn)在,獲取數(shù)據(jù)加載模型進(jìn)行訓(xùn)練的整個(gè)過程只需要三行代碼便能實(shí)現(xiàn)了
train_dl, valid_dl = get_data(train_ds, valid_ds, bs) model, opt = get_model() fit(epoches, model, loss_func, opt, train_dl, valid_dl)out:
0 0.2961075816631317 1 0.28558296990394594我們可以用這簡(jiǎn)單的三行代碼訓(xùn)練各種模型。下面讓我們看看怎么用它訓(xùn)練一個(gè)卷積神經(jīng)網(wǎng)絡(luò)。
使用卷積神經(jīng)網(wǎng)絡(luò)
現(xiàn)在我們用三個(gè)卷積層來(lái)構(gòu)造我們的卷積網(wǎng)絡(luò)。因?yàn)橹暗膶?shí)現(xiàn)的函數(shù)都沒有假定模型形式,這兒我們依然可以使用它們而不需要任何修改。
我們pytorch預(yù)定義的Conv2d類來(lái)構(gòu)建我們的卷積層。我們模型有三層,每一層卷積之后都跟一個(gè) ReLU,然后跟一個(gè)平均池化層。
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動(dòng)量momentum是隨機(jī)梯度下降的一個(gè)參數(shù),它考慮到了之前的梯度值使得訓(xùn)練更快。
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.3829730714321136 1 0.2258522843360901使用 nn.Sequential 搭建網(wǎng)絡(luò)
torch.nn還有另外一個(gè)方便的類可以簡(jiǎn)化我們的代碼:Sequential, 一個(gè)Sequential對(duì)象
class Lambda(nn.Module):def __init__(self, func):super().__init__()self.func = funcdef forward(self, x):return self.func(x)def preprocess(x):return x.view(-1, 1, 28, 28)Sequential是一種簡(jiǎn)化代碼的好方法。 一個(gè)Sequential對(duì)象按順序執(zhí)行包含在內(nèi)的每一個(gè)module,使用它可以很方便地建立一個(gè)網(wǎng)絡(luò)。
為了更好地使用Sequential模塊,我們需要自定義 pytorch中沒實(shí)現(xiàn)地module。例如pytorch中沒有自帶 改變張量形狀地層,我們創(chuàng)建 Lambda層,以便在Sequential中調(diào)用。
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.32739396529197695 1 0.25574398956298827簡(jiǎn)易的DataLoader
我們的網(wǎng)絡(luò)以及足夠精簡(jiǎn)了,但是只能適用于MNIST數(shù)據(jù)集,因?yàn)?/p>
- 網(wǎng)絡(luò)默認(rèn)輸入為 28x28 的張量
- 網(wǎng)絡(luò)默認(rèn)最后一個(gè)卷積層大小為 4x4 (因?yàn)槲覀兊某鼗瘜哟笮?x4)
現(xiàn)在我們?nèi)コ@兩個(gè)假設(shè),使得網(wǎng)絡(luò)可以適用于所有的二維圖像。首先我們移除最初的 Lambda層,用數(shù)據(jù)預(yù)處理層替代。
def preprocess(x, y):return x.view(-1, 1, 28, 28), yclass WrappedDataLoader:def __init__(self, dl, func):self.dl = dlself.func = funcdef __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。它允許我們自定義輸出張量的維度,而于輸入的張量無(wú)關(guān)。這樣我們的網(wǎng)絡(luò)便可以適用于各種size的網(wǎng)絡(luò)。
model = nn.Sequential(nn.Conv2d(1, 16, kernal_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)out:
0 0.32888883714675904 1 0.31000419993400574使用GPU
如果你的電腦有支持CUDA的GPU(你可以很方便地以 0.5美元/小時(shí) 的價(jià)格租到支持的云服務(wù)器),便可以使用GPU加速訓(xùn)練過程。首先檢測(cè)設(shè)備是否正常支持GPU:
print(torch.cuda.is_available())out:
Ture接著創(chuàng)建一個(gè)設(shè)備對(duì)象:
dev = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")更新 preprocess(x,y)把數(shù)據(jù)移到GPU:
def preprocess(x, y):return x.view(-1, 1, 28, 28).to(dev), y.to(device)train_dl, valid_dl = get_data(train_ds, valid_ds, bs) train_dl = WrappedDataLoader(train_dl, preprocess) valid_dl = WrappedDataLoader(valid_dl, preprocess)最后移動(dòng)網(wǎng)絡(luò)模型到GPU:
model.to(dev) opt = optim.SGD(model.parameters(),lr=lr, momentum=0.9)進(jìn)行訓(xùn)練,能發(fā)現(xiàn)速度快了很多:
fit(epochs, model, loss_func, opt, train_dl, valid_dl)out:
0 0.21190375366210937 1 0.18018000435829162總結(jié)
我們現(xiàn)在得到了一個(gè)通用的數(shù)據(jù)加載和模型訓(xùn)練方法,我們可以在pytorch種用這種方法訓(xùn)練大多的模型。想知道訓(xùn)練一個(gè)模型有多簡(jiǎn)單,回顧一下本次的代碼便可以了。
當(dāng)然,除此之外本篇內(nèi)容還有很多需求沒有講到,比如數(shù)據(jù)增強(qiáng),超參調(diào)試,數(shù)據(jù)監(jiān)控(monitoring training),遷移學(xué)習(xí)等。這些特點(diǎn)都以與本篇教程相似的設(shè)計(jì)方法包含于 fastai庫(kù)中。
本篇教程開頭我們承諾將會(huì)通過例程解釋 torch.nn torch.optim Dataset DataLoader等模塊,下面我們就這些模型進(jìn)行總結(jié)。
- torch.nn
- Module: 創(chuàng)建一個(gè)可以像函數(shù)一樣調(diào)用地對(duì)象,包含了網(wǎng)絡(luò)的各種狀態(tài),可以使用parameter方便地獲取模型地參數(shù),并有清零梯度,循環(huán)更新參數(shù)等功能。
- Parameter: 將模型中需要更新的參數(shù)全部打包,方便反向傳播過程中進(jìn)行更新。有 requires_grad屬性的參數(shù)才會(huì)被更新。
- functional:通常導(dǎo)入為F,包含了許多激活函數(shù),損失函數(shù)等。
- torch.optim: 包含了很多諸如SGD一樣的優(yōu)化器,用來(lái)在反向傳播中跟新參數(shù)
- Dataset: 一個(gè)帶有 __len__ __getitem__等函數(shù)的抽象接口。里面包含了 TensorDataset等類。
- DataLoader: 輸入任意的 Dataset 并按批(batch)迭代輸出數(shù)據(jù)。
附錄
完整代碼下載地址
- Download Python source code: nn_tutorial.py
- Download Jupyter notebook: nn_tutorial.ipynb
總結(jié)
以上是生活随笔為你收集整理的深入理解 TORCH.NN的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2019复旦大学计算机分数线,2019复
- 下一篇: 2073:【例2.16 】三角形面积