RNN到LSTM详解
RNN(Recurrent Neural Network)是一類用于處理序列數據的神經網絡。首先我們要明確什么是序列數據,摘取百度百科詞條:時間序列數據是指在不同時間點上收集到的數據,這類數據反映了某一事物、現象等隨時間的變化狀態或程度。這是時間序列數據的定義,當然這里也可以不是時間,比如文字序列,但總歸序列數據有一個特點——后面的數據跟前面的數據有關系。
RNN的結構及變體
我們從基礎的神經網絡中知道,神經網絡包含輸入層、隱層、輸出層,通過激活函數控制輸出,層與層之間通過權值連接。激活函數是事先確定好的,那么神經網絡模型通過訓練“學“到的東西就蘊含在“權值“中。
基礎的神經網絡只在層與層之間建立了權連接,RNN最大的不同之處就是在層之間的神經元之間也建立的權連接。如圖。
這是一個標準的RNN結構圖,圖中每個箭頭代表做一次變換,也就是說箭頭連接帶有權值。左側是折疊起來的樣子,右側是展開的樣子,左側中h旁邊的箭頭代表此結構中的“循環“體現在隱層。
在展開結構中我們可以觀察到,在標準的RNN結構中,隱層的神經元之間也是帶有權值的。也就是說,隨著序列的不斷推進,前面的隱層將會影響后面的隱層。圖中O代表輸出,y代表樣本給出的確定值,L代表損失函數,我們可以看到,“損失“也是隨著序列的推薦而不斷積累的。
除上述特點之外,標準RNN的還有以下特點:
1、權值共享,圖中的W全是相同的,UUU和VVV也一樣。
2、每一個輸入值都只與它本身的那條路線建立權連接,不會和別的神經元連接。
以上是RNN的標準結構,然而在實際中這一種結構并不能解決所有問題,例如我們輸入為一串文字,輸出為分類類別,那么輸出就不需要一個序列,只需要單個輸出。如圖。
同樣的,我們有時候還需要單輸入但是輸出為序列的情況。那么就可以使用如下結構:
還有一種結構是輸入雖是序列,但不隨著序列變化,就可以使用如下結構:
原始的NvsNN vs NNvsN RNN要求序列等長,然而我們遇到的大部分問題序列都是不等長的,如機器翻譯中,源語言和目標語言的句子往往并沒有相同的長度。
下面我們來介紹RNN最重要的一個變種:N vs M。這種結構又叫Encoder-Decoder模型,也可以稱之為Seq2Seq模型。
從名字就能看出,這個結構的原理是先編碼后解碼。左側的RNN用來編碼得到ccc,拿到c后再用右側的RNN進行解碼。得到ccc有多種方式,最簡單的方法就是把Encoder的最后一個隱狀態賦值給ccc,還可以對最后的隱狀態做一個變換得到ccc,也可以對所有的隱狀態做變換。
除了以上這些結構以外RNN還有很多種結構,用于應對不同的需求和解決不同的問題。還想繼續了解可以看一下下面這個博客,里面又介紹了幾種不同的結構。但相同的是循環神經網絡除了擁有神經網絡都有的一些共性元素之外,它總要在一個地方體現出“循環“,而根據“循環“體現方式的不同和輸入輸出的變化就形成了多種RNN結構。
標準RNN的前向輸出流程
上面介紹了RNN有很多變種,但其數學推導過程其實都是大同小異。這里就介紹一下標準結構的RNN的前向傳播過程。
再來介紹一下各個符號的含義:xxx是輸入,hhh是隱層單元,ooo為輸出,LLL為損失函數,yyy為訓練集的標簽。這些元素右上角帶的ttt代表ttt時刻的狀態,其中需要注意的是,因策單元hhh在ttt時刻的表現不僅由此刻的輸入決定,還受t時刻之前時刻的影響。V、W、UV、W、UV、W、U是權值,同一類型的權連接權值相同。
有了上面的理解,前向傳播算法其實非常簡單,對于t時刻:
h(t)=?(Ux(t)+Wh(t?1)+b)h(t)=?(Ux(t)+Wh(t?1)+b)h(t)=?(Ux(t)+Wh(t?1)+b)
其中?()?()?()為激活函數,一般來說會選擇tanhtanhtanh函數,bbb為偏置。
ttt時刻的輸出就更為簡單:
o(t)=Vh(t)+co(t)=Vh(t)+co(t)=Vh(t)+c
最終模型的預測輸出為:
y?(t)=σ(o(t))y?(t)=σ(o(t))y?(t)=σ(o(t))
其中σσσ為激活函數,通常RNN用于分類,故這里一般用softmaxsoftmaxsoftmax函數。
RNN的訓練方法——BPTT
BPTT(back-propagation through time)算法是常用的訓練RNN的方法,其實本質還是BP算法,只不過RNN處理時間序列數據,所以要基于時間反向傳播,故叫隨時間反向傳播。BPTT的中心思想和BP算法相同,沿著需要優化的參數的負梯度方向不斷尋找更優的點直至收斂。綜上所述,BPTT算法本質還是BP算法,BP算法本質還是梯度下降法,那么求各個參數的梯度便成了此算法的核心。
再次拿出這個結構圖觀察,需要尋優的參數有三個,分別是U、V、WU、V、WU、V、W。與BP算法不同的是,其中WWW和UUU兩個參數的尋優過程需要追溯之前的歷史數據,參數V相對簡單只需關注目前,那么我們就來先求解參數V的偏導數。
?L(t)?V=?L(t)?o(t)??o(t)?V\frac{\partial L^{(t)}}{\partial V}=\frac{\partial L^{(t)}}{\partial o^{(t)}}\cdot \frac{\partial o^{(t)}}{\partial V}?V?L(t)?=?o(t)?L(t)???V?o(t)?
這個式子看起來簡單但是求解起來很容易出錯,因為其中嵌套著激活函數函數,是復合函數的求道過程。
RNN的損失也是會隨著時間累加的,所以不能只求t時刻的偏導。
L=∑n=1tL(t)L=∑^t_{n=1}L^{(t)}L=n=1∑t?L(t)
?L?V=∑t=1n?L(t)?o(t)??o(t)?V\frac{\partial L}{\partial V}=\sum_{t=1}^{n}\frac{\partial L^{(t)}}{\partial o^{(t)}}\cdot \frac{\partial o^{(t)}}{\partial V}?V?L?=t=1∑n??o(t)?L(t)???V?o(t)?
W和UW和UW和U的偏導的求解由于需要涉及到歷史數據,其偏導求起來相對復雜,我們先假設只有三個時刻,那么在第三個時刻 L對WL對WL對W的偏導數為:
?L(3)?W=?L(3)?o(3)?o(3)?h(3)?h(3)?W+?L(3)?o(3)?o(3)?h(3)?h(3)?h(2)?h(2)?W+?L(3)?o(3)?o(3)?h(3)?h(3)?h(2)?h(2)?h(1)?h(1)?W\frac{\partial L^{(3)}}{\partial W}=\frac{\partial L^{(3)}}{\partial o^{(3)}}\frac{\partial o^{(3)}}{\partial h^{(3)}}\frac{\partial h^{(3)}}{\partial W}+\frac{\partial L^{(3)}}{\partial o^{(3)}}\frac{\partial o^{(3)}}{\partial h^{(3)}}\frac{\partial h^{(3)}}{\partial h^{(2)}}\frac{\partial h^{(2)}}{\partial W}+\frac{\partial L^{(3)}}{\partial o^{(3)}}\frac{\partial o^{(3)}}{\partial h^{(3)}}\frac{\partial h^{(3)}}{\partial h^{(2)}}\frac{\partial h^{(2)}}{\partial h^{(1)}}\frac{\partial h^{(1)}}{\partial W}?W?L(3)?=?o(3)?L(3)??h(3)?o(3)??W?h(3)?+?o(3)?L(3)??h(3)?o(3)??h(2)?h(3)??W?h(2)?+?o(3)?L(3)??h(3)?o(3)??h(2)?h(3)??h(1)?h(2)??W?h(1)?
相應的,L在第三個時刻對U的偏導數為:
?L(3)?U=?L(3)?o(3)?o(3)?h(3)?h(3)?U+?L(3)?o(3)?o(3)?h(3)?h(3)?h(2)?h(2)?U+?L(3)?o(3)?o(3)?h(3)?h(3)?h(2)?h(2)?h(1)?h(1)?U\frac{\partial L^{(3)}}{\partial U}=\frac{\partial L^{(3)}}{\partial o^{(3)}}\frac{\partial o^{(3)}}{\partial h^{(3)}}\frac{\partial h^{(3)}}{\partial U}+\frac{\partial L^{(3)}}{\partial o^{(3)}}\frac{\partial o^{(3)}}{\partial h^{(3)}}\frac{\partial h^{(3)}}{\partial h^{(2)}}\frac{\partial h^{(2)}}{\partial U}+\frac{\partial L^{(3)}}{\partial o^{(3)}}\frac{\partial o^{(3)}}{\partial h^{(3)}}\frac{\partial h^{(3)}}{\partial h^{(2)}}\frac{\partial h^{(2)}}{\partial h^{(1)}}\frac{\partial h^{(1)}}{\partial U}?U?L(3)?=?o(3)?L(3)??h(3)?o(3)??U?h(3)?+?o(3)?L(3)??h(3)?o(3)??h(2)?h(3)??U?h(2)?+?o(3)?L(3)??h(3)?o(3)??h(2)?h(3)??h(1)?h(2)??U?h(1)?
可以觀察到,在某個時刻的對W或是U的偏導數,需要追溯這個時刻之前所有時刻的信息,這還僅僅是一個時刻的偏導數,上面說過損失也是會累加的,那么整個損失函數對W和U的偏導數將會非常繁瑣。雖然如此但好在規律還是有跡可循,我們根據上面兩個式子可以寫出L在t時刻對W和U偏導數的通式:
?L(t)?W=∑k=0t?L(t)?o(t)?o(t)?h(t)(∏j=k+1t?h(j)?h(j?1))?h(k)?W\frac{\partial L^{(t)}}{\partial W}=\sum_{k=0}^{t}\frac{\partial L^{(t)}}{\partial o^{(t)}}\frac{\partial o^{(t)}}{\partial h^{(t)}}(\prod_{j=k+1}^{t}\frac{\partial h^{(j)}}{\partial h^{(j-1)}})\frac{\partial h^{(k)}}{\partial W}?W?L(t)?=k=0∑t??o(t)?L(t)??h(t)?o(t)?(j=k+1∏t??h(j?1)?h(j)?)?W?h(k)?
?L(t)?U=∑k=0t?L(t)?o(t)?o(t)?h(t)(∏j=k+1t?h(j)?h(j?1))?h(k)?U\frac{\partial L^{(t)}}{\partial U}=\sum_{k=0}^{t}\frac{\partial L^{(t)}}{\partial o^{(t)}}\frac{\partial o^{(t)}}{\partial h^{(t)}}(\prod_{j=k+1}^{t}\frac{\partial h^{(j)}}{\partial h^{(j-1)}})\frac{\partial h^{(k)}}{\partial U}?U?L(t)?=k=0∑t??o(t)?L(t)??h(t)?o(t)?(j=k+1∏t??h(j?1)?h(j)?)?U?h(k)?
整體的偏導公式就是將其按時刻再一一加起來。
前面說過激活函數是嵌套在里面的,如果我們把激活函數放進去,拿出中間累乘的那部分:
∏j=k+1t?hj?hj?1=∏j=k+1ttanh′?Ws\prod_{j=k+1}^{t}{\frac{\partial{h^{j}}}{\partial{h^{j-1}}}} = \prod_{j=k+1}^{t}{tanh^{'}}\cdot W_{s}j=k+1∏t??hj?1?hj?=j=k+1∏t?tanh′?Ws?
或是
∏j=k+1t?hj?hj?1=∏j=k+1ttanh′?Ws\prod_{j=k+1}^{t}{\frac{\partial{h^{j}}}{\partial{h^{j-1}}}} = \prod_{j=k+1}^{t}{tanh^{'}}\cdot W_{s}j=k+1∏t??hj?1?hj?=j=k+1∏t?tanh′?Ws?
我們會發現累乘會導致激活函數導數的累乘,進而會導致“梯度消失“和“梯度爆炸“現象的發生。
至于為什么,我們先來看看這兩個激活函數的圖像。
這是sigmoid函數的函數圖和導數圖。
這是tanh函數的函數圖和導數圖。
它們二者是何其的相似,都把輸出壓縮在了一個范圍之內。他們的導數圖像也非常相近,我們可以從中觀察到,sigmoid函數的導數范圍是(0,0.25],tach函數的導數范圍是(0,1],他們的導數最大都不大于1。
這就會導致一個問題,在上面式子累乘的過程中,如果取sigmoid函數作為激活函數的話,那么必然是一堆小數在做乘法,結果就是越乘越小。隨著時間序列的不斷深入,小數的累乘就會導致梯度越來越小直到接近于0,這就是“梯度消失“現象。其實RNN的時間序列與深層神經網絡很像,在較為深層的神經網絡中使用sigmoid函數做激活函數也會導致反向傳播時梯度消失,梯度消失就意味消失那一層的參數再也不更新,那么那一層隱層就變成了單純的映射層,毫無意義了,所以在深層神經網絡中,有時候多加神經元數量可能會比多家深度好。
你可能會提出異議,RNN明明與深層神經網絡不同,RNN的參數都是共享的,而且某時刻的梯度是此時刻和之前時刻的累加,即使傳不到最深處那淺層也是有梯度的。這當然是對的,但如果我們根據有限層的梯度來更新更多層的共享的參數一定會出現問題的,因為將有限的信息來作為尋優根據必定不會找到所有信息的最優解。
之前說過我們多用tanh函數作為激活函數,那tanh函數的導數最大也才1啊,而且又不可能所有值都取到1,那相當于還是一堆小數在累乘,還是會出現“梯度消失“,那為什么還要用它做激活函數呢?原因是tanh函數相對于sigmoid函數來說梯度較大,收斂速度更快且引起梯度消失更慢。
還有一個原因是sigmoid函數還有一個缺點,Sigmoid函數輸出不是零中心對稱。sigmoid的輸出均大于0,這就使得輸出不是0均值,稱為偏移現象,這將導致后一層的神經元將上一層輸出的非0均值的信號作為輸入。關于原點對稱的輸入和中心對稱的輸出,網絡會收斂地更好。
RNN的特點本來就是能“追根溯源“利用歷史數據,現在告訴我可利用的歷史數據竟然是有限的,這就令人非常難受,解決“梯度消失“是非常必要的。解決“梯度消失“的方法主要有:
1、選取更好的激活函數
2、改變傳播結構
關于第一點,一般選用ReLU函數作為激活函數,ReLU函數的圖像為:
ReLU函數的左側導數為0,右側導數恒為1,這就避免了“梯度消失“的發生。但恒為1的導數容易導致“梯度爆炸“,但設定合適的閾值可以解決這個問題。還有一點就是如果左側橫為0的導數有可能導致把神經元學死,不過設置合適的步長(學習旅)也可以有效避免這個問題的發生。
關于第二點,LSTM結構可以解決這個問題。
總結一下,sigmoid函數的缺點:
1、導數值范圍為(0,0.25],反向傳播時會導致“梯度消失“。tanh函數導數值范圍更大,相對好一點。
2、sigmoid函數不是0中心對稱,tanh函數是,可以使網絡收斂的更好。
LSTM
下面來了解一下LSTM(long short-term memory)。長短期記憶網絡是RNN的一種變體,RNN由于梯度消失的原因只能有短期記憶,LSTM網絡通過精妙的門控制將短期記憶與長期記憶結合起來,并且一定程度上解決了梯度消失的問題。
由于已經存在了一篇寫得非常好的博客,我在這里就直接轉載過來,再在其中夾雜點自己的理解。原文連接如下。
鏈接:https://www.jianshu.com/p/9dc9f41f0b29
長期依賴(Long-Term Dependencies)問題
RNN 的關鍵點之一就是他們可以用來連接先前的信息到當前的任務上,例如使用過去的視頻段來推測對當前段的理解。如果 RNN 可以做到這個,他們就變得非常有用。但是真的可以么?答案是,還有很多依賴因素。
有時候,我們僅僅需要知道先前的信息來執行當前的任務。例如,我們有一個語言模型用來基于先前的詞來預測下一個詞。如果我們試著預測 “the clouds are in the sky” 最后的詞,我們并不需要任何其他的上下文 —— 因此下一個詞很顯然就應該是 sky。在這樣的場景中,相關的信息和預測的詞位置之間的間隔是非常小的,RNN 可以學會使用先前的信息。
不太長的相關信息和位置間隔
但是同樣會有一些更加復雜的場景。假設我們試著去預測“I grew up in France… I speak fluent French”最后的詞。當前的信息建議下一個詞可能是一種語言的名字,但是如果我們需要弄清楚是什么語言,我們是需要先前提到的離當前位置很遠的 France 的上下文的。這說明相關信息和當前預測位置之間的間隔就肯定變得相當的大。
不幸的是,在這個間隔不斷增大時,RNN 會喪失學習到連接如此遠的信息的能力。
相當長的相關信息和位置間隔
在理論上,RNN 絕對可以處理這樣的 長期依賴 問題。人們可以仔細挑選參數來解決這類問題中的最初級形式,但在實踐中,RNN 肯定不能夠成功學習到這些知識。Bengio, et al. (1994)等人對該問題進行了深入的研究,他們發現一些使訓練 RNN 變得非常困難的相當根本的原因。
然而,幸運的是,LSTM 并沒有這個問題
LSTM 網絡
Long Short Term 網絡—— 一般就叫做 LSTM ——是一種 RNN 特殊的類型,可以學習長期依賴信息。LSTM 由Hochreiter & Schmidhuber (1997)提出,并在近期被Alex Graves進行了改良和推廣。在很多問題,LSTM 都取得相當巨大的成功,并得到了廣泛的使用。
LSTM 通過刻意的設計來避免長期依賴問題。記住長期的信息在實踐中是 LSTM 的默認行為,而非需要付出很大代價才能獲得的能力!
所有 RNN 都具有一種重復神經網絡模塊的鏈式的形式。在標準的 RNN 中,這個重復的模塊只有一個非常簡單的結構,例如一個 tanh 層。
LSTM 同樣是這樣的結構,但是重復的模塊擁有一個不同的結構。不同于 單一神經網絡層,整體上除了h在隨時間流動,細胞狀態c也在隨時間流動,細胞狀態c就代表著長期記憶。
不必擔心這里的細節。我們會一步一步地剖析 LSTM 解析圖?,F在,我們先來熟悉一下圖中使用的各種元素的圖標。
- 黃色的矩形是學習得到的神經網絡層
- 粉色的圓形表示一些運算操作,諸如加法乘法
- 黑色的單箭頭表示向量的傳輸
- 兩個箭頭合成一個表示向量的連接
- 一個箭頭分開表示向量的復制
LSTM 的核心思想
LSTM 的關鍵就是細胞狀態,水平線在圖上方貫穿運行。
細胞狀態類似于傳送帶。直接在整個鏈上運行,只有一些少量的線性交互。信息在上面流傳保持不變會很容易。
LSTM 有通過精心設計的稱作為“門”的結構來去除或者增加信息到細胞狀態的能力。門是一種讓信息選擇式通過的方法。他們包含一個 sigmoid 神經網絡層和一個 pointwise 乘法操作。
Sigmoid 層輸出 0 到 1 之間的數值,描述每個部分有多少量可以通過。0 代表“不許任何量通過”,1 就指“允許任意量通過”!
LSTM 擁有三個門,來保護和控制細胞狀態。
逐步理解 LSTM
在我們 LSTM 中的第一步是決定我們會從細胞狀態中丟棄什么信息。這個決定通過一個稱為遺忘門完成。該門會讀取ht?1ht?1和xtxt,輸出一個在 0 到 1 之間的數值給每個在細胞狀態Ct?1Ct?1 中的數字。1 表示“完全保留”,0 表示“完全舍棄”。
讓我們回到語言模型的例子中來基于已經看到的預測下一個詞。在這個問題中,細胞狀態可能包含當前主語的性別,因此正確的代詞可以被選擇出來。當我們看到新的主語,我們希望忘記舊的主語。
這里可以拋出兩個問題:這個門怎么做到“遺忘“的呢?怎么理解?既然是遺忘舊的內容,為什么這個門還要接收新的xtxt?
對于第一個問題,“遺忘“可以理解為“之前的內容記住多少“,其精髓在于只能輸出(0,1)小數的sigmoid函數和粉色圓圈的乘法,LSTM網絡經過學習決定讓網絡記住以前百分之多少的內容。對于第二個問題就更好理解,決定記住什么遺忘什么,其中新的輸入肯定要產生影響。
下一步是確定什么樣的新信息被存放在細胞狀態中。這里包含兩個部分。第一,sigmoid 層稱 “輸入門層” 決定什么值我們將要更新。然后,一個 tanh 層創建一個新的候選值向量,C? tC~t,會被加入到狀態中。下一步,我們會講這兩個信息來產生對狀態的更新。
在我們語言模型的例子中,我們希望增加新的主語的性別到細胞狀態中,來替代舊的需要忘記的主語。
現在是更新舊細胞狀態的時間了,Ct?1Ct?1 更新為 CtCt。前面的步驟已經決定了將會做什么,我們現在就是實際去完成。
我們把舊狀態與ftft相乘,丟棄掉我們確定需要丟棄的信息。接著加上it?C? tit?C~t。這就是新的候選值,根據我們決定更新每個狀態的程度進行變化。
有了上面的理解基礎輸入門,輸入門理解起來就簡單多了。sigmoid函數選擇更新內容,tanh函數創建更新候選。
最終,我們需要確定輸出什么值。這個輸出將會基于我們的細胞狀態,但是也是一個過濾后的版本。首先,我們運行一個 sigmoid 層來確定細胞狀態的哪個部分將輸出出去。接著,我們把細胞狀態通過 tanh 進行處理(得到一個在 -1 到 1 之間的值)并將它和 sigmoid 門的輸出相乘,最終我們僅僅會輸出我們確定輸出的那部分。
這三個門雖然功能上不同,但在執行任務的操作上是相同的。他們都是使用sigmoid函數作為選擇工具,tanh函數作為變換工具,這兩個函數結合起來實現三個門的功能。
LSTM 的變體
我們到目前為止都還在介紹正常的 LSTM。但是不是所有的 LSTM 都長成一個樣子的。實際上,幾乎所有包含 LSTM 的論文都采用了微小的變體。差異非常小,但是也值得拿出來講一下。
其中一個流行的 LSTM 變體,就是由 Gers & Schmidhuber (2000) 提出的,增加了 “peephole connection”。是說,我們讓 門層 也會接受細胞狀態的輸入。
上面的圖例中,我們增加了 peephole 到每個門上,但是許多論文會加入部分的 peephole 而非所有都加。
另一個變體是通過使用 coupled 忘記和輸入門。不同于之前是分開確定什么忘記和需要添加什么新的信息,這里是一同做出決定。我們僅僅會當我們將要輸入在當前位置時忘記。我們僅僅輸入新的值到那些我們已經忘記舊的信息的那些狀態 。
另一個變體是通過使用 coupled 忘記和輸入門。不同于之前是分開確定什么忘記和需要添加什么新的信息,這里是一同做出決定。我們僅僅會當我們將要輸入在當前位置時忘記。我們僅僅輸入新的值到那些我們已經忘記舊的信息的那些狀態 。
另一個改動較大的變體是 Gated Recurrent Unit (GRU),這是由 Cho, et al. (2014) 提出。它將忘記門和輸入門合成了一個單一的 更新門。同樣還混合了細胞狀態和隱藏狀態,和其他一些改動。最終的模型比標準的 LSTM 模型要簡單,也是非常流行的變體。
這里只是部分流行的 LSTM 變體。當然還有很多其他的,如Yao, et al. (2015) 提出的 Depth Gated RNN。還有用一些完全不同的觀點來解決長期依賴的問題,如Koutnik, et al. (2014) 提出的 Clockwork RNN。
要問哪個變體是最好的?其中的差異性真的重要嗎?Greff, et al. (2015) 給出了流行變體的比較,結論是他們基本上是一樣的。Jozefowicz, et al. (2015) 則在超過 1 萬種 RNN 架構上進行了測試,發現一些架構在某些任務上也取得了比 LSTM 更好的結果。
本文部分參考和摘錄了以下文章,在此由衷感謝以下作者的分享!
https://zhuanlan.zhihu.com/p/28054589
https://blog.csdn.net/qq_16234613/article/details/79476763
http://www.cnblogs.com/pinard/p/6509630.html
https://zhuanlan.zhihu.com/p/28687529
http://www.cnblogs.com/pinard/p/6509630.html
https://zhuanlan.zhihu.com/p/26892413
https://zhuanlan.zhihu.com/p/21462488?refer=intelligentunit
https://blog.csdn.net/zhaojc1995/article/details/80572098
持續更新的有用鏈接
https://www.itcodemonkey.com/article/9008.html
總結
以上是生活随笔為你收集整理的RNN到LSTM详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 瑞星:周末拦截挂马网站数减少 新闻类网站
- 下一篇: JAVA描述算法和数据结构(01):稀疏