利用 LSTM 神經網絡預測股價走勢
Python中文社區(ID:python-china)
LSTM 神經網絡
長短期記憶 (LSTM) 神經網絡屬于循環神經網絡 (RNN) 的一種,特別適合處理和預測與時間序列相關的重要事件。以下面的句子作為一個上下文推測的例子:
“我從小在法國長大,我會說一口流利的??”
由于同一句話前面提到”法國“這個國家,且后面提到“說”這個動作。因此,LSTM便能從”法國“以及”說“這兩個長短期記憶中重要的訊號推測出可能性較大的”法語“這個結果。
K線圖與此類似,股價是隨著時間的流動及重要訊號的出現而做出反應的:
- 在價穩量縮的盤整區間中突然出現一帶量突破的大紅K,表示股價可能要上漲了
- 在跳空缺口后出現島狀反轉,表示股價可能要下跌了
- 在連漲幾天的走勢突然出現帶有長上下影線的十字線,表示股價有反轉的可能
LSTM 要做的事情就是找出一段時間區間的K棒當中有沒有重要訊號(如帶量紅K)并學習預測之后股價的走勢。
LSTM 股價預測實例
數據是以鴻海(2317)從2013年初到2017年底每天的開盤價、收盤價、最高價、最低價、以及成交量等數據。
首先將數據寫入并存至pandas的DataFrame,另外對可能有N/A的row進行剔除:
數據寫入:
- import pandas as pd
- foxconndf= pd.read_csv('./foxconn_2013-2017.csv', index_col=0 )
- foxconndf.dropna(how='any',inplace=True)
為了避免原始數據太大或是太小沒有統一的范圍而導致 LSTM 在訓練時難以收斂,我們以一個最小最大零一正規化方法對數據進行修正:
- from sklearn import preprocessing
- def normalize(df):
- newdf= df.copy()
- min_max_scaler = preprocessing.MinMaxScaler()
- newdf['open'] = min_max_scaler.fit_transform(df.open.values.reshape(-1,1))
- newdf['low'] = min_max_scaler.fit_transform(df.low.values.reshape(-1,1))
- newdf['high'] = min_max_scaler.fit_transform(df.high.values.reshape(-1,1))
- newdf['volume'] = min_max_scaler.fit_transform(df.volume.values.reshape(-1,1))
- newdf['close'] = min_max_scaler.fit_transform(df.close.values.reshape(-1,1))
- return newdf
- foxconndf_norm= normalize(foxconndf)
然后對數據進行訓練集與測試集的切割,另外也定義每一筆數據要有多長的時間框架:
- import numpy as np
- def data_helper(df, time_frame):
- # 數據維度: 開盤價、收盤價、最高價、最低價、成交量, 5維
- number_features = len(df.columns)
- # 將dataframe 轉換為 numpy array
- datavalue = df.as_matrix()
- result = []
- # 若想要觀察的 time_frame 為20天, 需要多加一天作為驗證答案
- for index in range( len(datavalue) - (time_frame+1) ): # 從 datavalue 的第0個跑到倒數第 time_frame+1 個
- result.append(datavalue[index: index + (time_frame+1) ]) # 逐筆取出 time_frame+1 個K棒數值做為一筆 instance
- result = np.array(result)
- number_train = round(0.9 * result.shape[0]) # 取 result 的前90% instance 作為訓練數據
- x_train = result[:int(number_train), :-1] # 訓練數據中, 只取每一個 time_frame 中除了最后一筆的所有數據作為feature
- y_train = result[:int(number_train), -1][:,-1] # 訓練數據中, 取每一個 time_frame 中最后一筆數據的最后一個數值(收盤價)作為答案
- # 測試數據
- x_test = result[int(number_train):, :-1]
- y_test = result[int(number_train):, -1][:,-1]
- # 將數據組成變好看一點
- x_train = np.reshape(x_train, (x_train.shape[0], x_train.shape[1], number_features))
- x_test = np.reshape(x_test, (x_test.shape[0], x_test.shape[1], number_features))
- return [x_train, y_train, x_test, y_test]
- # 以20天為一區間進行股價預測
- X_train, y_train, X_test, y_test = data_helper(foxconndf_norm, 20)
我們以 Keras 框架作為 LSTM 的模型選擇,首先在前面加了兩層 256個神經元的 LSTM layer,并都加上了Dropout層來防止數據過度擬合(overfitting)。最后再加上兩層有不同數目神經元的全連結層來得到只有1維數值的輸出結果,也就是預測股價:
- from keras.models import Sequential
- from keras.layers.core import Dense, Dropout, Activation
- from keras.layers.recurrent import LSTM
- import keras
- def build_model(input_length, input_dim):
- d = 0.3
- model = Sequential()
- model.add(LSTM(256, input_shape=(input_length, input_dim), return_sequences=True))
- model.add(Dropout(d))
- model.add(LSTM(256, input_shape=(input_length, input_dim), return_sequences=False))
- model.add(Dropout(d))
- model.add(Dense(16,kernel_initializer="uniform",activation='relu'))
- model.add(Dense(1,kernel_initializer="uniform",activation='linear'))
- model.compile(loss='mse',optimizer='adam', metrics=['accuracy'])
- return model
- # 20天、5維
- model = build_model( 20, 5 )
建立好 LSTM 模型后,我們就用前面編輯好的訓練數據集開始進行模型的訓練:LSTM 模型訓練
- # 一個batch有128個instance,總共跑50個迭代
- model.fit( X_train, y_train, batch_size=128, epochs=50, validation_split=0.1, verbose=1)
在經過一段時間的訓練過程后,我們便能得到 LSTM 模型(model)。接著再用這個模型對測試數據進行預測,以及將預測出來的數值(pred)與實際股價(y_test)還原回原始股價的大小區間:
LSTM 模型預測股價及還原數值
- def denormalize(df, norm_value):
- original_value = df['close'].values.reshape(-1,1)
- norm_valuenorm_value = norm_value.reshape(-1,1)
- min_max_scaler = preprocessing.MinMaxScaler()
- min_max_scaler.fit_transform(original_value)
- denorm_value = min_max_scaler.inverse_transform(norm_value)
- return denorm_value
- # 用訓練好的 LSTM 模型對測試數據集進行預測
- pred = model.predict(X_test)
- # 將預測值與實際股價還原回原來的區間值
- denorm_pred = denormalize(foxconndf, pred)
- denorm_ytest = denormalize(foxconndf, y_test)
LSTM 預測股價結果
讓我們把還原后的數值與實際股價畫出來,看看效果如何:
LSTM 預測股價結果
- import matplotlib.pyplot as plt
- %matplotlib inline
- plt.plot(denorm_pred,color='red', label='Prediction')
- plt.plot(denorm_ytest,color='blue', label='Answer')
- plt.legend(loc='best')
- plt.show()
如下圖,藍線是實際股價、紅線是預測股價。雖然整體看起來預測股價與實際股價有類似的走勢,但仔細一看預測股價都比實際股價落后了幾天。
所以我們來調整一些設定:
- 時間框架長度的調整
- Keras 模型里全連結層的 activation 與 optimizaer 的調整
- Keras 模型用不同的神經網路(種類、順序、數量)來組合batch_size 的調整、epochs 的調整 …
經過我們對上述的幾個參數稍微調整過后,我們就得到一個更貼近實際股價的預測結果啦。