從網絡架構方面簡析循環神經網絡RNN

 1、前言

1.1 誕生緣由

  在普通的前饋神經網絡(如多層感知機MLP,卷積神經網絡CNN)中,每次的輸入都是獨立的,即網絡的輸出依賴且僅依賴於當前輸入,與過去一段時間內網絡的輸出無關。可是在現實生活中,許多系統的輸出不只依賴於當前輸入,還與過去一段時間內系統的輸出有關,即須要網絡保留必定的記憶功能,這就給前饋神經網絡提出了巨大的挑戰。除此以外,前饋神經網絡難以處理時序數據,好比視頻、語音等,由於時序數據的序列長度通常是不固定的,而前饋神經網絡要求輸入、輸出的維度都是固定的,不能任意改變。出於這兩方面的需求,循環神經網絡RNN應運而生。html

1.2 簡介

  循環神經網絡(Recurrent Neural Network,RNN)是一類具備短時間記憶能力的神經網絡。在循環神經網絡中,神經元既能夠如同前饋神經網絡中神經元那般從其餘神經元那裏接受信息,也能夠接收自身之前的信息。且和前饋神經網絡相比,循環神經網絡更加符合生物神經網絡的結構。git

1.3 與前饋神經網絡的差別

  就功能層面和學習性質而言,循環神經網絡也有異於前饋神經網絡。前饋神經網絡多用於迴歸(Regression)和分類(Classification),屬於監督學習(Supervised learning);而循環神經網絡多用於迴歸(Regression)和生成(Generation),屬於無監督學習(Unsupervised learning)。github

2、網絡架構

  上圖所示爲RNN的一個神經元模型。給定一個序列長度爲T的輸入序列,在t時刻將該序列中的第t個元素xt送進網絡,網絡會結合本次輸入xt及上一次的 「 記憶 」 ht-1,經過一個非線性激活函數f()(該函數一般爲tanh或relu),產生一箇中間值ht(學名爲隱狀態,Hidden States),該值即爲本次要保留的記憶。本次網絡的輸出爲ht的線形變換,即g(ht),其中g()爲簡單的線性函數。算法

  因爲循環神經網絡具備時序性,所以其網絡架構能夠從空間和時間兩方面進行了解。網絡

  爲方便理解,特以此情景舉例:假設目前正在訓練一個RNN,用於生成視頻。訓練用的train_data爲1000段等長度的視頻,劃分的batch_size爲10,即每次送給網絡10段視頻。假設每段視頻都有50幀,每幀的分辨率爲2X2。在此訓練過程當中,不須要外界給label,將某幀圖片input進RNN時,下一幀圖片即爲label。架構

2.1 空間角度

  在本例中,一個序列長度爲T的輸入序列即一段50幀視頻,其中的x1,x2,x3...x50分別對應着第一幀圖片、第二幀圖片、第三幀圖片......到第五十幀圖片。隨着時間的推移,依次將每幀圖片送進網絡,在每一個時刻,只送進一幀圖片,同時生成一幀圖片。該網絡架構以下圖所示。就如普通的前饋神經網絡通常,除了輸入輸出層的維度固定外,隱藏層的維度及層數都是能夠自主設計的。這裏假設只有一個隱藏層,該層有5個神經元。須要注意的是,每一個具備記憶功能的神經元中所存儲的隱狀態h,不只參與本神經元的下次輸入,還同時參與本層其餘具備記憶功能神經元的下次輸入。app

2.2 時間角度

  從空間角度觀察整個網絡,可將網絡視爲1個4X5X4的循環神經網絡RNN,其中隱藏層的5個神經元是具備記憶功能的;從時間角度展開整個網絡,可將網絡視爲50個4X5X4的多層感知機MLP,每一個MLP隱層神經元不只向該MLP的下一層輸出,同時還向下一個MLP中與之層數對應的全部神經元輸出(下圖爲求清晰表示,將其化簡爲一對一,但實質上是一對多),該輸出即爲隱狀態h。因爲隱狀態須要在MLP之間從前向後傳遞,所以這50個MLP只能依次運算,不能並行運算。函數

 

 

  循環神經網絡獨特的神經元結構賦予其記憶功能,但有得有失,該結構也形成其在處理時序數據時不能進行並行運算。目前推進算法進步的一大助力就是算力,而RNN卻沒法充分利用算力,這也是一部分人不太看好其前景的緣由。學習

3、pytorch demo 實現

  對一段正弦函數進行離散採樣做爲輸入,利用RNN生成滯後的正弦函數採樣值。實現環境:Colab。優化

  引入必要頭文件。

