Bot 機械学習・データサイエンス

開発記録#121(2024/11/1)【MLbot開発】「LSTMとアンサンブル学習(バギングとブースティング)」

2024年11月1日

前回の記事に引き続き、今回も仮想通貨botの開発状況をまとめていきます。

今回は「LSTMとアンサンブル学習」というアプローチでMLbotを作ってみました。

Yodaka

このアプローチには様々な組み合わせがあるため、まずは「バギングとブースティング」を組み合わせる手法から始めてみました。

本記事では、このアプローチについて整理するととともにプログラムのプロトタイプの解説も行います。

解決したかったこと

・アンサンブル学習について理解する

・LSTMとアンサンブル学習を組み合わせることの優位性を知る

・LSTMとアンサンブル学習を組み合わせることの問題点を知る

・上記の問題点を踏まえて、それらの解決策を調べる

・MLbot開発の引き出しを増やす

アンサンブル学習とは

アンサンブル学習は、複数の学習モデル(通常は機械学習のモデル)を組み合わせて、一つの問題に対する予測性能を向上させる手法です。このアプローチの基本的な考え方は、「多数の意見は一つの意見よりも優れているかもしれない」というもので、複数のモデルがそれぞれ異なる予測を行い、それらの結果を統合して最終的な予測を行います。

アンサンブル学習の種類

アンサンブル学習には主に次の三つの方法があります。

  1. バギング(Bootstrap Aggregating)
    • 同じタイプのモデルを多数用意し、それぞれを少し異なる訓練データで学習させます(ブートストラップサンプリング)。それぞれのモデルの予測結果を平均化したり、多数決で最終結果を得ます。
    • 例: ランダムフォレストは多くの決定木をバギングの原理で組み合わせたモデルです。
  2. ブースティング
    • 複数の弱いモデル(単純なモデル)を順番に学習させ、各ステップで前のモデルが間違えた点に注目して性能を向上させます。最終的な予測は、これらのモデルの予測を重み付けして組み合わせます。
    • 例: AdaBoost、Gradient Boostingが有名です。
  3. スタッキング
    • 複数の異なるモデルを独立して学習させ、その各モデルの出力を新たな「メタモデル」の入力として使います。このメタモデルが、最終的な予測を行います。

アンサンブル学習のメリット

  • 精度の向上: 単一のモデルよりも精度の高い予測が可能です。
  • 過剰適合のリスク軽減: 複数のモデルを組み合わせることで、特定のデータに対して過剰に適合するリスクを低減できます。
  • 堅牢性: 異なる種類のデータに対するモデルの堅牢性が向上します。

アンサンブル学習のデメリット

  • 計算コスト: 複数のモデルを訓練するため、計算リソースや時間が多く必要です。
  • モデルの解釈性の低下: 複数のモデルを組み合わせるため、どのようにしてその予測が導かれたかが理解しにくくなることがあります。

アンサンブル学習は、これらの利点と欠点を考慮しながら適切に適用することが重要です。実際の問題においては、これらの手法を使うことで、より信頼性の高い予測モデルを構築することが可能になります。

バギングとブースティング

バギングとブースティングは、アンサンブル学習の手法で、複数のモデルを組み合わせて一つの強力な予測モデルを作る方法です。これらは特に予測精度を向上させたい時に使われます。それぞれの手法には特有の特徴があり、異なる問題に対して異なる効果を発揮します。

バギング(Bootstrap Aggregating)

バギングは、ランダムに生成された複数の訓練データセットを用いて複数のモデルを訓練し、それぞれの予測を平均化するか、多数決で決める方法です。ここで言う「ランダムに生成された訓練データセット」とは、もとの訓練データから重複を許してランダムにサンプルを選び出す(ブートストラップサンプリング)ことにより作成されます。バギングの目的は、モデルのバリアンス(モデルの予測の揺らぎ)を減少させることで、特に過剰適合を起こしやすい複雑なモデル(例えば、決定木や深層学習モデル)に有効です。

  • ランダムフォレストはバギングの一種で、複数の決定木を訓練し、各木の予測結果を平均化するか多数決により最終的な予測を行います。

