基於長短期記憶網路的短時雷達外推演算法
本文由機器之心經授權轉載自 TeXtXc9snP05LqrjLHzJvg" rel="nofollow,noindex" target="_blank">墨跡天氣TechInfo(ID:moji_techinfo) ,未經授權禁止二次轉載。
本文主要介紹的是利用現有的pytorch框架,實現ConvLSTM和ConvGRU核心,並實現一個多層RNN的封裝結構層,方便使用者快速的堆疊多層的RNNCell。得益於pytorch的便利,我們只需要按照公式寫出forward的過程,後續的backward將由框架本身給我們完成。同時,作者還基於這些網路結構,搭建了一個簡單的影象時序預測模型,方便讀者理解每一結構之間的作用和聯絡。
首先是ConvLSTM,其單元結構如下圖所示:
在公式中可以明顯看出i,f,g,o,中的計算過程大多一致,因此我們利用一個卷積層,多個卷積核的方式來完成計算。對於每個門內部的計算我們也進行合併。如下圖所示:
拼接輸入資料X,h:
combined =torch.cat((input, hidden), 1)
計算每一個門的輸出:
A = self.conv(combined) (ai, af,ao, ag)= torch.split(A, self.num_features, dim=1)# it should return 4 tensors
這樣,我們就完成了LSTM中所有門的計算,在利用pytorch的支援下,我們只使用三行程式碼就完成了基礎的門運算操作。
然後我們參照原始公式,給每個門的輸出資料加上啟用函式和dropout層:
i =torch.sigmoid(ai) i = self.dropout(i) f = torch.sigmoid(af) f = self.dropout(f) o = torch.sigmoid(ao) o = self.dropout(o) g = torch.tanh(ag) g = self.dropout(g)
得到f,g,i,o之後可以繼續計算Ct,Ht:
next_c = f * c+ i * g next_h = o * torch.tanh(next_c) next_h = self.dropout(next_h)
得到Ct,Ht之後整個LSTM單元的計算過程變結束了,LSTM的主要創新之處在於儲存單元Ct,Ct在整個神經元中充當了狀態資訊的累加器,神經元通過各種引數化學習而來的控制門對資訊進行儲存和削減。
為了配合後面的多層RNN封裝結構,我們將神經元的輸出封裝為如下格式:
return next_h, (next_h,next_c)
至此,一個完整的 LSTM 單元 計算過程就實現完成了。
GRU單元的實現過程和LSTM類似,這裡只給出計算公式和對應的程式碼實現:
def forward(self, input, hidden): c1 = self.ConvGates(torch.cat((input, hidden), 1)) (rt, ut)= c1.chunk(2, 1) reset_gate = self.dropout(f.sigmoid(rt)) update_gate = self.dropout(f.sigmoid(ut)) gated_hidden = torch.mul(reset_gate, hidden) p1 = self.Conv_ct(torch.cat((input, gated_hidden), 1)) ct = f.tanh(p1) next_h = torch.mul(update_gate, hidden)+ (1 - update_gate) * ct return next_h
ConvGRUCell的具體定義過程可以查考文末的GitHub原始碼地址。
接下來我們繼續承接上面的輸入,實現一個 多層 RNN 的封裝結構層 。
其單元示意圖如下所示:
MultiRNNCell層中封裝了兩個RNN單元Cell1和Cell2,資料x1首先傳送給Cell1,Cell1有其初始化的狀態資訊h1(本文的LSTM結構狀態資訊有兩個,分別為h&c,下文中不再贅述),經過Cell1神經元的處理,得到新的狀態資訊h1,並傳給下一個單元Cell2,經過處理得到新的h2。兩個cell的狀態資訊將會被保留,在序列中的下一個時次資料x2到來之後,繼續參與計算。
forward的處理過程如下:
def forward(self, input, hidden_state): cur_inp = input new_states = []
new_states用來儲存每個cell計算得來的新狀態資訊,hidden_state儲存的是上一時次的狀態資訊,t為0時,hidden_state為初始化的狀態資訊。
for i,cell in enumerate(self._cells): cur_state = hidden_state[i] cur_inp, new_state= cell(cur_inp, cur_state) new_states.append(new_state)
MultiRNNCell物件的self._cells中儲存了多層封裝結構中的所有Cell,具體初始化方式和類成員可以在文末的github原始碼地址中找到。計算過程中我們取每個cell對應的狀態資訊h,和輸入資料x,當前Cell計算後,下一個Cell的輸入x使用上一個單元的輸出h。
在原始碼中我提供了一個簡單的影象時序預測模型,在這裡簡單講解一下它的資料流:
def forward(self, data): new_state = self.stacked_lstm.init_hidden(data.size()[1]) data的shape為(num_seqs,batch_size,channels_img,size_H,size_W)
new_state為初始化的狀態資訊
self.stacked_lstm是多層RNN封裝單元,其內部提供了一個初始化引數的方法。
x_unwrap = [] for i inxrange(self.input_num_seqs + self.output_num_seqs): # print i if i< self.input_num_seqs: y_1, new_state= self.stacked_lstm(data[i], new_state) else: y_1, new_state= self.stacked_lstm(x_1, new_state) # print y_1.size() x_1 = self.deconv1(y_1) # print x_1.size() if i>= self.input_num_seqs: x_unwrap.append(x_1) return x_unwrap
input_num_seqs是模型的序列輸入長度,output_num_seqs是模型的序列輸出長度,在輸入的過程中每個多層RNN結構體的輸入是我們的data,在輸出過程中每個多層RNN結構體的輸入是我們前一個時次的輸出。
在後續的loss計算過程中,我們計算的是實際的輸出序列和我們預測出的x_unwarp序列之間的誤差。其資料流如下圖所示:
至此,整個基於pytorch實現的ConvLSTM和ConvGRU核心,多層封裝結構層,時序預測網路已經完成了,受限於作者本身的水平,如有不妥之處可以在下方留言提出。
本文中涉及的網路結構程式碼地址為:https://github.com/chencodeX/RNN_Pytorch
在原始碼中還有一些基於上述結構實現的編碼預測網路(encoder-forecaster),加權均方誤差等,在本文中不再贅述,也歡迎大家fork。
本文作者簡介:
陳子豪
墨跡風雲科技股份有限公司演算法工程師,專注於氣象與深度學習領域融合應用。
本文由機器之心經授權轉載自 墨跡天氣TechInfo(ID:moji_techinfo) 。