LSTM

Photo by Markus Spiske on Unsplash

本文重點概要

  • 文章難度:★★★☆☆
  • 使用交易面資料進行股票預測
  • 閱讀建議:本文使用RNN架構進行時間序列預測,需要對時間序列或是深度學習有基礎瞭解,也可以參考【量化分析】預測市場?! 這篇關於 使用回歸模型 以及LOGISTIC模型來預測股價的。

前言

預測股票一直是人們所追求的,然而股票的隨機性遠遠難以掌握,在資料科學的進步下,計算成本大幅降低,本文用使用相較於【量化分析】預測市場?! 更複雜的深度學習模型,進行股票預測,使用前10天的開盤價、最高價、最低價、收盤價、成交量,預測隔天的收盤價。

編輯環境及模組需求

本文使用 Google colab作為編輯器

###三寶
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import tejapi
tejapi.ApiConfig.api_key=###yourkey
##################
import tensorflow as tf
from keras.layers.core import Dense, Dropout, Activation
from keras.callbacks import EarlyStopping,ModelCheckpoint
from keras.models import Sequential
from keras.layers import LSTM
from sklearn.preprocessing import MinMaxScaler

資料庫使用

證券交易資料表:上市(櫃)未調整股價(日),資料代碼為(TWN/EWPRCD)。

建立函式

建立RMSE 來評估模型預測能力

以及將資料轉換成符合LSTM輸入的形式型態如下圖所示分別代表為

(batch_size,time_steps,seq_len) : 1163組,5天,5個變數

dataset : 輸入訓練資料

target : 預測資料

start_index : 起始點 通常為0 因為後續自己會分組

end_index : 終點 設0

history_size 輸入長度 本文選10 target_size 預測長度 本文選1

