第一部分:從RNN到LSTM
1、什么是RNN
RNN全稱循環神經網絡(Recurrent Neural Networks),是用來處理序列數據的。在傳統的神經網絡模型中,從輸入層到隱含層再到輸出層,層與層之間是全連接的,每層之間的節點是無連接的。但是這種普通的神經網絡對於很多關於時間序列的問題卻無能無力。例如,你要預測句子的下一個單詞是什么,一般需要用到前面的單詞,因為一個句子中前后單詞並不是獨立的。RNN之所以稱為循環神經網路,即一個序列當前的輸出與前面的輸出也有關。具體的表現形式為網絡會對前面時刻的信息進行記憶並應用於當前輸出的計算中,即隱藏層之間的節點不再無連接而是有連接的,並且隱藏層的輸入不僅包括輸入層的輸出還包括上一時刻隱藏層的輸出,如下圖所示:
傳統的神經網絡中,數據從輸入層輸入,在隱藏層加工,從輸出層輸出。RNN不同的就是在隱藏層的加工方法不一樣,后一個節點不僅受輸入層輸入的影響,還包受上一個節點的影響。
展開來就是這個樣子:圖中的xt−1,xt, xt+1代表不同時刻的輸入,每個x都具有input layer的n維特征,依次進入循環神經網絡以后,隱藏層輸出st受到上一時刻st−1的隱藏層輸出以及此刻輸入層輸入xt
的兩方面影響。
缺點:RNN利用內部的記憶來處理任意時序的輸入序列,並且在其處理單元之間既有內部的反饋連接又有前饋連接,這使得RNN可以更加容易處理不分段的文本等。但是由於RNN只能對部分序列進行記憶,所以在長序列上表現遠不如短序列,造成了一旦序列過長便使得准確率下降的結果。
2、什么是LSTM
長短記憶神經網絡——通常稱作LSTM,是一種特殊的RNN,能夠學習長的依賴關系。 他們由Hochreiter&Schmidhuber引入,並被許多人進行了改進和普及。他們在各種各樣的問題上工作的非常好,現在被廣泛使用。
LSTM是為了避免長依賴問題而精心設計的。 記住較長的歷史信息實際上是他們的默認行為,而不是他們努力學習的東西。
所有循環神經網絡都具有神經網絡的重復模塊鏈的形式。 在標准的RNN中,該重復模塊將具有非常簡單的結構,例如單個tanh層。


LSTM也擁有這種鏈狀結構,但是重復模塊則擁有不同的結構。與神經網絡的簡單的一層相比,LSTM擁有四層,這四層以特殊的方式進行交互。


LSTM背后的核心理念
LSTM的關鍵是細胞狀態,表示細胞狀態的這條線水平的穿過圖的頂部。
細胞的狀態類似於輸送帶,細胞的狀態在整個鏈上運行,只有一些小的線性操作作用其上,信息很容易保持不變的流過整個鏈。
門(Gate)是一種可選地讓信息通過的方式。 它由一個Sigmoid神經網絡層和一個點乘法運算組成。


Sigmoid神經網絡層輸出0和1之間的數字,這個數字描述每個組件有多少信息可以通過, 0表示不通過任何信息,1表示全部通過
LSTM有三個門,用於保護和控制細胞的狀態。
一步步的拆解LSTM
LSTM的第一步是決定我們要從細胞狀態中丟棄什么信息。 該決定由被稱為“忘記門”的Sigmoid層實現。它查看ht-1(前一個輸出)和xt(當前輸入),並為單元格狀態Ct-1(上一個狀態)中的每個數字輸出0和1之間的數字。1代表完全保留,而0代表徹底刪除。
讓我們回到語言模型的例子,試圖根據以前的語料來預測下一個單詞。 在這樣的問題中,細胞狀態可能包括當前主題的性別,從而決定使用正確的代詞。 當我們看到一個新主題時,我們想要忘記舊主題的性別。


下一步是決定我們要在細胞狀態中存儲什么信息。 這部分分為兩步。 首先,稱為“輸入門層”的Sigmoid層決定了我們將更新哪些值。 接下來一個tanh層創建候選向量Ct,該向量將會被加到細胞的狀態中。 在下一步中,我們將結合這兩個向量來創建更新值。
在我們的語言模型的例子中,我們希望將新主題的性別添加到單元格狀態,以替換我們忘記的舊對象。


現在是時候去更新上一個狀態值Ct−1了,將其更新為Ct。簽名的步驟以及決定了應該做什么,我們只需實際執行即可。
我們將上一個狀態值乘以ft,以此表達期待忘記的部分。之后我們將得到的值加上 it∗C̃ t。這個得到的是新的候選值, 按照我們決定更新每個狀態值的多少來衡量.
在語言模型的例子中,對應着實際刪除關於舊主題性別的信息,並添加新信息,正如在之前的步驟中描述的那樣。


最后,我們需要決定我們要輸出什么。 此輸出將基於我們的細胞狀態,但將是一個過濾版本。 首先,我們運行一個sigmoid層,它決定了我們要輸出的細胞狀態的哪些部分。 然后,我們將單元格狀態通過tanh(將值規范化到-1和1之間),並將其乘以Sigmoid門的輸出,至此我們只輸出了我們決定的那些部分。
對於語言模型的例子,由於只看到一個主題,考慮到后面可能出現的詞,它可能需要輸出與動詞相關的信息。 例如,它可能會輸出主題是單數還是復數,以便我們知道動詞應該如何組合在一起。


