PyTorch 笔记(15)— 分别使用 tensor、autograd、torch.nn 搭建简易神经网络
1. 使用 tensor 搭建神經網絡
搭建神經網絡模型的具體代碼如下,這里會將完整的代碼分成幾部分進行詳細介紹,以便于理解。
import torch as tbatch_n = 100
input_data = 10000
hidden_layer = 100
output_data = 10
batch_n是在一個批次中輸入數據的數量,值是 100,這意味著我們在一個批次中輸入 100個數據;input_data表示batch_n包含的數據特征個數,該值是 1000,所以每個數據的數據特征就是 1000 個;hidden_layer用于定義經過隱藏層后保留的數據特征的個數,這里有 100 個,因為我們的模型只考慮一層隱藏層,所以在代碼中僅定義了一個隱藏層的參數;output_data是輸出的數據,值是 10,我們可以將輸出的數據看作一個分類結果值的數量,個數 10 表示我們最后要得到 10 個分類結果值;
一個批次的數據從輸入到輸出的完整過程是:先輸入100 個具有1000 個特征的數據,經過隱藏層后變成 100 個具有 100 個特征的數據,再經過輸出層后輸出 100 個具有 10 個分類結果值的數據,在得到輸出結果之后計算損失并進行后向傳播,這樣一次模型的訓練就完成了,然后循環這個流程就可以完成指定次數的訓練,并達到優化模型參數的目的。
下面看看如何完成從輸入層到隱藏層、從隱藏層到輸出層的權重初始化定義工作,代碼如下:
x = t.randn(batch_n, input_data)
y = t.randn(batch_n, output_data)w1 = t.randn(input_data, hidden_layer)
w2 = t.randn(hidden_layer, output_data)
在以上代碼中定義的從輸入層到隱藏層、從隱藏層到輸出層對應的權重參數,同在之前說到的過程中使用的參數維度是一致的。
由于我們現在并沒有好的權重參數的初始化方法,所以選擇通過torch.randn 來生成指定維度的隨機參數作為其初始化參數,盡管這并不是一個好主意。可以看到,在代碼中定義的輸入層維度為(100,1000),輸出層維度為(100,10),同時,從輸入層到隱藏層的權重參數維度為(1000,100),從隱藏層到輸出層的權重參數維度為(100,10),這里我們可能會好奇權重參數的維度是怎么定義下來的,其實,只要我們把整個過程看作矩陣連續的乘法運算,就自然能夠很快明白了。
在代碼中我們的真實值 y 也是通過隨機的方式生成的,所以一開始在使用損失函數計算損失值時得到的結果會較大。
在定義好輸入、輸出和權重參數之后,就可以開始訓練模型和優化權重參數了,在此之前,我們還需要明確訓練的總次數和學習速率,代碼如下:
epoch_n = 20
learning_rate = 1e-6
因為接下來會使用梯度下降的方法來優化神經網絡的參數,所以必須定義后向傳播的次數和梯度下降使用的學習速率。
在以上代碼中使用了 epoch_n 定義訓練的次數,epoch_n 的值為 20,所以我們需要通過循環的方式讓程序進行 20 次訓練,來完成對初始化權重參數的優化和調整。在優化的過程中使用的學習速率 learning_rate 的值為1e-6,即 0.000001。接下來對模型進行正式訓練并對參數進行優化,代碼如下:
for i in range(epoch_n):h1 = x.mm(w1)h1 = h1.clamp(min=0)y_pred = h1.mm(w2)loss = (y_pred - y).pow(2).sum()print("i is {}, loss is {}".format(i, loss))grad_y_pred = 2 * (y_pred - y)grad_w2 = h1.t().mm(grad_y_pred)grad_h = grad_y_pred.clone()grad_h = grad_h.mm(w2.t())grad_h.clamp_(min=0)grad_w1 = x.t().mm(grad_h)w1 -= learning_rate * grad_w1w2 -= learning_rate * grad_w2
以上代碼通過最外層的一個大循環來保證我們的模型可以進行 20 次訓練,循環內的是神經網絡模型具體的前向傳播和后向傳播代碼,參數的優化和更新使用梯度下降來完成。
在這個神經網絡的前向傳播中,通過兩個連續的矩陣乘法計算出預測結果,在計算的過程中還對矩陣乘積的結果使用 clamp 方法進行裁剪,將小于零的值全部重新賦值為 0,這就像加上了一個 ReLU 激活函數的功能。
前向傳播得到的預測結果通過 y_pred 來表示,在得到了預測值后就可以使用預測值和真實值來計算誤差值了。
我們用 loss 來表示誤差值,對誤差值的計算使用了均方誤差函數。之后的代碼部分就是通過實現后向傳播來對權重參數進行優化了,為了計算方便,我們的代碼實現使用的是每個節點的鏈式求導結果,在通過計算之后,就能夠得到每個權重參數對應的梯度分別是 grad_w1 和 grad_w2 。
在得到參數的梯度值之后,按照之前定義好的學習速率對 w1 和 w2 的權重參數進行更新,在代碼中每次訓練時,我們都會對 loss 的值進行打印輸出,以方便看到整個優化過程的效果,所以最后會有 20 個 loss 值被打印顯示,打印輸出的結果如下:
i is 0, loss is 506614656.0
i is 1, loss is 76480659456.0
i is 2, loss is 976.175537109375
i is 3, loss is 976.175537109375
i is 4, loss is 976.175537109375
i is 5, loss is 976.175537109375
i is 6, loss is 976.175537109375
i is 7, loss is 976.175537109375
i is 8, loss is 976.175537109375
i is 9, loss is 976.175537109375
i is 10, loss is 976.175537109375
i is 11, loss is 976.175537109375
i is 12, loss is 976.175537109375
i is 13, loss is 976.175537109375
i is 14, loss is 976.175537109375
i is 15, loss is 976.175537109375
i is 16, loss is 976.175537109375
i is 17, loss is 976.175537109375
i is 18, loss is 976.175537109375
i is 19, loss is 976.175537109375
可以看到,loss 值從之前的巨大誤差逐漸縮減,這說明我們的模型經過 20次訓練和權重參數優化之后,得到的預測的值和真實值之間的差距越來越小了。
2. 使用 autograd 搭建神經網絡
torch.autograd 包的主要功能是完成神經網絡后向傳播中的鏈式求導,手動實現鏈式求導的代碼會給我們帶來很大的困擾,而 torch.autograd 包中豐富的類減少了這些不必要的麻煩。
實現自動梯度功能的過程大致為:先通過輸入的 Tensor 數據類型的變量在神經網絡的前向傳播過程中生成一張計算圖,然后根據這個計算圖和輸出結果準確計算出每個參數需要更新的梯度,并通過完成后向傳播完成對參數的梯度更新。
import torch as tbatch_n = 100
input_data = 10000
hidden_layer = 100
output_data = 10x = t.randn(batch_n, input_data, requires_grad=False)
y = t.randn(batch_n, output_data, requires_grad=False)w1 = t.randn(input_data, hidden_layer, requires_grad=True)
w2 = t.randn(hidden_layer, output_data, requires_grad=True)epoch_n = 20
learning_rate = 1e-6for i in range(epoch_n):h1 = x.mm(w1)h1 = h1.clamp(min=0)y_pred = h1.mm(w2)loss = (y_pred - y).pow(2).sum()print("i is {}, loss is {}".format(i, loss.data))loss.backward()w1.data -= learning_rate * w1.grad.dataw2.data -= learning_rate * w2.grad.dataw1.grad.data.zero_()w2.grad.data.zero_()
代碼中使用了 requires_grad 參數,這個參數的賦值類型是布爾型,如果 requires_grad 的值是 False ,那么表示該變量在進行自動梯度計算的過程中不會保留梯度值。
我們將輸入的數據 x 和輸出的數據 y 的requires_grad 參數均設置為 False ,這是因為這兩個變量并不是我們的模型需要優化的參數,而兩個權重 w1 和 w2 的 requires_grad 參數的值為 True 。
和之前的代碼相比,當前的代碼更簡潔了,之前代碼中的后向傳播計算部分變成了新代碼中的 loss.backward(),這個函數的功能在于讓模型根據計算圖自動計算每個節點的梯度值并根據需求進行保留,有了這一步,我們的權重參數 w1.data 和 w2.data 就可以直接使用在自動梯度過程中求得的梯度值w1.data.grad 和 w2.data.grad ,并結合學習速率來對現有的參數進行更新、優化了。
在代碼的最后還要將本次計算得到的各個參數節點的梯度值通過 grad.data.zero_() 全部置零,如果不置零,則計算的梯度值會被一直累加,這樣就會影響到后續的計算。
3. 使用 torch.nn 搭建神經網絡
其實除了可以采用自動梯度方法,我們還可以通過構建一個繼承了 torch.nn.Module 的新類,來完成對前向傳播函數和后向傳播函數的重寫。
在這個新類中,我們使用 forward 作為前向傳播函數的關鍵字,使用 backward 作為后向傳播函數的關鍵字。
下面看看新的代碼部分是如何定義我們的前向傳播 forward 函數和后向傳播 backward 函數的:
class Model(nn.Module):def __init__(self):super(Model, self).__init__()def forward(self, input, w1, w2):x = t.mm(input, w1)x = t.clamp(x, min=0)x = t.mm(x, w2)return xdef backward(self):pass
以上代碼展示了一個比較常用的 Python 類的構造方式:
- 首先通過
class Model(torch.nn.Module)完成了類繼承的操作,之后分別是類的初始化,以及forward函數和backward函數。 forward函數實現了模型的前向傳播中的矩陣運算,backward實現了模型的后向傳播中的自動梯度計算,后向傳播如果沒有特別的需求,則在一般情況下不用進行調整。
在定義好類之后,我們就可以對其進行調用了,代碼如下:
model = Model()
整體代碼如下:
import torch as t
from torch import nnbatch_n = 100
input_data = 10000
hidden_layer = 100
output_data = 10x = t.randn(batch_n, input_data, requires_grad=False)
y = t.randn(batch_n, output_data, requires_grad=False)w1 = t.randn(input_data, hidden_layer, requires_grad=True)
w2 = t.randn(hidden_layer, output_data, requires_grad=True)epoch_n = 20
learning_rate = 1e-6class Model(nn.Module):def __init__(self):super(Model, self).__init__()def forward(self, input, w1, w2):x = t.mm(input, w1)x = t.clamp(x, min=0)x = t.mm(x, w2)return xdef backward(self):passmodel = Model()for i in range(epoch_n):y_pred = model(x, w1, w2)loss = (y_pred - y).pow(2).sum()print("i is {}, loss is {}".format(i, loss.data))loss.backward()w1.data -=(learning_rate * w1.grad.data)w2.data -=(learning_rate * w2.grad.data)w1.grad.data.zero_()w2.grad.data.zero_()
總結
以上是生活随笔為你收集整理的PyTorch 笔记(15)— 分别使用 tensor、autograd、torch.nn 搭建简易神经网络的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 乌镇过年好玩吗
- 下一篇: 52度景阳春多钱一箱