本文約3200字,建議閱讀5分鐘。本文介紹了LSTM模型結(jié)構(gòu)的可視化。
最近在學(xué)習(xí)LSTM應(yīng)用在時(shí)間序列的預(yù)測(cè)上,但是遇到一個(gè)很大的問題就是LSTM在傳統(tǒng)BP網(wǎng)絡(luò)上加上時(shí)間步后,其結(jié)構(gòu)就很難理解了,同時(shí)其輸入輸出數(shù)據(jù)格式也很難理解,網(wǎng)絡(luò)上有很多介紹LSTM結(jié)構(gòu)的文章,但是都不直觀,對(duì)初學(xué)者是非常不友好的。我也是苦苦冥思很久,看了很多資料和網(wǎng)友分享的LSTM結(jié)構(gòu)圖形才明白其中的玄機(jī)。
1、傳統(tǒng)的BP網(wǎng)絡(luò)和CNN網(wǎng)絡(luò)
2、LSTM網(wǎng)絡(luò)
3、LSTM的輸入結(jié)構(gòu)
4、pytorch中的LSTM
4.1 pytorch中定義的LSTM模型
4.2 喂給LSTM的數(shù)據(jù)格式
4.3 LSTM的output格式
5、LSTM和其他網(wǎng)絡(luò)組合
傳統(tǒng)的BP網(wǎng)絡(luò)和CNN網(wǎng)絡(luò)
BP網(wǎng)絡(luò)和CNN網(wǎng)絡(luò)沒有時(shí)間維,和傳統(tǒng)的機(jī)器學(xué)習(xí)算法理解起來相差無幾,CNN在處理彩色圖像的3通道時(shí),也可以理解為疊加多層,圖形的三維矩陣當(dāng)做空間的切片即可理解,寫代碼的時(shí)候照著圖形一層層疊加即可。如下圖是一個(gè)普通的BP網(wǎng)絡(luò)和CNN網(wǎng)絡(luò)。
圖中的隱含層、卷積層、池化層、全連接層等,都是實(shí)際存在的,一層層前后疊加,在空間上很好理解,因此在寫代碼的時(shí)候,基本就是看圖寫代碼,比如用keras就是:
# 示例代碼,沒有實(shí)際意義model = Sequential()model.add(Conv2D(32, (3, 3), activation='relu')) # 添加卷積層model.add(MaxPooling2D(pool_size=(2, 2))) # 添加池化層model.add(Dropout(0.25)) # 添加dropout層model.add(Conv2D(32, (3, 3), activation='relu')) # 添加卷積層model.add(MaxPooling2D(pool_size=(2, 2))) # 添加池化層model.add(Dropout(0.25)) # 添加dropout層.... # 添加其他卷積操作model.add(Flatten()) # 拉平三維數(shù)組為2維數(shù)組model.add(Dense(256, activation='relu')) 添加普通的全連接層model.add(Dropout(0.5))model.add(Dense(10, activation='softmax')).... # 訓(xùn)練網(wǎng)絡(luò)
當(dāng)我們?cè)诰W(wǎng)絡(luò)上搜索看LSTM結(jié)構(gòu)的時(shí)候,看最多的是下面這張圖:
這是RNN循環(huán)神經(jīng)網(wǎng)絡(luò)經(jīng)典的結(jié)構(gòu)圖,LSTM只是對(duì)隱含層節(jié)點(diǎn)A做了改進(jìn),整體結(jié)構(gòu)不變,因此本文討論的也是這個(gè)結(jié)構(gòu)的可視化問題。
中間的A節(jié)點(diǎn)隱含層,左邊是表示只有一層隱含層的LSTM網(wǎng)絡(luò),所謂LSTM循環(huán)神經(jīng)網(wǎng)絡(luò)就是在時(shí)間軸上的循環(huán)利用,在時(shí)間軸上展開后得到右圖。
看左圖,很多同學(xué)以為L(zhǎng)STM是單輸入、單輸出,只有一個(gè)隱含神經(jīng)元的網(wǎng)絡(luò)結(jié)構(gòu),看右圖,以為L(zhǎng)STM是多輸入、多輸出,有多個(gè)隱含神經(jīng)元的網(wǎng)絡(luò)結(jié)構(gòu),A的數(shù)量就是隱含層節(jié)點(diǎn)數(shù)量。
WTH?思維轉(zhuǎn)不過來啊。這就是傳統(tǒng)網(wǎng)絡(luò)和空間結(jié)構(gòu)的思維。
實(shí)際上,右圖中,我們看Xt表示序列,下標(biāo)t是時(shí)間軸,所以,A的數(shù)量表示的是時(shí)間軸的長(zhǎng)度,是同一個(gè)神經(jīng)元在不同時(shí)刻的狀態(tài)(Ht),不是隱含層神經(jīng)元個(gè)數(shù)。
我們知道,LSTM網(wǎng)絡(luò)在訓(xùn)練時(shí)會(huì)使用上一時(shí)刻的信息,加上本次時(shí)刻的輸入信息來共同訓(xùn)練。
舉個(gè)簡(jiǎn)單的例子:在第一天我生病了(初始狀態(tài)H0),然后吃藥(利用輸入信息X1訓(xùn)練網(wǎng)絡(luò)),第二天好轉(zhuǎn)但是沒有完全好(H1),再吃藥(X2),病情得到好轉(zhuǎn)(H2),如此循環(huán)往復(fù)知道病情好轉(zhuǎn)。因此,輸入Xt是吃藥,時(shí)間軸T是吃多天的藥,隱含層狀態(tài)是病情狀況。因此我還是我,只是不同狀態(tài)的我。
實(shí)際上,LSTM的網(wǎng)絡(luò)是這樣的:
LSTM網(wǎng)絡(luò)結(jié)構(gòu)
上面的圖表示包含2個(gè)隱含層的LSTM網(wǎng)絡(luò),在T=1時(shí)刻看,它是一個(gè)普通的BP網(wǎng)絡(luò),在T=2時(shí)刻看也是一個(gè)普通的BP網(wǎng)絡(luò),只是沿時(shí)間軸展開后,T=1訓(xùn)練的隱含層信息H,C會(huì)被傳遞到下一個(gè)時(shí)刻T=2,如下圖所示。上圖中向右的五個(gè)常常的箭頭,所的也是隱含層狀態(tài)在時(shí)間軸上的傳遞。
注意,圖中H表示隱藏層狀態(tài),C是遺忘門,后面會(huì)講解它們的維度。
為了更好理解LSTM結(jié)構(gòu),還必須理解LSTM的數(shù)據(jù)輸入情況。仿照3通道圖像的樣子,在加上時(shí)間軸后的多樣本的多特征的不同時(shí)刻的數(shù)據(jù)立方體如下圖所示:
右邊的圖是我們常見模型的輸入,比如XGBOOST,lightGBM,決策樹等模型,輸入的數(shù)據(jù)格式都是這種(N*F)的矩陣,而左邊是加上時(shí)間軸后的數(shù)據(jù)立方體,也就是時(shí)間軸上的切片,它的維度是(N*T*F),第一維度是樣本數(shù),第二維度是時(shí)間,第三維度是特征數(shù),如下圖所示:
這樣的數(shù)據(jù)立方體很多,比如天氣預(yù)報(bào)數(shù)據(jù),把樣本理解成城市,時(shí)間軸是日期,特征是天氣相關(guān)的降雨風(fēng)速PM2.5等,這個(gè)數(shù)據(jù)立方體就很好理解了。在NLP里面,一句話會(huì)被embedding成一個(gè)矩陣,詞與詞的順序是時(shí)間軸T,索引多個(gè)句子的embedding三維矩陣如下圖所示:
pytorch中定義的LSTM模型的參數(shù)如下
class torch.nn.LSTM(*args, **kwargs)參數(shù)有:input_size:x的特征維度 hidden_size:隱藏層的特征維度 num_layers:lstm隱層的層數(shù),默認(rèn)為1 bias:False則bihbih=0和bhhbhh=0. 默認(rèn)為True batch_first:True則輸入輸出的數(shù)據(jù)格式為 (batch, seq, feature) dropout:除最后一層,每一層的輸出都進(jìn)行dropout,默認(rèn)為: 0 bidirectional:True則為雙向lstm默認(rèn)為False
結(jié)合前面的圖形,我們一個(gè)個(gè)看。
(1)input_size:x的特征維度,就是數(shù)據(jù)立方體中的F,在NLP中就是一個(gè)詞被embedding后的向量長(zhǎng)度,如下圖所示:
(2)hidden_size:隱藏層的特征維度(隱藏層神經(jīng)元個(gè)數(shù)),如下圖所示,我們有兩個(gè)隱含層,每個(gè)隱藏層的特征維度都是5。注意,非雙向LSTM的輸出維度等于隱藏層的特征維度。
(3)num_layers:lstm隱層的層數(shù),上面的圖我們定義了2個(gè)隱藏層。
(4)batch_first:用于定義輸入輸出維度,后面再講。
(5)bidirectional:是否是雙向循環(huán)神經(jīng)網(wǎng)絡(luò),如下圖是一個(gè)雙向循環(huán)神經(jīng)網(wǎng)絡(luò),因此在使用雙向LSTM的時(shí)候我需要特別注意,正向傳播的時(shí)候有(Ht, Ct),反向傳播也有(Ht', Ct'),前面我們說了非雙向LSTM的輸出維度等于隱藏層的特征維度,而雙向LSTM的輸出維度是隱含層特征數(shù)*2,而且H,C的維度是時(shí)間軸長(zhǎng)度*2。
pytorch中LSTM的輸入數(shù)據(jù)格式默認(rèn)如下:
input(seq_len, batch, input_size)參數(shù)有:seq_len:序列長(zhǎng)度,在NLP中就是句子長(zhǎng)度,一般都會(huì)用pad_sequence補(bǔ)齊長(zhǎng)度 batch:每次喂給網(wǎng)絡(luò)的數(shù)據(jù)條數(shù),在NLP中就是一次喂給網(wǎng)絡(luò)多少個(gè)句子 input_size:特征維度,和前面定義網(wǎng)絡(luò)結(jié)構(gòu)的input_size一致。
前面也說到,如果LSTM的參數(shù) batch_first=True,則要求輸入的格式是:
input(batch, seq_len, input_size)
剛好調(diào)換前面兩個(gè)參數(shù)的位置。其實(shí)這是比較好理解的數(shù)據(jù)形式,下面以NLP中的embedding向量說明如何構(gòu)造LSTM的輸入。
如果把batch放在第一位,則三維矩陣的形式如下:
看懂了嗎,這就是輸入數(shù)據(jù)的格式,是不是很簡(jiǎn)單。
LSTM的另外兩個(gè)輸入是 h0 和 c0,可以理解成網(wǎng)絡(luò)的初始化參數(shù),用隨機(jī)數(shù)生成即可。
h0(num_layers * num_directions, batch, hidden_size)c0(num_layers * num_directions, batch, hidden_size)參數(shù):num_layers:隱藏層數(shù) num_directions:如果是單向循環(huán)網(wǎng)絡(luò),則num_directions=1,雙向則num_directions=2 batch:輸入數(shù)據(jù)的batch hidden_size:隱藏層神經(jīng)元個(gè)數(shù)
input(batch, seq_len, input_size)
h0(batc,num_layers * num_directions, h, hidden_size)c0(batc,num_layers * num_directions, h, hidden_size)
output,(ht, ct) = net(input) output: 最后一個(gè)狀態(tài)的隱藏層的神經(jīng)元輸出 ht:最后一個(gè)狀態(tài)的隱含層的狀態(tài)值 ct:最后一個(gè)狀態(tài)的隱含層的遺忘門值
output(seq_len, batch, hidden_size * num_directions)ht(num_layers * num_directions, batch, hidden_size)ct(num_layers * num_directions, batch, hidden_size)
和input的情況類似,如果我們前面定義的input格式是:
input(batch, seq_len, input_size)
ht(batc,num_layers * num_directions, h, hidden_size)ct(batc,num_layers * num_directions, h, hidden_size)
說了這么多,我們回過頭來看看ht和ct在哪里,請(qǐng)看下圖:
還記得嗎,output的維度等于隱藏層神經(jīng)元的個(gè)數(shù),即hidden_size,在一些時(shí)間序列的預(yù)測(cè)中,會(huì)在output后,接上一個(gè)全連接層,全連接層的輸入維度等于LSTM的hidden_size,之后的網(wǎng)絡(luò)處理就和BP網(wǎng)絡(luò)相同了,如下圖:
用pytorch實(shí)現(xiàn)上面的結(jié)構(gòu):
import torchfrom torch import nn
class RegLSTM(nn.Module): def __init__(self): super(RegLSTM, self).__init__() self.rnn = nn.LSTM(input_size, hidden_size, hidden_num_layers) self.reg = nn.Sequential( nn.Linear(hidden_size, 1) )
def forward(self, x): x, (ht,ct) = self.rnn(x) seq_len, batch_size, hidden_size= x.shape x = y.view(-1, hidden_size) x = self.reg(x) x = x.view(seq_len, batch_size, -1) return x
當(dāng)然,有些模型則是將輸出當(dāng)做另一個(gè)LSTM的輸入,或者使用隱藏層ht,ct的信息進(jìn)行建模,不一而足。
https://zhuanlan.zhihu.com/p/94757947
https://zhuanlan.zhihu.com/p/59862381
https://zhuanlan.zhihu.com/p/36455374
https://www.zhihu.com/question/41949741/answer/318771336
https://blog.csdn.net/android_ruben/article/details/80206792
歡迎加入機(jī)器學(xué)習(xí)愛好者微信群一起和同行交流,目前有機(jī)器學(xué)習(xí)交流群、博士群、博士申報(bào)交流、CV、NLP等微信群,請(qǐng)掃描下面的微信號(hào)加群,備注:”昵稱-學(xué)校/公司-研究方向“,例如:”張小明-浙大-CV“。請(qǐng)按照格式備注,否則不予通過。添加成功后會(huì)根據(jù)研究方向邀請(qǐng)進(jìn)入相關(guān)微信群。請(qǐng)勿在群內(nèi)發(fā)送廣告,否則會(huì)請(qǐng)出群,謝謝理解~(也可以加入機(jī)器學(xué)習(xí)交流qq群772479961)