前言:由於梯度消失的存在,在實際應用中,RNN很難處理長距離的依賴。RNN的一種改進版本:長短時記憶網絡(Long Short Term Memory Network, LSTM)。 LSTM就是用來解決RNN中梯度消失問題的。
怎么解決的呢?
LSTM增加了一個可以相隔多個timesteps來傳遞信息的方法。想想有一個傳送帶在你處理sequences時一起運轉。每個時間節點的信息都可以放到傳送帶上,或者從傳送帶上拿下來,當然你也可以更新傳送帶上的信息。這樣就保存了很久之前的信息,防止了信息的丟失。
LSTM
一、概述

如果把LSTM當成黑盒子看待,可以分為以下關鍵變量(參考上圖):
- 輸入:\(h_{t-1}\) (t-1時刻的隱藏層向量)和 \(x_t\)(t時刻的特征向量)
- 輸出:\(h_t\)(加softmax即可作為真正輸出,否則作為隱藏層)
- 主線/記憶: \(c_{t-1}\) 和 \(c_t\)
LSTM分為三個部分:
- 遺忘門:決定了上一時刻的單元狀態\(c_{t-1}\)有多少保留到當前時刻\(c_t\)。如果之前單元狀態中有主語,而輸入中又有了主語,那么原來存在的主語就應該被遺忘。concatenate的輸入和上一時刻的輸出經過sigmoid函數后,越接近於0被遺忘的越多,越接近於1被遺忘的越少。
- 輸入門:決定了當前時刻網絡的輸入\(x_t\)有多少保存到單元狀態\(c_t\);即哪些新的狀態應該被加入。新進來的主語自然就是應該被加入到細胞狀態的內容,同理也是靠sigmoid函數來決定應該記住哪些內容。
- 輸出門:控制單元狀態\(c_t\)有多少輸出到LSTM的當前輸出值\(h_t\)。根據當前的狀態和現在的輸入,確定輸出的比例。

- \(h_{t-1}\)和\(x_t\)聯合起來作為最終的輸入數據。
- \(c_{t-1}\)和\(c_t\)始終與外界隔離開來,顯然是作為LSTM記憶或者主線劇情的存在。
- \(f_t\)為遺忘門的輸出。主線進來后,首先受到遺忘門的衰減作用。
- \(i_t\)為輸入門的輸出。輸入門控制“補給大小”,給主線補充能量生成全新的主線。
- \(o_t\)為輸出門的輸出,其經過變換得到當前時刻的隱層向量\(h_t\),同時也為當前時刻的輸出。
二、三個門的計算公式,在時間步t。
假設1個batch,輸入為[1,length,d]。length是預定義的最大序列長度, \(d\)是序列中每個step的維度。隱層神經元個數為\(h\)。
- 遺忘門部分
\(x_t = [1,d]\),\(h_{t-1} = [1,h]\),則遺忘門權重矩陣的形狀 \(W_f = [d+h,h]\),遺忘門的輸出\(f_t = [1,h]\)
- 輸入門部分
輸入門矩陣\(W_i = [d+h,h]\),輸入門矩陣的輸出\(i_t =[1,h]\);
補給矩陣\(W_g=[d+h,h]\),補給輸出\(g_t=[1,h]\);
補給主線\(c_t=[1,h]\)。
- 輸出門部分
輸出門矩陣\(W_o = [d+h,h]\),輸出門輸出\(o_t=[1,h]\)
最終的輸出\(h_t=[1,h]\)
三、LSTM參數的數量
LSTM參數應該指包括embedding層的整個網絡的參數的數量。
LSTM的參數計算公式:
四、LSTM 和 RNN 結構對比


LSTM實現:時間序列預測
數據集:AirPassengers。1949 到 1960 一共 12 年,每年 12 個月的數據,一共 144 個數據,單位是 1000
數據集經過處理后得到訓練樣本
train_x : array([[x1,...x12],...]) 每個樣本為長度為12的數組,代表連續12個月份的乘客人數。因為輸入數據形狀為: [batch,時間步長,每個時刻的維度]
train_y : array([[y1],...]) 因為是時間序列預測,標簽為一個具體的數值。
一、構建LSTM模型
'''定義LSTM cell組件,該組件將在訓練過程中被不斷更新參數'''
def LstmCell():
lstm_cell = rnn.BasicLSTMCell(HIDDEN_SIZE, state_is_tuple=True)#
return lstm_cell
'''定義LSTM模型'''
def lstm_model(X, y):
# 輸入x的shape 為: (?, 1, 12),表示 (batch_size,序列長度為1,序列每步的維度為12)
'''以前面定義的LSTM cell為基礎定義多層堆疊的LSTM,我們這里只有1層'''
cell = rnn.MultiRNNCell([LstmCell() for _ in range(NUM_LAYERS)])
'''將已經堆疊起的LSTM單元轉化成動態的可在訓練過程中更新的LSTM單元'''
output, _ = tf.nn.dynamic_rnn(cell, X, dtype=tf.float32)
# output 的shape為: (?, 1, 40),表示 (batch_size,序列長度為1,隱層大小為40)
'''根據預定義的每層神經元個數來生成隱層每個單元'''
output = tf.reshape(output, [-1, HIDDEN_SIZE])
'''通過無激活函數的全連接層計算線性回歸,並將數據壓縮成一維數組結構'''
predictions = tf.contrib.layers.fully_connected(output, 1, None)
'''統一預測值與真實值的形狀'''
labels = tf.reshape(y, [-1])
predictions = tf.reshape(predictions, [-1])
'''定義損失函數,這里為正常的均方誤差'''
loss = tf.losses.mean_squared_error(predictions, labels)
'''定義優化器各參數'''
train_op = tf.contrib.layers.optimize_loss(loss,
tf.contrib.framework.get_global_step(),
optimizer='Adagrad',
learning_rate=0.6)
'''返回預測值、損失函數及優化器'''
return predictions, loss, train_op
二、訓練所用的模塊
learn = tf.contrib.learn
regressor = SKCompat(learn.Estimator(model_fn=lstm_model, model_dir='Models/model_2'))
tf.contrib.learn.Estimaor(模型,保存路徑),以sklearn的方式定義了一個模型,可以調用fit和predict方法完成訓練和預測。
Estimator里面還有一個叫SKCompat的類,如果使用x,y而不是input_fn來傳參數的形式,需要用這個類包裝一下。這里我們使用了x,y的輸入形式。
三、訓練和預測
'''以仿sklearn的形式訓練模型,這里指定了訓練批尺寸和訓練輪數'''
regressor.fit(train_X, train_y, batch_size=BATCH_SIZE, steps=TRAINING_STEPS)
'''利用已訓練好的LSTM模型,來生成對應測試集的所有預測值'''
predicted = np.array([pred for pred in regressor.predict(test_X)])
參考資料
- 快速理解LSTM,從懵逼到裝逼
https://zhuanlan.zhihu.com/p/88892937 - 零基礎入門深度學習(6) - 長短時記憶網絡(LSTM)
https://zybuluo.com/hanbingtao/note/581764 - (數據科學學習手札40)tensorflow實現LSTM時間序列預測
https://www.cnblogs.com/feffery/p/9130728.html
