深度运用LSTM神经网络并与经典时序模型对比
?作者 |?馮太濤
單位 | 上海理工大學
研究方向 | 概率論與數理統計
前言
RNN(循環神經網絡)是一種節點定向連接成環的人工神經網絡。不同于前饋神經網絡,RNN 可以利用內部的記憶來處理任意時序的輸入序列,即不僅學習當前時刻的信息,也會依賴之前的序列信息,所以在做語音識別、語言翻譯等等有很大的優勢。RNN 現在變種很多,常用的如 LSTM、Seq2SeqLSTM,還有其他變種如含有 Attention 機制的 Transformer 模型等等。這些變種原理結構看似很復雜,但其實只要有一定的數學和計算機功底,在學習的時候認認真真搞懂一個,后面的都迎刃而解。
本文將對 LSTM 里面知識做高度濃縮介紹(包括前饋推導和鏈式法則),然后再建立時序模型和優化模型,最后評估模型并與 ARIMA 或 ARIMA-GARCH 模型做對比。
RNN神經網絡底層邏輯介紹
(注:下面涉及的所有模型解釋圖來源于百度圖片)
1.1 輸入層、隱藏層和輸出層
▲ 圖1
從上圖 1,假設 是序列中第 個批量輸入(這里的 是樣本個數, 是樣本特征維度),對應隱藏層狀態為 ( 為隱藏層長度),最終輸出 ( 為輸出向量維度,即輸出向量到底含幾個元素!)。那么在計算 時刻 ,有公式:
這里的 為某一特定激活函數, 為需要學習的權重, 為要學習的偏差值,那么同理輸出結果為: 參數解釋如上!
1.2 損失函數定義
根據誤差函數性質,對于回歸問題,大多數建立的是基于距離形式的均方誤差函數或者絕對誤差函數,如果是分類問題,我們一般會選擇交叉熵這類函數!
時刻有誤差 ,這里的 為真實值, 為預測值。那么整個時間長度 ,我們有 ,我們的目的就是更新所有的參數 和 使 最小。
1.3 梯度下降與鏈式法則求導
這里的推導比較復雜,為了讓大家能理解到整個模型思想而不是存粹學術研究,只做重點介紹!且激活函數簡化!
對于參數 的更新,經典的梯度下降格式為: ,根據微積分知識,我們知道鏈式法則公式為:若 ,那么 可以表示為鏈式求導過程!
現在開始推導各個函數的鏈式求導結果,對于任意 時刻的輸出 ,由損失函數定義很容易知: ,那么對于 的更新,由 步才能到 ,求和可得:
對于終端時刻 ,我們很容易有:
但對于 < 時刻而言,對于隱藏層的求導比較復雜,因為有個時間前后關系,所以我們有:
那么同理,很容易我們將解決:
對于梯度消散(爆炸)的原理解釋
一般 RNN 模型,會因為在鏈式法則中存在梯度消散(爆炸)的問題,所以我們要發展新的變種來解決這種問題,那么這梯度問題到底在哪呢?仔細發現在上一節的(*)式推導過程中,對于隱藏層求導,我們繼續對(*)式改寫可得:
我們再對 往后推一步,然后依次推到 時刻,最終由數學歸納法很容易得到:?
由此式我們知道當 、 變大或變小,對于冪次計算,結果會突變大或者趨于平穩消散不見!由此一般 RNN 理論介紹到此,想具體了解的可以查閱相關論文。
LSTM底層理論介紹
為了更好的捕獲時序中間隔較大的依賴關系,基于門控制的長短記憶網絡(LSTM)誕生了!
▲ 圖2
所謂“門”結構就是用來去除或者增加信息到細胞狀態的能力。這里的細胞狀態是核心,它屬于隱藏層,類似于傳送帶,在整個鏈上運行,信息在上面流傳保持不變會變得很容易!
上圖 2 非常形象生動描繪了 LSTM 核心的“三門結構”。紅色圈就是所謂的遺忘門,那么在 時刻如下公式表示(如果我們真理解了 RNN 邏輯,LSTM 理解起來將變得比較輕松):
藍圈輸入門有
綠圈輸出門有
同理以上涉及的參數 和 為需要通過鏈式法則更新的參數!那么最后黃圈的細胞信息計算公式:
其中
這里涉及的雙曲正切函數 一般是固定的,那么費這么大事,搞這么多信息控制過程是為了什么?當然是為了更新細胞 值從而為了獲取下一步隱藏層的值:
3.1 sigmoid激活函數的意義
當 激活函數選擇 sigmoid 屬于 0~1 的函數時,對于遺忘門近似等于 1,輸入門近似等于 0,其實 是不更新的,那么過去的細胞信息一直保留到現在,解決了梯度消散問題。
同理,輸出門可以近似等于 1,也可以近似等于 0,那么近似等于 1 時細胞信息將傳遞給隱藏層;近似等于 0 時,細胞信息只自己保留。至此所有參數更新一遍并繼續向下走。。。
PS:也許初學者看到這么多符號會比較頭疼,但邏輯是從簡到復雜的,RNN 徹底理解有助于理解后面的深入模型。這里本人也省略了很多細節,大體模型框架就是如此,對于理解模型如何工作已經完全夠了。至于怎么想出來的以及更為詳細的推導過程,由于作者水平有限,可參考相關 RNN 論文,可多交流學習!
建模預測存在“右偏移”怎么辦!
為了做對比實驗,我們還會選擇之前時序文章所對應的實際銷量數據!我們將基于 keras 模塊構建自己的 LSTM 網絡進行時序預測。
▲ 圖3:實際銷量數據
4.1 構建一般LSTM模型,當我們選擇步長為1時,先給出結果如下
▲ 圖4
正常建立 LSTM 模型預測會出現如上預測值右偏現象,盡管 r2 或者 MSE 很好,但這建立的模型其實是無效模型!
4.2 原因與改進
當模型傾向于把上一時刻的真實值作為下一時刻的預測值,導致兩條曲線存在滯后性,也就是真實值曲線滯后于預測值曲線,如圖 4 那樣。之所以會這樣,是因為序列存在自相關性,如一階自相關指的是當前時刻的值與其自身前一時刻值之間的相關性。因此,如果一個序列存在一階自相關,模型學到的就是一階相關性。而消除自相關性的辦法就是進行差分運算,也就是我們可以將當前時刻與前一時刻的差值作為我們的回歸目標。
而且從之前文章做的白噪聲檢驗也發現,該序列確實存在很強的自相關性!如下圖 5 所示。
▲ 圖5
改進模型輸出
我們看下模型最終輸出結果:
▲?圖6:LSTM結果
5.1 經典時序模型下的最優輸出結果
ARIMA 模型的定階原理與建模分析:
https://zhuanlan.zhihu.com/p/417232759
▲ 圖7:ARIMA結果
此結果的全局 MSE=4401.02 大于 LSTM 網絡的 MSE=2521.30,由此可見當我們優化 LSTM 模型后,一定程度上時序建模比 ARIMA 或者 ARIMA-GARCH 要優!
LSTM 預測理論跟 ARIMA 也是有區別的,LSTM 主要是基于窗口滑動取數據訓練來預測滯后數據,其中的 cell 機制會由于權重共享原因減少一些參數;ARIMA 模型是根據自回歸理論,建立與自己過去有關的模型。兩者共同點就是能很好運用序列數據,而且通過不停迭代能無限預測下去,但預測模型還是基于短期預測有效,長期預測必然會導致偏差很大,而且有可能出現預測值趨于不變的情況。
最終代碼
from?keras.callbacks?import?LearningRateScheduler
from?sklearn.metrics?import?mean_squared_error
from?keras.models?import?Sequential
import?matplotlib.pyplot?as?plt
from?keras.layers?import?Dense
from?keras.layers?import?LSTM
from?keras?import?optimizers
import?keras.backend?as?K
import?tensorflow?as?tf
import?pandas?as?pd
import?numpy?as?npplt.rcParams['font.sans-serif']=['SimHei']##中文亂碼問題!
plt.rcParams['axes.unicode_minus']=False#橫坐標負號顯示問題!###初始化參數
my_seed?=?369#隨便給出個隨機種子
tf.random.set_seed(my_seed)##運行tf才能真正固定隨機種子sell_data?=?np.array([2800,2811,2832,2850,2880,2910,2960,3023,3039,3056,3138,3150,3198,3100,3029,2950,2989,3012,3050,3142,3252,3342,3365,3385,3340,3410,3443,3428,3554,3615,3646,3614,3574,3635,3738,3764,3788,3820,3840,3875,3900,3942,4000,4021,4055])
num_steps?=?3##取序列步長
test_len?=?10##測試集數量長度
S_sell_data?=?pd.Series(sell_data).diff(1).dropna()##差分
revisedata?=?S_sell_data.max()
sell_datanormalization?=?S_sell_data?/?revisedata##數據規范化##數據形狀轉換,很重要!!
def?data_format(data,?num_steps=3,?test_len=5):#?根據test_len進行分組X?=?np.array([data[i:?i?+?num_steps]for?i?in?range(len(data)?-?num_steps)])y?=?np.array([data[i?+?num_steps]for?i?in?range(len(data)?-?num_steps)])train_size?=?test_lentrain_X,?test_X?=?X[:-train_size],?X[-train_size:]train_y,?test_y?=?y[:-train_size],?y[-train_size:]return?train_X,?train_y,?test_X,?test_ytransformer_selldata?=?np.reshape(pd.Series(sell_datanormalization).values,(-1,1))
train_X,?train_y,?test_X,?test_y?=?data_format(transformer_selldata,?num_steps,?test_len)
print('\033[1;38m原始序列維度信息:%s;轉換后訓練集X數據維度信息:%s,Y數據維度信息:%s;測試集X數據維度信息:%s,Y數據維度信息:%s\033[0m'%(transformer_selldata.shape,?train_X.shape,?train_y.shape,?test_X.shape,?test_y.shape))def?buildmylstm(initactivation='relu',ininlr=0.001):nb_lstm_outputs1?=?128#神經元個數nb_lstm_outputs2?=?128#神經元個數nb_time_steps?=?train_X.shape[1]#時間序列長度nb_input_vector?=?train_X.shape[2]#輸入序列model?=?Sequential()model.add(LSTM(units=nb_lstm_outputs1,?input_shape=(nb_time_steps,?nb_input_vector),return_sequences=True))model.add(LSTM(units=nb_lstm_outputs2,?input_shape=(nb_time_steps,?nb_input_vector)))model.add(Dense(64,?activation=initactivation))model.add(Dense(32,?activation='relu'))model.add(Dense(test_y.shape[1],?activation='tanh'))lr?=?ininlradam?=?optimizers.adam_v2.Adam(learning_rate=lr)def?scheduler(epoch):##編寫學習率變化函數#?每隔epoch,學習率減小為原來的1/10if?epoch?%?100?==?0?and?epoch?!=?0:lr?=?K.get_value(model.optimizer.lr)K.set_value(model.optimizer.lr,?lr?*?0.1)print('lr?changed?to?{}'.format(lr?*?0.1))return?K.get_value(model.optimizer.lr)model.compile(loss='mse',?optimizer=adam,?metrics=['mse'])##根據損失函數性質,回歸建模一般選用”距離誤差“作為損失函數,分類一般選”交叉熵“損失函數reduce_lr?=?LearningRateScheduler(scheduler)###數據集較少,全參與形式,epochs一般跟batch_size成正比##callbacks:回調函數,調取reduce_lr##verbose=0:非冗余打印,即不打印訓練過程batchsize?=?int(len(sell_data)?/?5)epochs?=?max(128,batchsize?*?4)##最低循環次數128model.fit(train_X,?train_y,?batch_size=batchsize,?epochs=epochs,?verbose=0,?callbacks=[reduce_lr])return?modeldef?prediction(lstmmodel):predsinner?=?lstmmodel.predict(train_X)predsinner_true?=?predsinner?*?revisedatainit_value1?=?sell_data[num_steps?-?1]##由于存在步長關系,這里起始是num_stepspredsinner_true?=?predsinner_true.cumsum()??##差分還原predsinner_true?=?init_value1?+?predsinner_truepredsouter?=?lstmmodel.predict(test_X)predsouter_true?=?predsouter?*?revisedatainit_value2?=?predsinner_true[-1]predsouter_true?=?predsouter_true.cumsum()??##差分還原predsouter_true?=?init_value2?+?predsouter_true#?作圖plt.plot(sell_data,?label='原始值')Xinner?=?[i?for?i?in?range(num_steps?+?1,?len(sell_data)?-?test_len)]plt.plot(Xinner,?list(predsinner_true),?label='樣本內預測值')Xouter?=?[i?for?i?in?range(len(sell_data)?-?test_len?-?1,?len(sell_data))]plt.plot(Xouter,?[init_value2]?+?list(predsouter_true),?label='樣本外預測值')allpredata?=?list(predsinner_true)?+?list(predsouter_true)plt.legend()plt.show()return?allpredatamymlstmmodel?=?buildmylstm()
presult?=?prediction(mymlstmmodel)def?evaluate_model(allpredata):allmse?=?mean_squared_error(sell_data[num_steps?+?1:],?allpredata)print('ALLMSE:',allmse)evaluate_model(presult)上述代碼可直接復制使用,關鍵地方本人都有注釋,如有不清楚地方可以多多交流,也許此模型還有優化地方,可多多交流。對于 LSTM 建模,數據維度轉換是必要步驟,大家要認真理解!
總結
任何模型都不是萬能的,重點是要有發現問題和解決問題的能力。
小數據建模往往比大數據要更難,更要思考。
對于深度模型學習,本人還是強烈建議要大致懂模型的內涵和原理,有條件甚至可以自己推導一遍或者簡單實現下梯度下降算法、損失函數構建等等,否則很難解決真正的問題。
特別鳴謝
感謝 TCCI 天橋腦科學研究院對于 PaperWeekly 的支持。TCCI 關注大腦探知、大腦功能和大腦健康。
更多閱讀
#投 稿?通 道#
?讓你的文字被更多人看到?
如何才能讓更多的優質內容以更短路徑到達讀者群體,縮短讀者尋找優質內容的成本呢?答案就是:你不認識的人。
總有一些你不認識的人,知道你想知道的東西。PaperWeekly 或許可以成為一座橋梁,促使不同背景、不同方向的學者和學術靈感相互碰撞,迸發出更多的可能性。?
PaperWeekly 鼓勵高校實驗室或個人,在我們的平臺上分享各類優質內容,可以是最新論文解讀,也可以是學術熱點剖析、科研心得或競賽經驗講解等。我們的目的只有一個,讓知識真正流動起來。
📝?稿件基本要求:
? 文章確系個人原創作品,未曾在公開渠道發表,如為其他平臺已發表或待發表的文章,請明確標注?
? 稿件建議以?markdown?格式撰寫,文中配圖以附件形式發送,要求圖片清晰,無版權問題
? PaperWeekly 尊重原作者署名權,并將為每篇被采納的原創首發稿件,提供業內具有競爭力稿酬,具體依據文章閱讀量和文章質量階梯制結算
📬?投稿通道:
? 投稿郵箱:hr@paperweekly.site?
? 來稿請備注即時聯系方式(微信),以便我們在稿件選用的第一時間聯系作者
? 您也可以直接添加小編微信(pwbot02)快速投稿,備注:姓名-投稿
△長按添加PaperWeekly小編
🔍
現在,在「知乎」也能找到我們了
進入知乎首頁搜索「PaperWeekly」
點擊「關注」訂閱我們的專欄吧
·
總結
以上是生活随笔為你收集整理的深度运用LSTM神经网络并与经典时序模型对比的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 卫生间用红砖回填会加重墙体吗?
- 下一篇: 恩平市做吊顶的装饰公司电话?