LSTMを使った時系列予想モデル - TensorFlow実装例

🎯 はじめに

競馬予想において、馬の過去のパフォーマンスの時系列変化を捉えることは非常に重要です。 本記事では、LSTM(Long Short-Term Memory)ネットワークを使用して、 馬の成長曲線や調子の波を分析し、高精度な予想モデルを構築する方法を解説します。

この記事で学べること

  • LSTMの基本概念と競馬への応用
  • TensorFlowでのLSTMネットワーク実装
  • 時系列データの前処理と特徴量作成
  • Attention機構を含む高度なアーキテクチャ
  • モデル評価と予想精度の向上手法

🧠 1. LSTMの基本概念

LSTMとは

LSTM(Long Short-Term Memory)は、従来のRNNの問題点である勾配消失問題を解決した 深層学習アーキテクチャです。競馬データにおいては以下の特徴を活用できます:

競馬データでのLSTMの利点

  • 長期記憶: 馬の長期的な成長曲線を記憶
  • 短期記憶: 最近の調子や状態変化を捉える
  • ゲート機構: 重要な情報を選択的に保持・更新
  • 時系列パターン: 季節要因や周期的な変動を学習

環境設定

# 必要なライブラリのインストール
pip install tensorflow pandas numpy matplotlib scikit-learn
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout
import pandas as pd
import numpy as np

print("TensorFlow version", tf.__version__)
print("GPU available", tf.config.list_physical_devices('GPU'))

📊 2. 時系列データの準備

データ読み込みと前処理

# 競馬データの読み込み
race_data = pd.read_csv('horse_race_history.csv')

# 日付型変換
race_data['race_date'] = pd.to_datetime(race_data['race_date'])

# 馬ごとに時系列順にソート
race_data = race_data.sort_values(['horse_id', 'race_date'])

print("データ形状", race_data.shape)
print("期間", race_data['race_date'].min(), "~", race_data['race_date'].max())

時系列特徴量の作成

def create_time_series_features(df):
    # 基本特徴量
    features = [
        'finish_position',    # 着順
        'horse_weight',       # 馬体重
        'jockey_weight',      # 騎手重量
        'odds',              # オッズ
        'race_distance',     # 距離
    ]

    # パフォーマンス指標の計算
    df['position_score'] = 1.0 / df['finish_position']  # 着順の逆数
    df['win_flag'] = (df['finish_position'] == 1).astype(int)

    # レース間隔
    df['days_since_last'] = df.groupby('horse_id')['race_date'].diff().dt.days
    df['days_since_last'] = df['days_since_last'].fillna(30)

    time_features = features + ['position_score', 'win_flag', 'days_since_last']
    return df, time_features

race_data, feature_columns = create_time_series_features(race_data)

シーケンスデータの作成

def create_sequences(df, feature_columns, sequence_length=5):
    sequences = []
    targets = []

    for horse_id in df['horse_id'].unique():
        horse_data = df[df['horse_id'] == horse_id].copy()

        if len(horse_data) < sequence_length + 1:
            continue

        # 正規化
        from sklearn.preprocessing import StandardScaler
        scaler = StandardScaler()
        horse_features = scaler.fit_transform(horse_data[feature_columns])

        # シーケンス作成
        for i in range(len(horse_features) - sequence_length):
            seq = horse_features[i:i+sequence_length]
            target = horse_data.iloc[i+sequence_length]['win_flag']

            sequences.append(seq)
            targets.append(target)

    return np.array(sequences), np.array(targets)

sequence_length = 5
X_sequences, y_sequences = create_sequences(race_data, feature_columns, sequence_length)

print("シーケンス数", len(X_sequences))
print("シーケンス形状", X_sequences.shape)

🤖 3. LSTMモデルの構築

基本的なLSTMモデル

from sklearn.model_selection import train_test_split

# データ分割
X_train, X_test, y_train, y_test = train_test_split(
    X_sequences, y_sequences, test_size=0.2, random_state=42
)

# LSTMモデルの構築
model = Sequential([
    LSTM(64, return_sequences=True, input_shape=(sequence_length, len(feature_columns))),
    Dropout(0.2),
    LSTM(32, return_sequences=False),
    Dropout(0.2),
    Dense(16, activation='relu'),
    Dense(1, activation='sigmoid')
])

model.compile(
    optimizer='adam',
    loss='binary_crossentropy',
    metrics=['accuracy']
)

print(model.summary())

モデルの訓練

from tensorflow.keras.callbacks import EarlyStopping

# コールバック設定
early_stopping = EarlyStopping(
    monitor='val_loss',
    patience=10,
    restore_best_weights=True
)

# 訓練
history = model.fit(
    X_train, y_train,
    validation_data=(X_test, y_test),
    epochs=100,
    batch_size=32,
    callbacks=[early_stopping],
    verbose=1
)

# 評価
from sklearn.metrics import accuracy_score
y_pred = (model.predict(X_test) > 0.5).astype(int)
accuracy = accuracy_score(y_test, y_pred)

print("LSTM精度", accuracy)

📈 4. モデル評価と実用化

学習曲線の可視化

import matplotlib.pyplot as plt

plt.figure(figsize=(12, 4))

plt.subplot(1, 2, 1)
plt.plot(history.history['loss'], label='Training Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.title('Model Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(history.history['accuracy'], label='Training Accuracy')
plt.plot(history.history['val_accuracy'], label='Validation Accuracy')
plt.title('Model Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()

plt.tight_layout()
plt.show()

実用的な予想システム

class HorseRacingLSTMPredictor:
    def __init__(self, model, scaler, feature_columns, sequence_length):
        self.model = model
        self.scaler = scaler
        self.feature_columns = feature_columns
        self.sequence_length = sequence_length

    def predict_horse_performance(self, horse_history):
        if len(horse_history) < self.sequence_length:
            return None

        # 最新のデータを取得
        recent_history = horse_history.tail(self.sequence_length)

        # 特徴量正規化
        features = self.scaler.transform(recent_history[self.feature_columns])
        sequence = features.reshape(1, self.sequence_length, -1)

        # 予測
        win_probability = self.model.predict(sequence)[0][0]
        return win_probability

# 予想システムのインスタンス化
predictor = HorseRacingLSTMPredictor(
    model=model,
    scaler=scaler,
    feature_columns=feature_columns,
    sequence_length=sequence_length
)

print("LSTM予想システムが準備完了しました!")

🎯 まとめ

本記事では、LSTMを使った競馬の時系列予想モデルについて詳しく解説しました。 特に重要なポイントは以下の通りです:

重要なポイント

  • 時系列の重要性: 馬の成長曲線や調子の波を捉える
  • LSTM構造: 長期・短期両方の記憶を活用
  • シーケンス長の調整: 適切な過去レース数の設定が重要
  • 特徴量エンジニアリング: 時系列特有の特徴量作成
  • 実用化: 予想システムとしての実装

技術的な発展可能性

  • Attention機構: より重要なタイムステップに注目
  • 多変量時系列: 複数馬の相互作用を考慮
  • 強化学習: 投資戦略の最適化
  • Transformer: より高度な時系列モデル

LSTMを基盤として、さらに高度な深層学習手法を組み合わせることで、 より精度の高い競馬予想システムを構築できるでしょう。