(本文對https://blog.csdn.net/out_of_memory_error/article/details/81456501的結果進行了復現。)
在實驗室的項目遇到了困難,弄不明白LSTM的原理。到網上搜索,發現LSTM是RNN的變種,那就從RNN開始學吧。
帶隱藏狀態的RNN可以用下面兩個公式來表示:
可以看出,一個RNN的參數有W_xh,W_hh,b_h,W_hq,b_q和H(t)。其中H(t)是步數的函數。
參考的文章考慮了這樣一個問題,對於x軸上的一列點,有一列sin值,我們想知道它對應的cos值,但是即使sin值相同,cos值也不同,因為輸出結果不僅依賴於當前的輸入值sinx,還依賴於之前的sin值。這時候可以用RNN來解決問題
用到的核心函數:torch.nn.RNN() 參數如下:
-
input_size – 輸入
x
的特征數量。 -
hidden_size – 隱藏層的特征數量。
-
num_layers – RNN的層數。
-
nonlinearity – 指定非線性函數使用
tanh
還是relu
。默認是tanh
。 -
bias – 如果是
False
,那么RNN層就不會使用偏置權重 $b_ih$和$b_hh$,默認是True
-
batch_first – 如果
True
的話,那么輸入Tensor
的shape應該是[batch_size, time_step, feature],輸出也是這樣。 -
dropout – 如果值非零,那么除了最后一層外,其它層的輸出都會套上一個
dropout
層。 -
bidirectional – 如果
True
,將會變成一個雙向RNN
,默認為False
。
下面是代碼:
1 # encoding:utf-8 2 import torch 3 import numpy as np 4 import matplotlib.pyplot as plt # 導入作圖相關的包 5 from torch import nn 6 7 8 # 定義RNN模型 9 class Rnn(nn.Module): 10 def __init__(self, INPUT_SIZE): 11 super(Rnn, self).__init__() 12 13 # 定義RNN網絡,輸入單個數字.隱藏層size為[feature, hidden_size] 14 self.rnn = nn.RNN( 15 input_size=INPUT_SIZE, 16 hidden_size=32, 17 num_layers=1, 18 batch_first=True # 注意這里用了batch_first=True 所以輸入形狀為[batch_size, time_step, feature] 19 ) 20 # 定義一個全連接層,本質上是令RNN網絡得以輸出 21 self.out = nn.Linear(32, 1) 22 23 # 定義前向傳播函數 24 def forward(self, x, h_state): 25 # 給定一個序列x,每個x.size=[batch_size, feature].同時給定一個h_state初始狀態,RNN網絡輸出結果並同時給出隱藏層輸出 26 r_out, h_state = self.rnn(x, h_state) 27 outs = [] 28 for time in range(r_out.size(1)): # r_out.size=[1,10,32]即將一個長度為10的序列的每個元素都映射到隱藏層上. 29 outs.append(self.out(r_out[:, time, :])) # 依次抽取序列中每個單詞,將之通過全連接層並輸出.r_out[:, 0, :].size()=[1,32] -> [1,1] 30 return torch.stack(outs, dim=1), h_state # stack函數在dim=1上疊加:10*[1,1] -> [1,10,1] 同時h_state已經被更新 31 32 33 TIME_STEP = 10 34 INPUT_SIZE = 1 35 LR = 0.02 36 37 model = Rnn(INPUT_SIZE) 38 print(model) 39 40 loss_func = nn.MSELoss() # 使用均方誤差函數 41 optimizer = torch.optim.Adam(model.parameters(), lr=LR) # 使用Adam算法來優化Rnn的參數,包括一個nn.RNN層和nn.Linear層 42 43 h_state = None # 初始化h_state為None 44 45 for step in range(300): 46 # 人工生成輸入和輸出,輸入x.size=[1,10,1],輸出y.size=[1,10,1] 47 start, end = step * np.pi, (step + 1)*np.pi 48 49 steps = np.linspace(start, end, TIME_STEP, dtype=np.float32) 50 x_np = np.sin(steps) 51 y_np = np.cos(steps) 52 53 x = torch.from_numpy(x_np[np.newaxis, :, np.newaxis]) 54 y = torch.from_numpy(y_np[np.newaxis, :, np.newaxis]) 55 56 # 將x通過網絡,長度為10的序列通過網絡得到最終隱藏層狀態h_state和長度為10的輸出prediction:[1,10,1] 57 prediction, h_state = model(x, h_state) 58 h_state = h_state.data # 這一步只取了h_state.data.因為h_state包含.data和.grad 舍棄了梯度 59 # 反向傳播 60 loss = loss_func(prediction, y) 61 optimizer.zero_grad() 62 loss.backward() 63 64 # 優化網絡參數具體應指W_xh, W_hh, b_h.以及W_hq, b_q 65 optimizer.step() 66 67 # 對最后一次的結果作圖查看網絡的預測效果 68 plt.plot(steps, y_np.flatten(), 'r-') 69 plt.plot(steps, prediction.data.numpy().flatten(), 'b-') 70 plt.show()
最后一步預測和實際y的結果作圖如下:
可看出,訓練RNN網絡之后,對網絡輸入一個序列sinx,能正確輸出對應的序列cosx