3、股票預測實戰1
在對理論有理解的基礎上,我們使用LSTM對股票每日最高價進行預測。在本例中,僅使用一維特征。
數據格式如下:
本例取每日最高價作為輸入特征[x],后一天的最高價最為標簽[y]
步驟一、導入數據:
import pandas as pd import numpy as np import matplotlib.pyplot as plt import tensorflow f=open('stock_dataset.csv') df=pd.read_csv(f) #讀入股票數據 data=np.array(df['最高價']) #獲取最高價序列 data=data[::-1] #反轉,使數據按照日期先后順序排列 #以折線圖展示data plt.figure() plt.plot(data) plt.show() normalize_data=(data-np.mean(data))/np.std(data) #標准化 normalize_data=normalize_data[:,np.newaxis] #增加維度 #———————————————————形成訓練集————————————————————— #設置常量 time_step=20 #時間步 rnn_unit=10 #hidden layer units batch_size=60 #每一批次訓練多少個樣例 input_size=1 #輸入層維度 output_size=1 #輸出層維度 lr=0.0006 #學習率 train_x,train_y=[],[] #訓練集 for i in range(len(normalize_data)-time_step-1): x=normalize_data[i:i+time_step] y=normalize_data[i+1:i+time_step+1] train_x.append(x.tolist()) train_y.append(y.tolist())
出來的train_x就是像這個樣子:
[[[-1.59618],……中間還有18個……, [-1.56340]] …… [[-1.59202] [-1.58244]]]
是一個shape為[-1,time_step,input__size]的矩陣
步驟二、定義神經網絡變量
X=tf.placeholder(tf.float32, [None,time_step,input_size]) #每批次輸入網絡的tensor Y=tf.placeholder(tf.float32, [None,time_step,output_size]) #每批次tensor對應的標簽 #輸入層、輸出層權重、偏置 weights={ 'in':tf.Variable(tf.random_normal([input_size,rnn_unit])), 'out':tf.Variable(tf.random_normal([rnn_unit,1])) } biases={ 'in':tf.Variable(tf.constant(0.1,shape=[rnn_unit,])), 'out':tf.Variable(tf.constant(0.1,shape=[1,])) }
步驟三、定義lstm網絡
def lstm(batch): #參數:輸入網絡批次數目 w_in=weights['in'] b_in=biases['in'] input=tf.reshape(X,[-1,input_size]) #需要將tensor轉成2維進行計算,計算后的結果作為隱藏層的輸入 input_rnn=tf.matmul(input,w_in)+b_in input_rnn=tf.reshape(input_rnn,[-1,time_step,rnn_unit]) #將tensor轉成3維,作為lstm cell的輸入 cell=tf.nn.rnn_cell.BasicLSTMCell(rnn_unit) init_state=cell.zero_state(batch,dtype=tf.float32) output_rnn,final_states=tf.nn.dynamic_rnn(cell, input_rnn,initial_state=init_state, dtype=tf.float32) #output_rnn是記錄lstm每個輸出節點的結果,final_states是最后一個cell的結果 output=tf.reshape(output_rnn,[-1,rnn_unit]) #作為輸出層的輸入 w_out=weights['out'] b_out=biases['out'] pred=tf.matmul(output,w_out)+b_out return pred,final_states
步驟四、訓練模型
def train_lstm(): global batch_size pred,_=rnn(batch_size) #損失函數 loss=tf.reduce_mean(tf.square(tf.reshape(pred,[-1])-tf.reshape(Y, [-1]))) train_op=tf.train.AdamOptimizer(lr).minimize(loss) saver=tf.train.Saver(tf.global_variables()) with tf.Session() as sess: sess.run(tf.global_variables_initializer()) #重復訓練10000次 for i in range(10000): step=0 start=0 end=start+batch_size while(end<len(train_x)): _,loss_=sess.run([train_op,loss],feed_dict={X:train_x[start:end],Y:train_y[start:end]}) start+=batch_size end=start+batch_size #每10步保存一次參數 if step%10==0: print(i,step,loss_) print("保存模型:",saver.save(sess,'stock.model')) step+=1
步驟五、預測模型
def prediction(): pred,_=lstm(1) #預測時只輸入[1,time_step,input_size]的測試數據 saver=tf.train.Saver(tf.global_variables()) with tf.Session() as sess: #參數恢復 module_file = tf.train.latest_checkpoint(base_path+'module2/') saver.restore(sess, module_file) #取訓練集最后一行為測試樣本。shape=[1,time_step,input_size] prev_seq=train_x[-1] predict=[] #得到之后100個預測結果 for i in range(100): next_seq=sess.run(pred,feed_dict={X:[prev_seq]}) predict.append(next_seq[-1]) #每次得到最后一個時間步的預測結果,與之前的數據加在一起,形成新的測試樣本 prev_seq=np.vstack((prev_seq[1:],next_seq[-1])) #以折線圖表示結果 plt.figure() plt.plot(list(range(len(normalize_data))), normalize_data, color='b') plt.plot(list(range(len(normalize_data), len(normalize_data) + len(predict))), predict, color='r') plt.show()
例子中只有把最高價作為特征,去預測之后的最高價趨勢,下一講會增加輸入的特征維度,把最低價、開盤價、收盤價、交易額等作為輸入的特征對之后的最高價進行預測。
最終運行結果顯示: