使用torch實現RNN


  (本文對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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM