「數據遊戲」:使用 LSTM 模型預測三天後單股收盤價

做者:瘋貓子,「數據遊戲」優勝隊伍成員算法

摘要

LSTM模型是RNN的一種,其特色是在單一循環神經網絡的基礎上,構建出了長短記憶門,也就是能夠長時間發現和記憶長依賴關係。本次比賽將使用LSTM模型來預測招商銀行三天後的收盤價,也就是利用5月10日前的數據,來預測5月15日的收盤價。數組

1、模型選擇

股價價格的預測實際上是一件極其不靠譜的事情。不少專業機構和量化交易的我的都是極力在規避價格預測這種作法的。微信

緣由有二:一是股市(不管哪一個國家,哪一種性質)隨機突發事件太多,且突發事件對股市的影響力也是高度隨機和不可預測的,也就是所謂的噪音多到讓你懷疑人生。二是,連續變量做爲預測目標是個糟糕的設計,由於這會使得預測空間太大,而致使所搜空間無限大。這個看法來自於強化學習,強化學習的一個技術要點就是把預測空間有限化,即使客觀世界是連續而無限的,也須要採用相似於Tile coding的技術使其離散化,有限化。本着迎難而上,不成功也能夠提升本身的初衷,嘗試開始着手解決這一難題。網絡

選擇LSTM模型做爲主算法來採用,是參考了kaggle上一個長期項目,預測美股收盤價的一個項目,其中第三名就是採用LSTM的。拿來測試以後,具備必定預測做用,可是預測精度不高,且性能不穩定。而後小組討論後,是否就採用這個基本模型爲核心,開展算法升級,獲得一致贊成後,因而肯定了LSTM算法爲核心算法,並作再次開發。數據結構

2、模型升級

LSTM模型之全部可以具備預測股價的能力,主要的仍是模型自己捕捉了價格序列中的時序要素中所透射出來的信息。對於模型進行預測自己是徹底沒有問題的,而此次模型升級的根本目標是提高預測精度。app

關於模型升級主要來自於兩方面的,一是經過對模型的優化,二是優化數據。dom

(一)升級LSTM

LSTM模型大概有6種變形形式,主要的特色就是針對不一樣數據輸入的類型。這裏我選用了Multiple Input模型,也就是多序列輸入,單序列輸出。選擇這個模型,對數據的構建也有很是好的促進做用,能夠構建一個張量(多維數組),這個張量是一個5維張量,每一個維度是一個特徵數據,同時還能夠按照N天的方式造成數據切片,這種設計基於兩個緣由:函數

一是數據中包含了大量信息,而越多的特徵數據,提供的信息越多,多因子的雛形。工具

二是在保持多特徵數據的基礎上,保留的時間序列的特色。也就是在不增長特徵的狀況,將特徵信息成倍增長。性能

這種數據處理模式極大的優於ML的諸多算法。ML的諸多算法仍是以單同樣本爲切片輸入全部維度的數據,在時序構建方面是有所欠缺的。

(二)升級數據集

數據是從大智慧中取出的數據,數據時間段是2010年1月1日—2019年5月10日,數據包含open(開盤價)、close(收盤價)、volume(成交量)、turnover(成交額度)、return(日收益率)。特徵選擇了5個,緣由是增長特徵必然增長數據的獲取難度,多因子模型的構建是基於豐富的數據供應基礎上,在目前的這個比賽中,是不具有這個條件,因此只用4個基本特徵數據加一個收益率的衍生變量。

按照N個交易日的模式,將數據變成一個(M,N,5)的張量表。

3、代碼解析

# 引入各類工具包
import pandas as pd
import numpy as np
np.set_printoptions(threshold=np.inf) #設置np數據在打印時可以完整輸出,方便觀察
from keras.models import Sequential
from keras.layers import LSTM,Dense
import keras
import matplotlib.pyplot as plt

# 全局參數,全部要調整的參數都在這裏
dim=300 #輸出維度數,也是LSTM的網絡節點數
epochs=400 #訓練代數(能夠理解爲訓練次數)
days=20 #讀取多少天的數據做爲一次預測。例如讀取20天的歷史數據來預測將來1天的狀況
batch_size = 535 #訓練批次大小,就是一次性讀取多少個樣本進行一次運算,越大運算速度越快,可是佔用內存和顯存越大,根據本身的機器性能設置。同時該參數還決定梯度降低算法的降低步長數。

開始構建網絡,
n_steps = days #輸入張量的維度數 
n_features = 5 #輸入張量的維度
model_2 = Sequential()
# 激活函數用relu
model_2.add(LSTM(dim, activation='relu',input_shape=(n_steps, n_features)))
# 輸出層使用全鏈接層,只要一個輸出節點
model_2.add(Dense(1))
#選擇優化器和損失函數,優化器爲線性規劃算法,損失函數用的是高維空間測定距離的函數
model_2.compile(optimizer='rmsprop', loss='mse')

接下來開始構建數據,主要分爲三個步驟完成
第一步導入數據
第二步生成數據切片,以及監督學習的標籤,也就是三天後的收盤價。拆分訓練序列訓練集、測試集、標籤
第三步載入模型進行訓練

數據導入的基本操做,順便觀察下數據集的狀況。

data = pd.read_csv('600036.csv')
data.head()
 
data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 2250 entries, 0 to 2249
Data columns (total 5 columns):
open        2250 non-null float64
close       2250 non-null float64
volume      2250 non-null int64
turnover    2250 non-null int64
return      2250 non-null float64
dtypes: float64(3), int64(2)
memory usage: 88.0 KB

構建兩個處理數據生成張量表的函數,一個用帶標籤輸出,另一個只處理輸入數據集,生成20x5的切片數據。

def processData(data,lb):
    X,Y = [],[]
    for i in range(len(data)-lb-1):
        X.append(data[i:(i+lb),0])
        try:
            Y.append(data[(i+2+lb),0])
        except:
            Y.append(data[(i+lb),0])
    return np.array(X),np.array(Y)

def pData(data,lb):
    X,Y = [],[]
    for i in range(len(data)-lb-1):
        X.append(data[i:(i+lb)])
return np.array(X)

開始處理數據,同時對數據進行特徵縮放處理,由於後面須要對特徵縮放的數據進行逆運算,因此,要定義兩個不一樣的特徵縮放函數,不然後面針對輸出標籤逆運算會沒法進行。
對數據進行特徵縮放處理,將數據縮放到0-1區間內,這樣能夠加快訓練結果的快速收斂。

from sklearn.preprocessing import MinMaxScaler
close = data['close']
cl = np.array(close)
cl = cl.reshape(cl.shape[0],1)
scl = MinMaxScaler()
sc2 = MinMaxScaler()
cl = scl.fit_transform(cl)


# 生成標籤
_,y = processData(cl,days)
X = data.values
X = sc2.fit_transform(X)
X = pData(X,days)

對數據集進行訓練集和測試集的拆分,我在這裏偷了個懶,只生成了兩組數據集。

y_train,y_test = y[:int(y.shape[0]*0.80)],y[int(y.shape[0]*0.80):]
X_train,X_test = X[:int(X.shape[0]*0.80)],X[int(X.shape[0]*0.80):]

拆分出來的數據是這個樣子的

  • y_train的數據結構爲: (1783,)
  • y_test的數據結構爲: (446,)
  • X_train的數據結構爲: (1783, 20, 5) # 1783個20x5的數據切片
  • X_test的數據結構爲: (446, 20, 5) # 446個20x5的數據切片
  • 張量表的結構爲:(一個切片)
#執行模型訓練
History = model_2.fit(
X_train,y_train,batch_size=batch_size, epochs=epochs,validation_data=(X_test,y_test),shuffle=False)


# 顯示訓練過程
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])

模型訓練過程當中的loss值,一個真實值的loss,一個是預測值的loss,能夠明顯的看到,兩個loss已經快速收斂,可是預測值的loss並不穩定。在這種狀況下,若是模型使用精確度來進行評估,明顯已經不符合實際要求。故須要從新找到模型性能評估的方法。

模型訓練完畢以後,須要對訓練模型進行效果評估,大概的評估思路分爲三步:

