LSTMで株価予測入門 [Python,Keras]

こんにちは、スーパーソフトウエアの船木です。

時系列データの未来の値をディープラーニングで予測する方法を見ていきます。RNN(再帰型ニューラルネットワーク)の一種であるLSTMを使いますが、複雑な数式やロジックではなく実用性やメリットを感じてもらうために入門的な内容です。興味を持った人は、より詳しく数式や論文にあたってもらえればと思います。

また、当然ですが投資取引への勧誘等を目的にしたものではなく、本情報を利用した際の取引等は全て自己の責任において行ってください。

LSTMとは

LSTMとは「Long Short Term Memory」の略で、長・短期記憶と呼ばれるディープラーニングのアーキテクチャです。元々RNNは古いアウトプットを次のインプットとして使用することで学習していきますが、長期的な特徴の学習には向いていない仕組みでした。

LSTMの特徴として、RNNの仕組みに加えて長期的記憶をアウトプットし、それを少しずつ変えてインプットデータとして使用することで長・短期記憶を実現し、飛躍的な成果を挙げました。データの長期の特徴、短期の特徴を記憶して、さらには一部を忘れることも可能なので、より人間の脳に近いと言えるかもしれません。2016年頃からGoogle、Apple、Amazon各社の音声認識などにもLSTMが使われている実用度も知名度も高い手法です。

LSTMのアプローチ方法

ここで実装する基本的な考え方としては、株価の時系列データをWindow(窓)と呼ばれる期間分ずらしたデータをインプットデータとして学習を行います。例えばWindowサイズが60の場合、過去60日分のデータを学習して61日目のデータを予想する、ということを繰り返して学習するモデルになります。
そして、時系列データ全体をトレーニング用とテスト用に分けて、トレーニングデータで学習したモデルを、テストデータでどの程度現実に近い値が予測できているか、を視覚的に確認していきます。

LSTMの準備

				
					import numpy as np
from sklearn.metrics import r2_score
from pandas_datareader import data as pdr
import yfinance as yf
yf.pdr_override()
from datetime import datetime
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import Dense, LSTM, Dropout
import matplotlib.pyplot as plt
plt.style.use("fivethirtyeight")
%matplotlib inline
				
			

Kerasやmatplotlibなど必要なライブラリを読み込みます。Kerasとは、Googleが開発したディープラーニングのフレームワークTensorflowに含まれているライブラリで、記述が比較的簡単になっています。また、yfinanceライブラリがyahooからデータ取得が出来ないようのでpdr_overrideでfixしています。

Google(アルファベット)のOHLCVを取得する

				
					s_target = 'GOOG'
df = pdr.get_data_yahoo(s_target, start='2014-01-01', end=datetime.now())
df.head()
				
			
blank
まず、時系列データを取得してDataFrameに保持します。OHLCVとは、Open(始値), High(高値), Low(安値), Close(終値), Volume(取引高)の頭文字をとったもので、日次や月次での時系列データです。データを分析して自動で売買するシステムトレードやテクニカル分析などではよく使われます。今回はGoogle(Alphabet Inc Class C)の株価をyfinanceでデータを取得して、確認のためにhead()でデータの先頭を表示しています。
「GOOG」という文字列はティッカーシンボルで、米国市場の株式コードです。アップル:AAPL、マイクロソフト:MSFT、アマゾン:AMZN、AT&T:T、ビザ:Vなどに変えれば各社の株価データを取得することができます。

株価データをグラフで確認

				
					plt.figure(figsize=(16,6))
plt.title(s_target + ' Close Price History')
plt.plot(df['Close'])
plt.xlabel('Date', fontsize=14)
plt.ylabel('Close Price USD ($)', fontsize=14)
plt.show()
				
			
blank
matplotlibを使うと簡単にデータをグラフとして表示できます。2014年から最新の2022年までのデータが入っていることが確認できます。

株価のMA(移動平均)も確認

株価のテクニカル分析では基本のMA(移動平均)も、Pythonを使うとデータとして簡単に処理できるのでご紹介します。(今回の株価予測には直接関係ありません)
				
					ma_day = [10, 20, 50]
for ma in ma_day:
    column_name = f"MA for {ma} days"
    df[column_name] = df['Adj Close'].rolling(ma).mean()
				
			
				
					plt.figure(figsize=(16,6))
plt.title(s_target + ' Close Price MA History')
plt.plot(df['Close'][-300:])
plt.plot(df['MA for 10 days'][-300:])
plt.plot(df['MA for 20 days'][-300:])
plt.plot(df['MA for 50 days'][-300:])
plt.xlabel('Date', fontsize=14)
plt.ylabel('Close Price USD ($)', fontsize=14)
plt.legend(['Close', 'MA for 10 days', 'MA for 20 days', 'MA for 50 days'], loc='upper right')
plt.show()
				
			
blank
rolling関数で指定期間を取り出してmeanで平均を取るだけで移動平均の数字データが作成できます。上記は、10日、20日、50日の終値に対する移動平均です。グラフでは全期間を表示するとMAが分かりにくいので直近300日分の表示にしています。

LSTMのためのデータを準備

				
					# Close(終値)のデータ
data = df.filter(['Close'])
dataset = data.values

# データを0〜1の範囲に正規化
scaler = MinMaxScaler(feature_range=(0,1))
scaled_data = scaler.fit_transform(dataset)
scaled_data
				
			
終値のデータだけを取り出して、このデータ値は実際のドル価格で入っているため、0〜1の範囲の値に正規化します。
blank

LSTMトレーニングデータを準備

