AIモデルを構築して日経平均株価を予測してみた

今回は **LSTM（Long Short-Term Memory）** というAIの機械学習モデルを使って、シンプルに2023年〜2024年の日経平均株価の終値を予測してみました。
正直、LSTMモデルとか聞くと「難しそう…」って感じるかもしれませんが、今回は細かい理論や数式は置いといて、「とりあえずこんな感じでAIモデルで株価予測ってできるんだ！」っていう雰囲気を紹介します。


----

ちなみにLSTMモデルを使って感情分析をした記事もあるので、
気になる方はこちらもぜひご覧ください！

https://qiita.com/kinopy513/items/7f72a1f86db1ee131e41

----


## 本記事のターゲット

-   機械学習に関する基礎的な知識がなんとなくあるくらいの機械学習初心者（pandasやnumpyなど基礎知識は解説しません）
-   時系列データでの予測に興味があるけど、LSTMを使った分析はまだやったことがない人
-   株価データや金融データなど未来予測に興味がある人
-   これからデータ分析を学習したくて、とりあえずは機械学習の雰囲気を感じてみたい人

※今回は特徴量エンジニアリングなどは行わずなるべくシンプルなモデルにしてるので、ある程度機械学習やっているKagglerなどは物足りないと思います


## 最終ゴール

2010年から2022年までの日経平均株価のデータを使って、過去60日の終値データをもとに翌日分の株価を予測するAIモデルを作成します。

つまり、2023年1月1日の株価を予測するなら、その前60日間(2022年11月、12月)のデータを基に予測するって流れです。そして、2023年〜2024年の株価を予測した結果をこんな感じのグラフにしてみます。


![2023-2024_nikkei_close.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3843712/599f1482-1ab2-3512-e927-1b2861f09532.png)


## モデル精度について

今回は「とにかくシンプルに！」を意識して、終値だけを使った簡単なモデルで予測しています。そのため、正直精度はあまり突き詰めていません。ただ、それでも上のグラフを見てもらうと、予測（赤のライン）と実際の株価（青のライン）がそれなりにリンクしているのがわかると思います。

<br />

最初に、最低限の用語を簡単に紹介します。


## 機械学習とは

機械学習とは、ざっくり言うと「コンピュータがデータをもとにパターンを学習して、予測や判断を行う技術」です。
データ分析をしたことがない人にとってはあまり馴染みのない用語かもしれないですが、例えば、スマートフォンの顔認識やSiriやAlexaなどの音声認識、ChatGPTなどのチャットツール、自動運転などに利用されており、実は結構身近に溢れています。


## LSTMとは

今回使うLSTMモデルは機械学習で使われる手法の一つで、時系列データに強いのが特徴です。今回の株価予測のように、過去のデータをもとに未来のデータを予測するのに適しています。
  
<br />

では早速分析を始めていきましょう。


## 1. 必要なライブラリのインストール

まずは、データを取得したり処理したり、モデルを訓練するために必要なライブラリをインストールします。今回は予測がメインなので、詳しくは省きます。
pipを使っている方はこちらを参考にしてください。

```bash
pip install yfinance numpy pandas matplotlib seaborn scikit-learn tensorflow
```

- **yfinance**: 今回は株価データ取得のためにYahoo Financeのライブラリを使用します

- **numpy**: 数値計算のためのライブラリ

- **pandas**: データ処理のためのライブラリ

- **matplotlib**: グラフ描画用のライブラリ

- **seaborn**: グラフ描画用のライブラリ。matplotlibと一緒に使うことが多いです。

- **scikit-learn**: データの前処理や評価に使うライブラリ

- **tensorflow**: LSTMモデルを構築するためのライブラリ

  
## 2. データの取得

今回は **Yahoo Finance** から、2010年〜2024年までの日経平均株価データを取得します。

```python
import pandas as pd
import numpy as np
import yfinance as yf

# 2010年1月1日〜2024年9月30日までの日経平均株価のデータを取得
# 今回は日経平均株価を使用するため`N225`を使いますが、ここを好きな株の銘柄コードに変えれば他の銘柄の株価分析も可能です
nikkei = yf.download("^N225", start="2010-01-01", end="2024-09-30")

# 訓練データ（2010年〜2022年）とテストデータ（2023年〜2024年）に分割
train_data = nikkei.loc['2010-01-01':'2022-12-31']
test_data = nikkei.loc['2023-01-01':'2024-09-30']
```

今回は、2010年1月1日から2024年9月30日までの日経平均株価データを取得し、2010年から2022年までを訓練データ（こちらのみをAIの学習に使用する）、2023年から2024年までをテストデータ（こちらは後で予測した値と答え合わせをする）に分割しています。

ちなみに`print(train_data.head())` で先頭5行のみ確認すると、ちゃんとデータが取得できていることが確認できます。