def root_mean_squared_error(y_true, y_pred):
    return np.sqrt(np.mean(np.square(y_pred - y_true)
###計算Rmse
def multivariate_data(dataset, target, start_index, end_index, history_size,
                      target_size, single_step=False):
  data = []
  labels = []
  
  start_index = start_index + history_size
  if end_index is None:
    end_index = len(dataset) - target_size
  
  for i in range(start_index, end_index):
    indices = range(i-history_size, i)
    data.append(dataset[indices])
    
    if single_step:
      labels.append(target[i+target_size])
    else:
      labels.append(target[i:i+target_size])
  
  return np.array(data), np.array(labels)

接下來是來疊模型了,其中Dropout是用來防止過擬,本文就不詳盡解釋原理,可以從d去調整從0~1之間,

input length為輸入的時間長度,本文選用10天

input_dim為變數數量,本文總共有5個變數

return_sequences : True 為維持 (batch ,time_steps ,seq_len) ,連接下一層LSTM 設置 False 將會變成一維

loss 使用mean_squared_error訓練,Optimizer 使用 Adam

def build_model(input_length, input_dim):
    d=0.3
    model= Sequential()
    model.add(LSTM(128,input_shape=(input_length, input_dim),return_sequences=True))
    
    model.add(Dropout(d))
    
    model.add(LSTM(64,input_shape=(input_length, input_dim),return_sequences=False))
    
    model.add(Dropout(d))
    
    model.add(Dense(1,activation='linear'))
#linear / softmax(多分類) / sigmoid(二分法)
    
    model.compile(loss='mse',optimizer='adam')
    return mode1

資料導入

coid='3037'
start='2016-01-01'
end='2022-5-22'
opts={'columns': ['open_d' ,'high_d','low_d','mdate', 'volume','close_d']}
tw=tejapi.get('TWN/EWPRCD',coid=coid,
                mdate={'gt':start,'lt':end},
                paginate=True,
                chinese_column_name=True,
                opts=opts
                )
tw.set_index("日期",drop=True,inplace=True)
tw.sort_index(inplace=True)

設置 變數 與應變數 收盤價為要預測的,其他為變數

y =tw["收盤價"]
x =tw
將資料正規化,讓資料變成0~1之間,讓資料訓練速度更快,且更容易收斂
將資料正規化,讓資料變成0~1之間,讓資料訓練速度更快、更容易收斂

 

scaler=MinMaxScaler(feature_range=(0,1))
y=scaler.fit_transform(y.to_frame())
scaler1=MinMaxScaler(feature_range=(0,1))
x=scaler1.fit_transform(x)

並將資料分成訓練組,驗證組,測試組

x,y=multivariate_data( x ,y , 0 ,None, 10 , 1 ,single_step=True)
split =0.95
x_,y_  = x[0:int(split*len(x))] , y[0:int(split*len(x))]
x_test ,y_test   = x[int(split*len(x)):] , y[int(split*len(x)):]
split= 0.8
x_train,y_train  =x_[:int(split*len(x_))] , y_[:int(split*len(x_))]
x_vaild,y_vaild  =x_[int(split*len(x_)):] , y_[int(split*len(x_)):]
模型分成4種,一層Lstm,一層Dense
模型分成4種,一層Lstm,一層Dense

 

my_callbacks = [
tf.keras.callbacks.EarlyStopping(patience=300, monitor = 'val_loss')
] ######## 在訓練組訓練,使用驗證組選取
filepath="lstm.best.hdf5"
checkpoint = ModelCheckpoint(filepath, monitor='val_loss', verbose=1, mode='min',save_best_only=True)
call_backlist = [my_callbacks,checkpoint]
lstm00 = lstm_model0(10,5)
historylstm0 = lstm0.fit( x_train, y_train, batch_size=30,shuffle=False , epochs=1000,validation_data=(x_vaild,y_vaild),callbacks=call_backlist)
lstm00.summary()

EarlyStopping : 在訓練組訓練參數,以驗證組最低為選擇標準,如果300個epochs,沒有改善即停止訓練

filepath : 模型儲存路徑

ModelCheckpoint : 選擇val_loss最低的當作最後的模型

batch 是每次訓練抽取樣本數

epochs 訓練次數

shuffle True 隨機打亂 False 不打亂排序

視覺化結果

#繪製 訓練情形
plt.plot(historylstm0.history['loss'])
plt.plot(historylstm0.history['val_loss'])
plt.title('Model loss')
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.legend(['Train', 'Test'], loc='upper left')
plt.show()
發現使用 一層lstm訓練效果不好 ,嚴重過擬
發現使用 一層lstm訓練效果不好 ,嚴重過擬

 

lstm0train  = lstm00.predict(x_train)
lstm0val = lstm00.predict(x_vaild)
lstm0pre = lstm00.predict(x_test)
pre = lstm00.predict(x_train)
pre1=lstm00.predict(x_vaild)
fc=np.concatenate((pre,pre1))
yreal=np.concatenate((y_train,y_vaild))
plt.figure(facecolor='white')
pd.Series(fc.reshape(-1)).plot(color='blue', label='Predict1')
pd.Series(yreal.reshape(-1)).plot(color='red', label='Original')
plt.legend()
plt.show()
從這張圖就能看見,單層的Lstm模型無法掌握此時間序列
從這張圖就能看見,單層的Lstm模型無法掌握此時間序列

 

lstm0pre= scaler.inverse_transform(lstm0pre)#將資料轉換回來
y_test = scaler.inverse_transform(y_test.reshape(-1,1))
plt.figure()
plt.plot(lstm0pre)
plt.plot(y_test)
plt.title('pre')
plt.ylabel('股價')
plt.xlabel('day')
plt.legend(['pre', 'Test'], loc='upper left')
plt.show()
root_mean_squared_error(lstm0pre,y_test)
預測效果非常不好!
預測效果非常不好!

 

接下來嘗試2層的

使用更複雜的模型
使用更複雜的模型

 

模型結果
模型結果

結論

我們能看到雖然在stack-Lstm下擬和效果不錯,但實際觀察過後,發現模型的預測僅僅是把昨天去預測明天,也就是使用深度學習單純預測隔天收盤價也是不太可行的,或許需要改成預測漲跌,以及加入更多特徵看是否能改善預測能力! 也許能結合之前轉寫的一些選股策略再將資料餵入,以後會再為大家介紹!

完整程式碼

延伸閱讀

相關連結

返回總覽頁
Procesing