ブースティング

ブースティングは、連続的にモデルを訓練し、前のモデルが間違えた部分に重点を置いて次のモデルを改善していく手法です。ブースティングでは、各モデルの訓練が順番に行われ、前のモデルの誤りを次のモデルが補正するように重み付けを行います。ブースティングはモデルのバイアス(システマティックな誤差)を減少させることを目的としており、単純なモデルが少しずつ複雑化される過程で、全体としては非常に強力な予測モデルが構築されます。

  • AdaBoost(アダブースト)は、誤った予測に対する重みを増やしながら次々と分類器を追加していく方法です。
  • Gradient Boosting(勾配ブースティング)は、前のモデルの残差(誤差)に対して新しいモデルを訓練し、これを繰り返すことで全体の予測能力を向上させます。

まとめ

  • バギングは、個々のモデルの予測を平均化することでバリアンスを減少させ、過剰適合を防ぎます。独立して訓練される複数のモデルから構成されます。
  • ブースティングは、連続的にモデルを改善していくことでバイアスを減少させ、一連の弱い学習者(分類器)を強い学習者に変えます。

これらの手法は、単一モデルよりも通常、より良い予測性能を提供しますが、適用する際は計算コストや過剰適合のリスクも考慮する必要があります。

アプローチの優位性

ビットコインの価格予測において、LSTM(Long Short-Term Memory)モデルとアンサンブル学習(特にバギングとブースティング)を組み合わせるアプローチは、予測精度の向上と堅牢なモデル構築の両方を目指す強力な戦略です。以下にその優位性を詳しく説明します。

LSTMの利点

  1. 時系列データの特徴把握: LSTMは時系列データの長期的な依存関係を捉える能力に優れており、ビットコインの価格変動のような金融時系列データの予測に適しています。これにより、過去の価格動向から未来の価格を推測する際に、短期的な変動だけでなく、長期的なトレンドも考慮することができます。
  2. ノイズへの耐性: LSTMはノイズの多いデータセットに対しても効果的に機能するため、市場の不確実性や外部ショックが頻繁に起こるビットコイン市場において、価格予測の精度を保つのに役立ちます。

アンサンブル学習の利点

  1. バギングによるバリアンスの削減:
    • バギングは複数の予測モデルを平均化することで、個々のモデルの過剰適合を防ぎます。これにより、LSTMモデルが特定の訓練データに対して過剰にフィットすることなく、より一般化された予測が可能になります。
    • モデルのバリアンスが減少することで、新しいデータや未知の市場条件に対しても安定したパフォーマンスが期待できます。
  2. ブースティングによるバイアスの削減:
    • ブースティングは弱い予測モデルを徐々に改善していくことで、全体としての予測誤差を減らします。初期のLSTMモデルが見逃した細かなパターンやトレンドを捉え、連続的にモデルを改善していくことが可能です。
    • ビットコイン市場のように動的で変動が激しい環境でも、ブースティングは予測モデルの適応性を高め、より正確な予測を行うことができます。

組み合わせによるシナジー

  • 堅牢性と精度の向上: LSTMの時系列データの特徴把握能力と、アンサンブル学習による過剰適合の抑制・予測誤差の削減が組み合わさることで、非常に堅牢で精度の高い予測モデルを構築することができます。
  • 予測の安定性: バギングとブースティングを適用することで、LSTMモデルの単独使用時に比べて、予測結果の安定性が向上します。これにより、投資戦略の立案やリスク管理の精度が高まります。

このように、ビットコインの価格予測においてLSTMとアンサンブル学習の組み合わせは、複雑で変動が大きい市場環境における予測課題に対して、高い予測精度とモデルの堅牢性をもたらす可能性があります。これにより、より信頼性の高いトレーディング戦略を開発することが可能になります。