```bash
Price                         Adj Close         Close          High  \
Ticker                            ^N225         ^N225         ^N225   
Date                                                                  
2010-01-04 00:00:00+00:00  10654.790039  10654.790039  10694.490234   
2010-01-05 00:00:00+00:00  10681.830078  10681.830078  10791.040039   
2010-01-06 00:00:00+00:00  10731.450195  10731.450195  10768.610352   
2010-01-07 00:00:00+00:00  10681.660156  10681.660156  10774.000000   
2010-01-08 00:00:00+00:00  10798.320312  10798.320312  10816.450195   

Price                               Low          Open     Volume  
Ticker                            ^N225         ^N225      ^N225  
Date                                                              
2010-01-04 00:00:00+00:00  10608.139648  10609.339844  104400000  
2010-01-05 00:00:00+00:00  10655.570312  10719.440430  166200000  
2010-01-06 00:00:00+00:00  10661.169922  10709.549805  181800000  
2010-01-07 00:00:00+00:00  10636.669922  10742.750000  182600000  
2010-01-08 00:00:00+00:00  10677.559570  10743.299805  211800000
```

`shape` を使うと行数、列数を取得できます。

```python
train_data.shape
```

```bash
(3179, 6)
```

```python
test_data.shape
```

```bash
(427, 6)
```

訓練用のデータは3179行 / 6列、予測用のデータは427行 / 6列なので、3179日分の株価データを使ってAIモデルを作り、それを使って427日分の株価データを予測します。

データの型も見ておきます。`dtypes` でデータ型を確認できます。
```python
train_data.dtypes
```

```bash
Price      Ticker
Adj Close  ^N225     float64
Close      ^N225     float64
High       ^N225     float64
Low        ^N225     float64
Open       ^N225     float64
Volume     ^N225       int64
dtype: object
```
今回はCloseのみを指標（特徴量）として用いるので、Closeがfloat64（浮動小数点）と確認すればOKです。



## 3. データの可視化

ここで、取得したデータの中から2010年〜2022年の終値（学習用データ）をグラフで可視化してみます。

```python
import matplotlib.pyplot as plt
import seaborn as sns

# グラフのスタイル指定（日本語表示に対応させる）
sns.set(style="darkgrid", font="Hiragino Maru Gothic Pro")
plt.figure(figsize=(15, 8))

# 終値データをプロット
plt.plot(train_data.index, train_data["Close"], label="日経平均株価終値", color="blue")
plt.title("2010年〜2022年の日経平均株価", fontsize=16)
plt.xlabel("日付", fontsize=12)
plt.ylabel("終値", fontsize=12)

plt.show()
```


![2010-2022_nikkei_close.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3843712/cf741cd9-ac67-540e-9d1c-d25c99d6c323.png)


## 4. データの標準化

LSTMにデータを入力する前に、データのスケーリング（標準化）します。データ加工をすることで、モデルが効率的に学習できるようになります。

```python
from sklearn.preprocessing import MinMaxScaler

# 終値データを0〜1の範囲にスケーリング
scaler = MinMaxScaler(feature_range=(0, 1))
scaled_train_data = scaler.fit_transform(train_data[["Close"]].values.reshape(-1, 1))
scaled_test_data = scaler.fit_transform(test_data[["Close"]].values.reshape(-1, 1))
```

ここでは、`MinMaxScaler`を使ってデータを0〜1の範囲にスケーリングしています。これにより、LSTMモデルがより速く効率的に学習できるようになります。

ちなみに、`scaled_train_data` の中身を確認すると先ほどの`train_data` のCloseと違い、0〜1の範囲に値がおさまっているのがわかります。

```bash
array([[0.11082942],
       [0.11203066],
       [0.11423501],
       ...,
       [0.8076596 ],
       [0.79669429],
       [0.79673117]])
```


## 5. LSTMモデルの構築

次に、LSTMモデルを構築します。
今回は、過去60日分の終値データを使って翌日の終値を予測するモデルを作ります。

```python
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense

# 過去60日間のデータを使って次の日を予測するデータセットを作成
def create_dataset(data, time_step=60):
    X, y = [], []
    
    for i in range(time_step, len(data)):
        X.append(data[i-time_step:i, 0])
        y.append(data[i, 0])
    return np.array(X), np.array(y)

# 訓練データの作成
X_train, y_train = create_dataset(scaled_train_data)
X_train = np.reshape(X_train, (X_train.shape[0], X_train.shape[1], 1))

# テストデータの作成
X_test, y_test = create_dataset(scaled_test_data)
X_test = np.reshape(X_test, (X_test.shape[0], X_test.shape[1], 1))

# LSTMモデルの定義
model = Sequential()
model.add(LSTM(units=50, return_sequences=True, input_shape=(X_train.shape[1], 1)))
model.add(LSTM(units=50))
model.add(Dense(1))

# モデルのコンパイルとトレーニング
model.compile(optimizer='adam', loss='mean_squared_error')
model.fit(X_train, y_train, epochs=10, batch_size=32)
```