1 import torch.nn as nn
2 import torch
3 import numpy as np
4 import matplotlib.pyplot as plt
5 %matplotlib inline

   採樣獲取input及label,並可視化。

 1 # 制定畫布大小
 2 plt.figure(figsize=(8, 5))
 3 
 4 # 每一個batch中數據個數
 5 num = 20
 6 
 7 # 生成數據
 8 time_steps = np.linspace(0, np.pi, num+1)
 9 data = np.sin(time_steps)
10 data = data.reshape((num+1, 1))
11 
12 x = data[0:num, :] # 除了最後一個數據外的全部其餘數據
13 y = data[1:num+1, :] # 除了第一個數據外的全部其餘數據
14 
15 # 可視化數據
16 plt.plot(time_steps[1:num+1], x, 'r.', label='input_x')
17 plt.plot(time_steps[1:num+1], y, 'b.', label='output_y')
18 
19 plt.legend(loc='best')
20 plt.show()

 

  經過torch.nn.RNN及torch.nn.fc自定義網絡模型。

 1 # 自定義網絡
 2 class myRNN(nn.Module):
 3     def __init__(self, input_size, output_size, hidden_dim, n_layers):
 4         super(myRNN, self).__init__()
 5         self.hidden_dim = hidden_dim # 隱藏層節點個數
 6         self.rnn = nn.RNN(input_size, hidden_dim, n_layers, batch_first=True) # rnn層
 7         self.fc = nn.Linear(hidden_dim, output_size) # 全鏈接層
 8         
 9     def forward(self, x, hidden):
10         batch_size = x.shape[0]
11         # 生成預測值和隱狀態,預測值傳向下一層,隱狀態做爲記憶參與下一次輸入
12         r_out, hidden = self.rnn(x, hidden)       
13         r_out = r_out.view(-1, self.hidden_dim)       
14         output = self.fc(r_out)
15         
16         return output, hidden

   實例化模型,並指定超參數、損失函數和優化器。

 1 # 指定超參數
 2 input_size = 1
 3 output_size = 1
 4 hidden_dim = 32
 5 n_layers = 1
 6 
 7 # 實例化模型
 8 rnn = myRNN(input_size, output_size, hidden_dim, n_layers)
 9 
10 # 指定損失函數和優化器,學習率設定爲0.01
11 loss = nn.MSELoss()
12 optimizer = torch.optim.Adam(rnn.parameters(), lr=0.01)

   定義訓練並打印輸出的函數。 

 1 def train(rnn, n_steps, print_every):
 2     
 3     # 記憶初始化
 4     hidden = None
 5     loss_list = []
 6     for batch_i,step in enumerate(range(n_steps)):
 7         optimizer.zero_grad() # 梯度清零
 8         # 生成訓練數據
 9         time_steps = np.linspace(step*np.pi, (step+1)*np.pi, num+1)
10         data = np.sin(time_steps)
11         data = data.reshape((num+1, 1))
12 
13         x = data[0:num, :] # 除了最後一個數據外的全部其餘數據
14         y = data[1:num+1, :] # 除了第一個數據外的全部其餘數據
15         
16         x_tensor = torch.from_numpy(x).unsqueeze(0).type('torch.FloatTensor')
17         y_tensor = torch.from_numpy(y).type('torch.FloatTensor')
18         
19         prediction, hidden = rnn(x_tensor, hidden) # 生成預測值和隱狀態       
20         hidden = hidden.data        
21         loss_rate = loss(prediction, y_tensor) # 計算損失
22         loss_rate.backward() # 偏差反向傳播        
23         optimizer.step() # 梯度更新
24         loss_list.append(loss_rate)
25 
26         
27         if batch_i%print_every == 0:
28             plt.plot(time_steps[1:num+1], x, 'r.', label='input')
29             plt.plot(time_steps[1:num+1], prediction.data.numpy().flatten(), 'b.', label='predicte')
30             plt.show()  
31     
32     x = np.linspace(0, n_steps, n_steps)
33     plt.plot(x, loss_list, color='blue', linewidth=1.0, linestyle='-', label='loss')
34     plt.legend(loc='upper right')
35     plt.show()
36         
37     return rnn

   訓練模型並打印輸出。

1 n_steps = 100
2 print_every = 25
3 trained_rnn = train(rnn, n_steps, print_every)

  從左到右、自上而下,分別是訓練2五、50、75和100次後的模型效果。

 

參考資料:

  邱錫鵬老師《神經網絡與深度學習》: https://nndl.github.io/

  優達學城pytorch學習課程: https://github.com/udacity/deep-learning-v2-pytorch

  慕課手記——RNN架構詳解:  http://www.imooc.com/article/details/id/31105

  pytorch官方手冊: https://pytorch.org/docs/stable/nn.html#recurrent-layers

相關文章
相關標籤/搜索