プログラムのプロトタイプ

ビットコインの価格予測と基本的な売買ロジックを組み合わせたPythonコードを以下に示します。この例では、予測価格が現在価格より高い場合に購入し、低い場合に売却する単純な戦略を採用しています。実際のトレード実行部分はコメントアウトされています。

import ccxt
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense
from sklearn.ensemble import BaggingRegressor

# バイナンスからBTC/USDTの価格データを取得
exchange = ccxt.binance()
bars = exchange.fetch_ohlcv('BTC/USDT', timeframe='1h', limit=1000)
df = pd.DataFrame(bars, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')

# 特徴量とラベルの生成
window_size = 5
features = []
labels = []

for i in range(window_size, len(df)):
    features.append(df['close'].iloc[i-window_size:i].values)
    labels.append(df['close'].iloc[i])

features, labels = np.array(features), np.array(labels)
features = np.reshape(features, (features.shape[0], features.shape[1], 1))

# データセットの分割
X_train, X_test, y_train, y_test = train_test_split(features, labels, test_size=0.2, random_state=0)

# LSTMモデルの構築
model = Sequential([
    LSTM(50, return_sequences=True, input_shape=(window_size, 1)),
    LSTM(50),
    Dense(1)
])
model.compile(optimizer='adam', loss='mean_squared_error')

# モデルの訓練
model.fit(X_train, y_train, epochs=20, batch_size=32)

# バギングを適用
bagging_model = BaggingRegressor(base_estimator=model, n_estimators=10, random_state=0)
bagging_model.fit(X_train, y_train)

# 最新データで予測を生成
latest_features = np.array([df['close'].values[-window_size:]])
latest_features = np.reshape(latest_features, (latest_features.shape[0], latest_features.shape[1], 1))
predicted_price = bagging_model.predict(latest_features)[0]

# 現在の価格を取得
current_price = df['close'].iloc[-1]

# 売買ロジック
if predicted_price > current_price:
    print("Buy signal - Predicted price is higher than current price.")
    # exchange.create_market_buy_order('BTC/USDT', amount)
elif predicted_price < current_price:
    print("Sell signal - Predicted price is lower than current price.")
    # exchange.create_market_sell_order('BTC/USDT', amount)

print(f"Current Price: {current_price}, Predicted Price: {predicted_price}")

注意点

  • 実際にトレードを実行する前に、APIキーとシークレットをccxtに設定しておく必要があります。
  • トレード実行のコメントを解除する前に、amount(取引量)を適切に設定してください。
  • 実際のトレードにはリスク管理のためのさらに複雑な戦略が必要です。トレードの前に適切なリスク評価とテストを行うことをお勧めします。

このコードを基にして、実際のマーケット環境でのトレーディング戦略を開発することができます。

問題点と解決方法

制約のある個人開発者がLSTMとアンサンブル学習を組み合わせたビットコイン価格予測プログラムを効果的に運用するための改善案を以下に提案します。これらの改善策は、計算リソースの最適化、過剰適合の防止、そして実行コストの削減に焦点を当てています。

1. モデルの軽量化

モデル構造の簡素化

  • 小さなLSTMユニット数: LSTM層のユニット数を減少させることで、モデルの複雑性を下げ、訓練に要する計算資源を削減します。また、層の数も少なくしてシンプルな構造にします。
  • 畳み込みLSTMの利用: 時系列データの空間的特徴を捉えることができ、パラメータ数を削減できる畳み込みLSTM(ConvLSTM)を利用して、計算効率を向上させます。

2. アンサンブル戦略の最適化

バギングの代わりにモデル平均化

  • モデル平均化: 複数のLSTMモデルを独立して訓練し、それぞれの予測結果を平均化します。この方法はバギングに似ていますが、ランダムサンプリングを必要とせず、計算コストを下げることが可能です。

ブースティングの効率化

  • 軽量なブースティング: AdaBoostや軽量な勾配ブースティングフレームワーク(例えば、LightGBM)を使用し、計算負荷の低い弱学習器を順番に訓練します。これにより、LSTMとの組み合わせではなく、より単純な特徴ベースでブースティングを行い、計算資源の使用を最適化します。

3. データ管理と処理の改善

効率的なデータ処理

  • データの前処理の最適化: 不要なデータを排除し、必要最小限のデータセットに絞って処理します。例えば、価格データのみに焦点を当て、ボリュームやその他の指標は除外することができます。
  • 特徴選択: 相関分析や情報利得を用いて、最も予測に寄与する特徴を選択します。これにより、モデルの入力次元を減少させ、訓練時間を短縮します。

4. コスト効率の良い計算環境

クラウドサービスの利用

  • プリエンプティブVMやスポットインスタンスを利用した計算: AWSのスポットインスタンスやGoogle CloudのプリエンプティブVMなど、割引率の高い仮想マシンを利用して、計算コストを削減します。

5. ハイパーパラメータチューニングの自動化

自動ハイパーパラメータチューニング

  • ハイパーパラメータの最適化: OptunaやHyperoptのようなライブラリを使用して、モデルのハイパーパラメータを自動的に最適化します。これにより、手動での試行錯誤による時間とコストを削減し、最適なパラメータセットを迅速に特定できます。

これらの改善策は、個人開発者が限られたリソースを効率的に活用しながら、ビットコインの価格予測の精度を向上させるためのものです。技術的な難易度には差がありますが、これらの戦略を適切に組み合わせることで、計算資源と時間のコストを最小限に抑えつつ、効果的な予測モデルを構築することが可能です。

プログラムのプロトタイプ(修正版)

以下は、前述した改善提案に基づいて、ビットコインの価格予測プログラムを修正したコードです。主な変更点は、モデルの軽量化、計算コストの削減、およびデータ処理の最適化を目的としています。

import ccxt
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout
from tensorflow.keras.optimizers import Adam

# バイナンスからBTC/USDTの価格データを取得
exchange = ccxt.binance()
bars = exchange.fetch_ohlcv('BTC/USDT', timeframe='1h', limit=1000)
df = pd.DataFrame(bars, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')

# 特徴量とラベルの生成
window_size = 5
features = []
labels = []

for i in range(window_size, len(df)):
    features.append(df['close'].iloc[i-window_size:i].values)
    labels.append(df['close'].iloc[i])

features, labels = np.array(features), np.array(labels)
features = np.reshape(features, (features.shape[0], features.shape[1], 1))

# データセットの分割
X_train, X_test, y_train, y_test = train_test_split(features, labels, test_size=0.2, random_state=0)

# LSTMモデルの構築
model = Sequential([
    LSTM(20, return_sequences=True, input_shape=(window_size, 1)),  # ユニット数を減らす
    Dropout(0.2),  # ドロップアウト層を追加
    LSTM(20),
    Dropout(0.2),
    Dense(1)
])
model.compile(optimizer=Adam(learning_rate=0.01), loss='mean_squared_error')  # 学習率を調整

# モデルの訓練
model.fit(X_train, y_train, epochs=10, batch_size=64)  # エポック数とバッチサイズを調整

# 最新データで予測を生成
latest_features = np.array([df['close'].values[-window_size:]])
latest_features = np.reshape(latest_features, (latest_features.shape[0], latest_features.shape[1], 1))
predicted_price = model.predict(latest_features)[0][0]  # バギングの適用を省略し、単一のLSTMモデルを使用

# 現在の価格を取得
current_price = df['close'].iloc[-1]

# 売買ロジック
if predicted_price > current_price:
    print("Buy signal - Predicted price is higher than current price.")
    # exchange.create_market_buy_order('BTC/USDT', amount)
elif predicted_price < current_price:
    print("Sell signal - Predicted price is lower than current price.")
    # exchange.create_market_sell_order('BTC/USDT', amount)

print(f"Current Price: {current_price}, Predicted Price: {predicted_price}")

コード変更の解説

  1. モデルの軽量化:LSTMユニット数を20に削減し、処理を高速化。
  2. ドロップアウト層の追加:過剰適合を防ぐために、各LSTM層後にドロップアウト層を挿入。
  3. 学習パラメータの調整:学習率を上げ、エポック数とバッチサイズを調整して、より迅速な収束を目指す。
  4. バギングの削除:計算コストを削減するためにアンサンブル手法のバギングを省略し、単一モデルの性能に依存。

この修正により、計算リソースが限られた環境でも効率的に実行可能で、過剰適合のリスクも低減されるプログラムを実現します。

プログラムの解説

上記のコードは、ビットコインの価格予測モデルを構築し、その予測を基に取引シグナルを生成するプログラムです。以下にコードの主要な部分ごとの詳細な解説を提供します。

ライブラリのインポート

import ccxt
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout
from tensorflow.keras.optimizers import Adam

ccxt: 暗号通貨取引所とのインターフェースを提供するライブラリで、価格データの取得などに使用します。pandas: データ分析を支援するライブラリで、データの操作や分析に使用します。numpy: 数値計算を効率的に行うためのライブラリで、配列操作などに使用します。train_test_split: scikit-learnのモジュールで、データを訓練セットとテストセットに分割するのに使用します。Sequential, LSTM, Dense, Dropout: TensorFlowとKerasのモジュールで、ニューラルネットワークの構築に使用します。Adam: オプティマイザーで、モデルの訓練時に使用されるアルゴリズムです。

データの取得と前処理

exchange = ccxt.binance()
bars = exchange.fetch_ohlcv('BTC/USDT', timeframe='1h', limit=1000)
df = pd.DataFrame(bars, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')

ccxt.binance(): Binance取引所のAPIを初期化します。exchange.fetch_ohlcv(): 指定された通貨ペア(ここではBTC/USDT)のOHLCVデータ(始値、高値、安値、終値、ボリューム)を取得します。pd.DataFrame(): 取得したデータをpandas DataFrameに変換します。timestampの変換: タイムスタンプをミリ秒から適切な日時形式に変換します。

特徴量とラベルの生成

window_size = 5
features = []
labels = []

for i in range(window_size, len(df)):
    features.append(df['close'].iloc[i-window_size:i].values)
    labels.append(df['close'].iloc[i])

features, labels = np.array(features), np.array(labels)
features = np.reshape(features, (features.shape[0], features.shape[1], 1))

window_size: 価格データのウィンドウサイズを定義します。ここでは5時間分のデータを一つの特徴セットとして使用します。特徴量とラベルの生成: クローズ価格を使用して、過去5時間の価格データを特徴量として、次の時間の価格をラベル(予測対象)として集めます。

データセットの分割

X_train, X_test, y_train, y_test = train_test_split(features, labels, test_size=0.2, random_state=0)

train_test_split(): 特徴量とラベルを訓練データとテストデータに分割します。ここでは20%をテストデータとしています。

LSTMモデルの構築と訓練

model = Sequential([
    LSTM(20, return_sequences=True, input_shape=(window_size, 1)),
    Dropout(0.2),
    LSTM(20),
    Dropout(0.2),
    Dense(1)
])
model.compile(optimizer=Adam(learning_rate=0.01), loss='mean_squared_error')
model.fit(X_train, y_train, epochs=10, batch_size=64)

モデルの構築: 2層のLSTMと各層に続くドロップアウト層、出力層としての全結合層(Dense)から成ります。コンパイルと訓練: モデルをコンパイルし、訓練データで訓練します。学習率、損失関数、エポック数、バッチサイズを設定しています。

予測と取引シグナルの生成

latest_features = np.array([df['close'].values[-window_size:]])
latest_features = np.reshape(latest_features, (latest_features.shape[0], latest_features.shape[1], 1))
predicted_price = model.predict(latest_features)[0][0]

current_price = df['close'].iloc[-1]

if predicted_price > current_price:
    print("Buy signal - Predicted price is higher than current price.")
elif predicted_price < current_price:
    print("Sell signal - Predicted price is lower than current price.")
  • 予測: 最新のデータを用いて次の時間の価格を予測します。
  • 取引シグナル: 予測価格が現在価格より高ければ買い、低ければ売りのシグナルを出力します。

このコードは、リアルタイムの市場データに基づいてビットコインの価格動向を予測し、その予測を基にして取引シグナルを生成することを目的としています。

考察(11/2追記)

LSTMとアンサンブル学習(特にバギングやブースティング)の組み合わせに関するより深い専門的な考察です。ここでは、これらの技術の組み合わせが持つ潜在的な問題点や課題を、より詳細に分析します。

LSTMの特性と課題

  1. 記憶能力と長期依存問題
    • LSTMは長期依存問題(long-term dependencies)を解決するために設計されたが、実際には非常に長いシーケンスに対しては依然として効果的に機能するかどうかが問題となる。特に、金融市場データのようにノイズが多く、外的ショックによる急激な変動が頻繁にある場合、過去のデータが現在に直接的な影響を与えるとは限らない。
  2. 過剰適合とモデルの複雑性
    • LSTMはパラメータ数が多く、訓練が不十分であると過剰適合を起こしやすい。特に少ないデータポイントを用いる場合、そのリスクは増大する。また、モデルの複雑性が高いために、予測が不安定になりがちであり、新しい市場状況に対する適応性が低下することがある。

アンサンブル学習の特性と課題

  1. バギングとブースティング
    • バギングは独立して訓練されたモデルの予測を平均化することで、モデルのバリアンスを減少させるが、LSTMのような複雑なモデルでは計算コストが著しく高くなる。さらに、バギングはモデル間で独立性が求められるが、LSTMモデルは時系列データの内在的なパターンを学習するため、本質的に類似した予測を行いがちであり、バギングの効果が低減される可能性がある。
    • ブースティングは誤分類されたインスタンスに焦点を当てることで、連続的にモデルの性能を改善していく。しかし、LSTMと組み合わせると、初期の予測誤差が次々と積み重なることで、最終的なモデルが過剰適合を引き起こすリスクがある。

深層学習とアンサンブル学習の相互作用

  • 相互作用の評価
    • LSTMの動的な特性とアンサンブル学習の静的なアプローチを組み合わせることで、理論的にはモデルの堅牢性が向上する可能性がある。しかし、実際にはこれらのアプローチが相互に有効に機能するかどうかは、具体的な実装とパラメータ調整に大きく依存する。
    • 組み合わせの効果を最大化するには、個々のLSTMモデルの設定、訓練データの質と量、そしてアンサンブル手法の適切な選択が重要であり、これらが不適切であると予測性能がむしろ低下する可能性もある。

改善策と推奨事項

  1. データの前処理と特徴工学
    • ノイズの多いデータや外れ値の適切な処理、重要な特徴の選定と抽出を行うことで、LSTMモデルの学習効率と予測精度を向上させる。
  2. モデルの単純化と正則化
    • LSTMモデルの層数やユニット数を減らす、正則化手法(L1、L2正則化など)を導入することで、過剰適合を防ぎ、計算コストを抑える。
  3. 適切なアンサンブル戦略の選択
    • データや問題の特性に応じてバギングやブースティングの適用を検討し、それぞれのアンサンブル手法が持つ長所と短所を理解し、効果的に利用する。

まとめ

今回は「LSTMとアンサンブル学習(バギングとブースティング)」についてまとめました。

機械学習系のbotの引き出しがだいぶ増えてきたので、ここから一つ一つの手法をもっと掘り下げて強いbot作りに繋げたいです。

Yodaka

複数の方法を組み合わせることでより良いものができるかどうかも併せて検証していきます。

今後もこの調子で開発の状況を発信していきます。

-Bot, 機械学習・データサイエンス