第一步單值預測檢驗
第二步序列預測檢驗
第三步用統計檢驗方法中的T檢驗對預測性能進行評估

#隨機從測試集中抽取一個單一數據切片進行預測
act = []
pred = []
import random
i=random.randint(0,250)
Xt = model_2.predict(X_test[i].reshape(1,days,5))
print('預測值:{0}, 實際值:{1}'.format(Xt,y_test[i].reshape(-1,1)))
pred.append(Xt)
act.append(y_test[i])

預測值:[[0.7393236]], 實際值:[[0.74340618]]

# 將測試集中的全部切片以序列的方式進行預測,查看預測結果與真實值的擬合狀況。
Xt = model_2.predict(X_test)
fig = plt.gcf()
plt.plot(y_test.reshape(-1,1),label='y_test')
plt.plot(Xt,label='Forecast')
plt.legend()

 
# T檢驗中的差值統計,查看差值序列在統計挺行上的綜合表現
a = y_test.reshape(-1,1)
b = Xt
c = a - b #實際值減去預測值
c = pd.DataFrame(c)
c.describe()

統計指標說明:

  • mean:表明測試集驗證後的結果與真實狀況的差值序列的平均值,也就是總體差別水平。正負無所謂,越趨近0越好。經過上述的結果來看,此次訓練的模型預測結果於真實狀況的總體偏差已經小於1%,
  • std:標準差,表明均值在正負兩個方向的分散程度,越小越好,說明結果比較集中,偏差比較小,經過以上結果來看分散度僅有4.33%,在95%的置信度下。

模型保存

由於在訓練模型時,確保可以產生最大的隨機數,並未設置隨機數種子。若是遇到性能較好的結果就運行下面的代碼,以便將模型保存在本地。方便評估模型訓練的最優參數。

path='my_model_2' # 請自行設置存儲路徑及文件名,例如:D:\\股票\\my_model
model_2.save(path+'.h5',include_optimizer=True) # 保存模型本體
model_2.save_weights(path + '_weights.h5') # 保存模型權重

模型載入執行預測

說明:
因爲神經網絡依靠隨機數,未設置隨機數種子,因此每次訓練結果均不相同。因此將性能較好的模型進行存儲。
在實際使用時進行模型載入,分別查看預測結果。取最佳模型。
載入數據預測5月15日的close數值

filepath = 'my_model_1'
my_model = keras.models.load_model(filepath+'.h5')
p_1 = my_model.predict(X_test)
p_1 = scl.inverse_transform(p_1)
print('5月15日的close爲:',p_1[-1])

5月15日的close爲: [33.819942]

總結

該模型最優參數組合,是經過幾十次的反覆訓練所的獲得的。在這個過程當中還作了大量的調整和比對試驗,就不作贅述,只將總結到的要點進行概括闡述:

  1. 由於構建的張量維度數並非十分大,因此在網絡的設計上,一個LSTM層加一個全鏈接層就已經足夠了。若是咱們的維度數能夠增長到上百個,這個狀況就能夠繼續增長隱藏層的數量,同時使用dropout層,丟棄部分冗餘。
  2. 對於LSTM模型,在作預測的時候,不能只給一個切片(單值)數據,這個預測的結果很大機率會產生誤差。正確的作法,應該是給一個切片序列,而你要預測的內容必須放置到最後一個。由於實驗發現,LSTM模型的運行原理中,會根據上下鏈接的數據切片修正本身的長短記憶內容,也就是具有必定的推理能力,在使用這個模型時,須要給與足夠的數據,讓模型可以進行推理。
  3. Y值(標籤)的構建一樣須要和X值(輸入)的設計進行關聯,由於這關係到你的訓練數據是離散化,仍是序列化,也關係到你的訓練方式是能夠離散化,仍是序列化(時序化)。很是重要。這也是針對預測目標反推須要選擇哪些數據組成數組的宗旨。

Ad Time

瞭解更多「數據遊戲」能夠關注微信公衆號數據科學與技術(read_csv) 或加入 QQ 羣 759677734

相關文章
相關標籤/搜索