次に、トレーニング、テストそれぞれのデータに分割するサイズを計算します。今回はデータ全体の80%をトレーニングデータ、残り20%をテストデータとしました。
				
					# 全体の80%をトレーニングデータとして扱う
training_data_len = int(np.ceil( len(dataset) * .8 ))
				
			
				
					# どれくらいの期間をもとに予測するか
window_size = 60

train_data = scaled_data[0:int(training_data_len), :]

# train_dataをx_trainとy_trainに分ける
x_train, y_train = [], []
for i in range(window_size, len(train_data)):
    x_train.append(train_data[i-window_size:i, 0])
    y_train.append(train_data[i, 0])

# numpy arrayに変換
x_train, y_train = np.array(x_train), np.array(y_train)
x_train = np.reshape(x_train, (x_train.shape[0], x_train.shape[1], 1))
				
			
ここでwindow sizeを60に設定しています。過去60日分のデータを特徴量として利用して次の61日目を学習するという意味になります。その期間だけずれた配列を作り、numpy arrayに変換します。

LSTMモデルの実装と学習

トレーニングデータが準備できたので、LSTMモデルを構築します。
				
					model = Sequential()
model.add(LSTM(units=50,return_sequences=True,input_shape=(x_train.shape[1], 1)))
model.add(Dropout(0.2))
model.add(LSTM(units=50,return_sequences=True))
model.add(Dropout(0.2))
model.add(LSTM(units=50,return_sequences=True))
model.add(Dropout(0.2))
model.add(LSTM(units=50))
model.add(Dropout(0.2))
model.add(Dense(units=1))

model.compile(optimizer='adam', loss='mean_squared_error')
history = model.fit(x_train, y_train, batch_size=32, epochs=100)
				
			

どういうモデルにするかが問題ですが、今回はLSTMを用いて株価を予測するにはどれくらい学習すべきかを書いた論文「Stock Market Prediction Using LSTM Recurrent Neural Network」に近い実装となっていそうな「mwitiderrick/stockprice」を参考にしました。LSTMの層にそれぞれDropoutが接続されているので、特徴を見つけながら一部は忘れて強くなっていくイメージですね。最適化アルゴリズムにadam、損失関数にはMSE(平均二乗誤差)を指定しています。

これを実行すると下記のように学習が始まります。予測値と正解値を計算して、損失(loss)が最小になるように学習していきます。しばらく眺めているとlossの値が小さくなっていくはずです。

blank

また、batch_size(バッチサイズ)とepochs(エポック)を指定していますが、これはトレーニングデータをバッチサイズを元にして分割して、分割した数だけ学習を繰り返します。さらにその学習をエポック数回繰り返します。

一般的にエポック数が多いほど良いモデルとなりますが、ただし、ある値を超えると悪化することもあります。どれくらいで悪化するかなどは実際に学習を実行してみないと分からない部分なので、通常は精度を検証しながら学習をすすめていきます。
				
					model.summary()
				
			
で作成したモデルの概要を表示できます。
blank

テストデータでLSTMモデルを検証する

作成したモデルをテストデータで検証してみましょう。まずは先ほど残しておいた20%のデータでテストデータを作成して、トレーニングデータの時と同様にnumpy arrayに変換します。
				
					# テストデータを作成
test_data = scaled_data[training_data_len - window_size: , :]

x_test = []
y_test = dataset[training_data_len:, :]
for i in range(window_size, len(test_data)):
    x_test.append(test_data[i-window_size:i, 0])

# numpy arrayに変換
x_test = np.array(x_test)
x_test = np.reshape(x_test, (x_test.shape[0], x_test.shape[1], 1 ))
				
			
予測(predict)を実行してpredictionsに入力します。最初に0〜1の値に正規化していたので、実際の価格に戻しています。
簡単な確認のために、二乗平均平方根誤差RMSEと、決定係数R2という値を見てみます。RMSEは0に近いほど予測精度が高く、R2は1に近いほど予測精度が高くなります。
				
					# 予測を実行する
predictions = model.predict(x_test)
predictions = scaler.inverse_transform(predictions)

# 二乗平均平方根誤差(RMSE): 0に近いほど良い
rmse = np.sqrt(np.mean(((predictions - y_test) ** 2)))
print(rmse)

# 決定係数(r2) : 1に近いほど良い
r2s = r2_score(y_test, predictions)
print(r2s)
				
			
blank
予測値と正解値をグラフで可視化してみます。茶色のラインが予測値、赤いラインが実際の株価(正解値)です。
				
					train = data[:training_data_len]
valid = data[training_data_len:]
valid['Predictions'] = predictions

plt.figure(figsize=(16,6))
plt.title('Model')
plt.xlabel('Date', fontsize=14)
plt.ylabel('Close Price USD ($)', fontsize=14)
plt.plot(train['Close'])
plt.plot(valid[['Close', 'Predictions']])
plt.legend(['Train', 'Val', 'Predictions'], loc='lower right')
plt.show()
				
			
blank

まとめ

Kerasなどのディープラーニングライブラリも進化しているので、時系列データを扱ってLSTMで予測をすることは意外に簡単にできるようになってきています。実際には、学習の工夫や精度の検証など細かい部分が必要になりますが大枠は変わりません。また、DataFrameやnumpy array、データ分割の扱いなど、機械学習を行う際は同じような実装パターンに出会うと思いますので、慣れていけば有益なスキルになると思います。

スーパーソフトウエアの採用情報

あなたが活躍できるフィールドと充実した育成環境があります

blank
blank