今回のLSTMモデルは、`epochs=10`で10回の訓練を行い、株価予測の精度を高めます。この数が大きいとある程度までは精度が向上しますが、その代わりに多くすればするほど計算量が多くなるため処理完了までに時間がかかるのと、過学習になる恐れがあります。
過学習については、機械学習と切っては切り離せないものなので、知らない方はぜひ調べてみてください。
  

## 6. 予測

モデルができたら、テストデータ(2023年〜2024年)で予測してみます。
予測結果を実際の株価と比較します。

```python
# 予測
predicted_stock_price = model.predict(X_test)

# スケールを元に戻す
predicted_stock_price = scaler.inverse_transform(predicted_stock_price)
actual_stock_price = scaler.inverse_transform(scaled_test_data[60:])
```


## 7. グラフで可視化
予測した株価を実際の株価と比較します。
  
```python
# 予測結果をグラフで表示
plt.figure(figsize=(10, 6))
plt.plot(test_data.index[60:], actual_stock_price, color="blue", label="実際の日経平均株価")
plt.plot(test_data.index[60:], predicted_stock_price, color="red", label="予測の日経平均株価")

plt.title("2023年〜2024年の日経平均株価の予測値と実測値", fontsize=16)
plt.xlabel("日付", fontsize=12)
plt.ylabel("終値", fontsize=12)
plt.legend()

plt.show()
```


![2023-2024_nikkei_close.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3843712/d43d06de-a583-dc54-b162-4cdbcb6d3e99.png)


予測結果と実際の株価を同じグラフにプロットしています。青い線が実際の株価、赤い線がモデルの予測結果です。これにより、LSTMモデルがどれだけ正確に株価を予測しているかが視覚的に確認できますが、おおまかな動きは大体捉えられていることがわかります。
  

## 8. 評価

最後にモデルを評価をします。
モデルの予測がどれだけ実際の株価に近いかを確認するために、今回はRMSE（平均二乗平方根誤差）とMAE（平均絶対誤差）を計算します。

```python
from sklearn.metrics import mean_squared_error, mean_absolute_error
import math

# RMSE（Root Mean Squared Error）を計算
rmse = math.sqrt(mean_squared_error(actual_stock_price, predicted_stock_price))
print(f'RMSE: {rmse}')

# MAE（Mean Absolute Error）を計算
mae = mean_absolute_error(actual_stock_price, predicted_stock_price)
print(f'MAE: {mae}')
```

```bash
# 評価結果
RMSE: 967.2722648699537
MAE: 736.8586405824251
```

今回は、RMSEとMAEという指標を使いましたが、評価の指標はこれ以外にもたくさんあるので、分析手法や分析の目的（ゴール）によって必要な指標を用います。
-   **RMSE**: 予測値と実際の値の誤差の二乗平均平方根。値が小さいほどモデルの予測が正確であることを示します。
-   **MAE**: 予測値と実際の値の誤差の平均絶対値。こちらも値が小さいほどモデルの精度が高いことを示します。

今回の場合、予測値は実測値と比べて平均±967円ないし平均±736円ずれていることを示しています。このずれが大きいか小さいかは目的などにより評価が異なりますが、一般的にはここからモデルを改善していきこの指標をなるべく小さくしていくことで、精度をより高めていきます。


## まとめ

今回の記事では、LSTMを使って日経平均株価の終値を予測しました。
機械学習を用いた分析はどうでしたか？精度が高いと感じた人もいれば、まだまだだなと思った人もいると思いますが、今回の分析はあくまで機械学習の第一歩です。ここからどんどん工夫して、精度を上げていくのがデータ分析の楽しいところです！

もっと精度を上げたければ、例えば今回使わなかった **高値、安値、取引量**などの他の特徴量を使って予測したり、どの特徴量が株価に大きな影響を与えるかを特徴量エンジニアリングといった手法で分析していく必要があります。また、モデルの訓練データやパラメータを調整することで、予測精度を向上させていきます。

まだまだ自分もデータ分析について日々勉強中ですが、これからも引き続き機械学習を突き詰めていきたいと思います。


## 採用拡大中！
アシストエンジニアリングでは一緒に働くフロントエンド、バックエンドのエンジニア仲間を大募集しています！

少しでも興味ある方は、カジュアル面談からでもぜひお気軽にお話ししましょう！

お問い合わせはこちらから↓
[https://official.assisteng.co.jp/contact/](https://official.assisteng.co.jp/contact/)