Bot

開発記録#118(2024/10/22)MLbot開発「PPOとLSTM」

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

PPOとは

PPO(Proximal Policy Optimization)は、強化学習のアルゴリズムの一つであり、特に連続的なアクションスペースを持つ問題に適用されることが多いポリシー勾配法の一種です。2017年にOpenAIによって導入されたこの手法は、そのシンプルさと強力なパフォーマンスで多くの強化学習タスクに広く利用されています。

PPOの概念

PPOは、トラストリージョンポリシーオプティマイゼーション(TRPO)のアイデアを継承しながら、計算上の複雑さを削減し、実装を容易にすることを目的としています。TRPOでは、ポリシー更新を安全に行うために、新しいポリシーと古いポリシーの間のカルバック・ライブラー発散(KL発散)を制限します。一方、PPOはこの発散を直接制限する代わりに、より簡単に実装可能な方法でポリシーの更新幅を制限します。

PPOの主な特徴

  1. クリッピングされた目的関数:
    • PPOは、ポリシーの更新を制限するためにクリッピング手法を導入します。具体的には、ポリシー比(新旧ポリシーの比率)がある閾値(例えば、0.8から1.2)の範囲内でのみ、目的関数を最大化します。これにより、ポリシーの更新が急激に変化するのを防ぎます。
  2. 複数のエポックでのバッチ更新:
    • PPOでは、収集したデータを複数のエポックにわたって再利用することが推奨されます。これにより、限られたデータからより多くの学習を行うことができ、サンプル効率が向上します。
  3. アクタークリティックアーキテクチャ:
    • PPOはアクタークリティック方式を採用しており、アクター(ポリシー)ネットワークとクリティック(価値)ネットワークを同時に学習します。これにより、アクターのポリシー改善とクリティックの価値推定が同時に行われるため、学習プロセスが安定します。

システム開発におけるPPOの実装の考慮事項

  • 計算資源: PPOは比較的計算コストが高いため、GPUやTPUなどのハードウェアアクセラレーションを利用することが望ましいです。
  • ハイパーパラメータの調整: 学習率、クリッピング範囲、エポック数、バッチサイズなど、PPOの性能に大きく影響するハイパーパラメータの調整が必要です。
  • 実装ツール: TensorFlowやPyTorchなどの現代的なディープラーニングフレームワークを使用すると、PPOの実装が容易になります。これらのフレームワークは自動微分機能を提供し、複雑な勾配計算を抽象化します。
  • デバッグと最適化: 強化学習のアルゴリズムはデバッグが困難であることが多いため、適切なロギングとモニタリングが必要です。特に、報酬の設計や状態表現の適切さが学習成果に大きく影響します。

PPOは、その強力な性能と柔軟性により、自動運転車、ロボット制御、ゲームAIなど多岐にわたる分野での応用が期待されています。特にシステムトレーダーにとっては、市場のダイナミクスを学習し、適応する能力が特に有用です。

補足1

連続的なアクションスペースとは、エージェントが取りうる行動が連続値で表される環境を指します。この場合、エージェントの決定が離散的な選択肢から選ばれるのではなく、一定の範囲内の任意の値を取ることができます。

連続的なアクションスペースの例

  1. ロボティクス:
    • ロボットの関節を動かす場合、それぞれの関節が取りうる角度が連続的な範囲を持ちます。たとえば、ロボットアームの関節角度を-90度から+90度まで連続的に変更することができる場合、そのアクションスペースは連続的です。
  2. 自動運転車:
    • ステアリングの角度を決定する場合、ステアリングの角度は通常、連続的な範囲内で変更可能です。例えば、-30度から+30度までの範囲でステアリングを調整できる場合、これも連続的なアクションスペースになります。
  3. 財務取引:
    • 投資の決定である投資額の調整も連続的なアクションスペースの一例です。たとえば、可能な投資額が0ドルから100ドルまで連続的に調整できる場合、その行動は連続的なアクションスペースを形成します。

連続的なアクションスペースにおける強化学習の課題

連続的なアクションスペースは、離散的なアクションスペースに比べていくつかの課題を持ちます。これは、アクションの選択肢が無限にあるため、どのアクションが最適かを直接学習することがより困難であるためです。

  • 学習の複雑性: 連続的なアクションの候補が無限に存在するため、どのアクションを試すべきかを決定することが難しくなります。これにより、学習プロセスが離散的なケースに比べて遅くなることがあります。
  • 探索の困難さ: 適切な探索戦略を設計することがより難しくなります。エージェントは連続的な範囲内で有効なアクションを見つけ出すために、効率的な探索アルゴリズムが必要です。

解決策

  • ポリシーベースのアプローチ: ポリシーベースの強化学習アルゴリズム(例えばPPO)は、ポリシー自体を直接最適化することで、連続的なアクションスペースで効果的に動作します。
  • アクタークリティック法: アクターが連続的なアクションを選択し、クリティックがそのアクションの価値を評価することで、連続的なアクションスペースにおけるポリシーを効果的に学習することができます。
  • 決定論的ポリシーグラディエント法(DPG)や深層決定論的ポリシーグラディエント法(DDPG): これらは連続的なアクションスペースに特化したアルゴリズムで、アクションの選択を最適化するために決定論的なアプローチを採用しています。

連続的なアクションスペースの扱いは、その複雑性からアルゴリズム選定やパラメータ調整において高度な技術的理解を要求しますが、適切に実装されれば多くの現実世界の問題に対して強力な解決策を提供することができます。

補足2

PPO(Proximal Policy Optimization)とDDPG(Deep Deterministic Policy Gradient)は両方とも強化学習アルゴリズムであり、特に連続的なアクションスペースに適用される点で類似していますが、それぞれのアプローチと主要な特性には重要な違いがあります。

類似点

  1. 連続アクションスペースの適用:
    • 両アルゴリズムとも連続的なアクションスペースに適用されるため、ロボティクスや自動運転車など、実世界の連続的な決定が必要なタスクに使用されます。
  2. アクタークリティックフレームワーク:
    • PPOとDDPGはアクタークリティック方式を採用しています。アクターがアクションを提案し、クリティックがそのアクションの価値を評価します。

相違点

  1. ポリシー更新の方法:
    • PPO: クリッピングされた目的関数を用いて、ポリシーが大きく変化することを防ぎます。これにより、学習中のポリシーの安定性が保たれ、効率的な学習が可能となります。
    • DDPG: 決定論的ポリシーグラディエントを使用してポリシーを更新します。これは、環境からの各ステップのサンプルを使用して、アクターネットワークを直接最適化する方法です。
  2. 探索戦略:
    • PPO: 確率的ポリシーを採用し、アクションを選択する際に確率的な挙動を使います。これにより自然な探索が行われるため、追加の探索メカニズムが必要ありません。
    • DDPG: 決定論的ポリシーを用いるため、探索を別途行う必要があります。通常、探索のためにオフポリシー学習時にノイズ(例: Ornstein-Uhlenbeckノイズ)をアクションに加える方法が採用されます。
  3. サンプル効率:
    • PPO: オンポリシー学習方式を採用しているため、新しいポリシーに基づくデータを継続的に取得し、学習に使用する必要があります。これはサンプル効率が低いが、更新が安定しやすいという特性があります。
    • DDPG: オフポリシー学習方式を採用しているため、過去の経験をリプレイバッファから再利用することができ、サンプル効率が良いですが、学習の不安定さが課題となり得ます。

結論

PPOとDDPGは、それぞれが持つ独自の特性によって異なるシナリオでの使用が推奨されます。PPOは学習の安定性が求められる場合に有効であり、DDPGはサンプル効率を重視する場合に適しています。選択するアルゴリズムは、特定のタスクの要件と、実装の複雑さ、必要な計算資源、期待される学習速度によって異なるでしょう。

補足3

機械学習、特に強化学習の文脈で使用される「エージェント」は、学習するシステムまたはプログラムのことを指します。このエージェントは、ある環境内で特定のタスクを遂行するために行動を選択し、それにより得られる結果(通常は報酬として表される)を基に学習していきます。つまり、エージェントは環境との相互作用を通じて、どのような行動が最も有益な結果をもたらすかを学習するシステムです。

エージェントの役割

  1. 行動の選択: エージェントは、与えられた状況(環境の現在の状態)に基づいて、何をするかを決めます。これは、ロボットが次にどの方向に進むかを決めることや、オンラインゲームで次の手を選ぶことなどが該当します。
  2. 環境の観察: エージェントは環境から情報を受け取り、その情報を使用して現在の状況を理解します。これには、カメラからの映像データやセンサーからの数値データなどが含まれることがあります。
  3. 報酬の最大化: エージェントの目標は、通常、行動を通じて得られる報酬の合計を最大化することです。報酬は、エージェントが目標にどれだけ近づいたかを示す指標であり、正の報酬は望ましい結果を、負の報酬は望ましくない結果を示します。
  4. 学習と適応: エージェントは経験を積むことで学習し、より良い行動決定を行うようになります。これは、試行錯誤や以前の経験から得た知識を活用することで達成されます。

エージェントの例

  • 自動運転車: 自動運転車はエージェントとして機能し、周囲の環境をセンサーで観察し、安全に運転するための最適な行動(ステアリングの角度、アクセルの踏み方、ブレーキの使用など)を学習します。
  • ビデオゲームのAI: ゲーム内のキャラクターはエージェントとして動作し、プレイヤーまたは他のAIとの相互作用を通じて最適な戦略を学習します。
  • 株式取引アルゴリズム: 株式市場での取引を自動化するアルゴリズムもエージェントの一種です。これは市場のデータを分析し、最も利益をもたらす可能性のある取引を決定します。

結論

強化学習におけるエージェントは、与えられた環境内で自らのパフォーマンスを最大化する方法を自動で学習するプログラムです。この概念は、複雑な問題解決や意思決定タスクにおいて人間の介入を減らすことを目指しています。

優位性・問題点

PPOとLSTMを組み合わせて仮想通貨の自動取引プログラムを作成する場合、以下のような優位性が期待できますが、それに伴いいくつかの問題点も発生する可能性があります。

優位性

  1. 時間依存性の捉え方:
    • LSTMは長短期記憶を利用して時系列データの時間的依存関係を捉えることができます。これにより、市場のトレンドや周期的なパターンを理解し、将来の価格動向をより正確に予測することが可能になります。
  2. ポリシーの安定化:
    • PPOはポリシー更新時の安定性に優れており、トレーニング中の過大なポリシー変更を防ぎます。これにより、学習プロセスがより安定し、効率的な収束が期待できます。
  3. サンプル効率の向上:
    • PPOはサンプル効率が良いアルゴリズムとして知られています。これは、過去の経験を効果的に再利用することにより、必要な学習データの量を減少させることができるため、コストと時間を節約することができます。

問題点と技術的なアプローチ

  1. 計算コスト:
    • LSTMは計算コストが高いモデルです。大規模なデータや複数の隠れ層を持つネットワークでは、リソースの消費が問題になることがあります。
    • アプローチ: より効率的なネットワークアーキテクチャを探求する、例えばGRU(Gated Recurrent Unit)のようにパラメータが少ないモデルを使用する。また、モデルのプルーニングや量子化を行い、計算効率を向上させる。
  2. 過学習:
    • LSTMが過去のデータに過剰に適合すると、新しいデータや市場状況に対してうまく対応できなくなる場合があります。
    • アプローチ: ドロップアウトや正則化技術を適用してモデルの一般化能力を向上させる。また、クロスバリデーションを使用してモデルの堅牢性を検証する。
  3. 市場ノイズの扱い:
    • 仮想通貨市場は非常にボラタイルであり、ノイズが多いデータに適切に対応することは難しいです。
    • アプローチ: データ前処理の工夫、例えば異常値の除去やデータスムージングを行う。また、データのノイズに強いモデルを設計するために、アンサンブル学習やデータ拡張を試みる。
  4. リアルタイム性能:
    • リアルタイムでの取引には、予測と決定が迅速に行われる必要がありますが、LSTMは予測に時間がかかることがあります。
    • アプローチ: モデルのインフェレンス速度を向上させるために、よりシンプルなモデルを使用するか、GPUやTPUのような専用ハードウェアで実行する。

これらのアプローチは、PPOとLSTMを組み合わせた取引プログラムの性能と効率を向上させるために有効です。また、これらの問題に対処することで、プログラムの市場適応性と長期的な利益を確保することが期待されます。

プログラムの雛形

このコードは、ビットコイン取引のための強化学習モデルをシミュレートするもので、仮想の環境を作成し、PPO(Proximal Policy Optimization)とLSTM(Long Short-Term Memory)ネットワークを組み合わせてエージェントを訓練しています。

import numpy as np
import tensorflow as tf
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Dense, LSTM
from tensorflow.keras.optimizers import Adam
import gym
from gym import spaces
import ccxt
import pandas as pd

class CryptoTradingEnv(gym.Env):
    def __init__(self, history_len=10):
        super(CryptoTradingEnv, self).__init__()
        self.history_len = history_len
        self.observation_space = spaces.Box(low=np.inf, high=np.inf, shape=(history_len, 5), dtype=np.float32)
        self.action_space = spaces.Discrete(3)  # 0: Hold, 1: Buy, 2: Sell
        self.current_step = 0
        self.data = self.load_data()
        self.current_inventory = 10
        self.starting_cash = 10000
        self.cash = self.starting_cash

    def load_data(self):
        # 仮想データを生成します
        dates = pd.date_range('20230101', periods=1000)
        prices = np.abs(np.random.normal(loc=10000, scale=2000, size=(1000,)))
        df = pd.DataFrame(data={'date': dates, 'close': prices})
        df['open'] = df['close'] + np.random.normal(loc=0, scale=100, size=(1000,))
        df['high'] = df[['open', 'close']].max(axis=1) + np.random.normal(loc=0, scale=50, size=(1000,))
        df['low'] = df[['open', 'close']].min(axis=1) - np.random.normal(loc=0, scale=50, size=(1000,))
        df['volume'] = np.random.normal(loc=1000, scale=300, size=(1000,))
        return df

    def step(self, action):
        self.current_step += 1
        if action == 1 and self.cash >= self.data.iloc[self.current_step]['close']:  # Buy
            self.current_inventory += 1
            self.cash -= self.data.iloc[self.current_step]['close']
        elif action == 2 and self.current_inventory > 0:  # Sell
            self.current_inventory -= 1
            self.cash += self.data.iloc[self.current_step]['close']
        next_state = self.get_observation()
        reward = self.get_reward()
        done = self.current_step >= len(self.data) - 1
        return next_state, reward, done, {}

    def get_observation(self):
        return np.array([self.data.iloc[self.current_step-self.history_len+1:self.current_step+1][['open', 'high', 'low', 'close', 'volume']].values])

    def get_reward(self):
        return self.current_inventory * self.data.iloc[self.current_step]['close'] + self.cash - self.starting_cash

    def reset(self):
        self.current_step = self.history_len
        self.current_inventory = 10
        self.cash = self.starting_cash
        return self.get_observation()

class PPOAgent:
    def __init__(self, state_size, action_size):
        self.state_size = state_size
        self.action_size = action_size
        self.gamma = 0.99
        self.learning_rate = 0.00025
        self.actor_critic = self.build_model()

    def build_model(self):
        inputs = Input(shape=(self.state_size, 5))
        x = LSTM(64, return_sequences=True)(inputs)
        x = LSTM(32)(x)
        action_output = Dense(self.action_size, activation='softmax')(x)
        value_output = Dense(1, activation='linear')(x)

        model = Model(inputs=inputs, outputs=[action_output, value_output])
        model.compile(optimizer=Adam(self.learning_rate), loss=['categorical_crossentropy', 'mse'])
        return model

    def act(self, state):
        action_probs, _ = self.actor_critic.predict(state)
        return np.random.choice(self.action_size, p=action_probs[0])

    def train(self, state, action, reward, next_state, done):
        # バッチデータを使った学習プロセスが必要
        pass

env = CryptoTradingEnv()
agent = PPOAgent(state_size=10, action_size=3)

episodes = 100
for episode in range(episodes):
    state = env.reset()
    total_reward = 0
    done = False
    while not done:
        action = agent.act(state)
        next_state, reward, done, _ = env.step(action)
        agent.train(state, action, reward, next_state, done)
        state = next_state
        total_reward += reward
    print(f'Episode: {episode+1}, Total Reward: {total_reward}')
Yodaka

ざっくり説明すると以下の通りです。

クラス定義と機能

  1. CryptoTradingEnvクラス:
    • Gymライブラリを利用してカスタムの取引環境を作成しています。
    • この環境は仮想のビットコイン価格データを生成し、取引戦略のテストに使用します。
    • history_len パラメータで指定された数の過去データポイントを観察空間として設定。
    • 行動空間は3つの離散的なアクション(保持、購入、売却)を持ちます。
    • 各ステップでアクションに基づいて内部状態を更新し、報酬を計算します。
  2. PPOAgentクラス:
    • LSTMを用いたアクタークリティックモデルを構築しています。このモデルは取引行動の確率と状態価値を予測します。
    • act メソッドは現在の状態に基づいて行動を決定し、行動確率はアクターモデルによって生成されます。
    • train メソッドは、状態、行動、報酬、次の状態を使用してモデルを訓練しますが、このコードでは具体的な実装が省略されています。

シミュレーション実行

  • エピソード毎に環境をリセットし、完了するまでエージェントに行動を取らせ、その結果に基づいて学習させます。
  • 各エピソードの結果(総報酬)を出力しています。

目的と応用

  • このシステムは、仮想のデータを用いてビットコインの自動取引戦略を開発・テストするために設計されています。
  • シミュレーション環境での検証を経て、実際の市場データとAPIを統合し、リアルタイムの取引に応用することが期待されます。

注意点

  • 実際の取引には、APIを通じたリアルタイムデータの取得や取引実行のロジックが追加される必要があります。
  • 強化学習に基づく取引システムは市場の不確実性やデータの変動に敏感であるため、システムの堅牢性を確保するためにはさらなる検証と最適化が必要です。

詳細な解説

コードの各部分についてもっと詳しく解説します。

1. CryptoTradingEnv クラス

このクラスは、ビットコイン取引のための環境をシミュレートします。gym.Envを継承しており、主に以下のメソッドで構成されています:

  • __init__:
    • 環境の初期設定を行います。
    • history_len は環境が観察する過去のデータポイントの数です。
    • observation_space は状態の形状を定義し、状態は過去 history_len 個の「open」,「high」,「low」,「close」,「volume」のデータです。
    • action_space はエージェントが取ることができる行動の種類を定義します。ここでは3つのアクション(保持、購入、売却)があります。
  • load_data:
    • 仮想のビットコイン価格データを生成します。これはシミュレーション用であり、実際の市場データに基づいたトレーニングやテストには適していません。
  • step:
    • エージェントの行動を受けて、環境の状態を更新します。
    • 購入や売却のアクションに基づき、インベントリや現金の量が変動します。
    • 報酬は現在のインベントリ価値と現金の合計からスタート時の現金を引いたもので計算されます。
    • エピソードが終了したかどうか(done)と、次の状態(next_state)を返します。
  • reset:
    • エピソードを新たに開始するための環境をリセットします。
    • 初期インベントリと現金が設定され、最初の観察データが返されます。

2. PPOAgent クラス

このクラスはPPOアルゴリズムを使用して取引戦略を学習するエージェントを定義します:

  • __init__:
    • エージェントの初期設定を行います。
    • アクタークリティックモデルを初期化します。
  • build_model:
    • アクタークリティックモデルを構築します。
    • LSTM層を使用して過去の価格データから特徴を学習します。
    • アクション出力(どの行動を取るかの確率)と価値出力(状態の価値評価)の両方をモデルが出力します。
  • act:
    • 現在の状態に基づいて行動を決定します。
    • モデルが出力する行動確率に従ってランダムに行動を選択します。
  • train:
    • 状態、行動、報酬、次の状態を用いてモデルを訓練します。
    • このメソッドは実際には具体的な実装が必要ですが、サンプルコードでは省略されています。

3. シミュレーション実行部分

  • エージェントと環境を初期化し、指定されたエピソード数だけ学習を行います。
  • 各エピソードで環境をリセットし、エピソードが終了するまでエージェントに行動を取らせ、モデルを訓練します。
  • エピソードごとの総報酬を出力して学習の進行状況を確認します。

もっと深掘り

ビットコインの自動取引を行うためのこの強化学習モデルの各部分について、技術的な詳細とその相互関連を徹底的に解説します。

1. CryptoTradingEnv クラス

概要

このクラスは、OpenAI Gymの環境をカスタマイズしてビットコイン取引のシミュレーションを行うために設計されています。この環境は、市場の価格動向を模擬し、トレーディング戦略の訓練と評価を可能にします。

属性

  • history_len: 状態として考慮する過去の時間窓の長さ。
  • observation_space: 状態空間で、history_len バー分のオープン、ハイ、ロー、クローズ、ボリュームの5次元データ。
  • action_space: 行動空間で、0(保持)、1(購入)、2(売却)の3つのアクション。
  • current_step: 現在のステップインデックス。
  • data: ランダムに生成される市場データ。
  • current_inventory: 現在の保有ビットコイン数。
  • starting_cash: 開始時の現金。
  • cash: 現在の現金量。

メソッド

  • load_data: テストや学習のための市場データを生成。実際のデータに基づく場合は、APIからデータを取得して整形するプロセスに置き換えられるべきです。
  • step: エージェントが選択した行動を受けて市場環境を更新し、新しい状態、報酬、終了フラグを返します。取引ロジックと報酬計算が含まれます。
  • get_observation: 現在のcurrent_stepに基づいて、観察状態を生成します。
  • get_reward: ステップごとの報酬を計算。ここではポートフォリオの価値の増加を報酬としています。
  • reset: 環境を初期状態に戻します。新しいエピソードの開始前に呼ばれます。

2. PPOAgent クラス

概要

PPOとLSTMを使用して取引戦略を学習するエージェント。PPOは安定したポリシー更新を提供し、LSTMは価格データの時系列特性を捉えます。

属性

  • state_size: 状態ベクトルのサイズ。
  • action_size: 取りうる行動の数。
  • gamma: 割引率、将来の報酬をどの程度重視するか。
  • learning_rate: 学習率。
  • actor_critic: アクタークリティックモデル。

メソッド

  • build_model: アクタークリティックアーキテクチャを構築。アクターは行動確率を出力し、クリティックは状態価値を評価します。
  • act: 現在の状態に基づいて行動を選択。確率的に行動を選ぶため、探索と利用のバランスが取れます。
  • train: 収集された経験(状態、行動、報酬、次の状態)を用いてモデルを訓練。PPOの特性を活かし、ポリシーが大きく変動しないようにします。

3. シミュレーション実行部分

このセクションでは、環境とエージェントが初期化され、定義されたエピソード数にわたって交互に動作します。エージェントは各ステップで行動を選択し、環境はその行動に基づいて次の状態と報酬を提供します。学習プロセスでは、エージェントが得た経験を使用してポリシーと価値関数を改善します。

4. 技術的・戦略的な意味

このコードの戦略的な意味は、実際の市場データを用いたシナリオで検証と調整を行い、リアルタイムのトレーディングに適用可能な堅牢な自動取引システムを開発することにあります。強化学習モデルは市場の変動を学習し、未知の市場状況にも柔軟に対応可能な戦略を形成します。このプロセスは大量の計算資源と厳密なバックテストを必要としますが、最終的には高度な自動化された取引戦略をもたらす可能性があります。

まとめ

今回は「PPOとLSTM」を利用した自動取引botのプロトタイプを作ってみました。

シミュレーション用のコードはそれなりに高速で稼働させる事ができるようになりました。

Yodaka

様々なタイプのMLbotを作っていると、他のロジックや技術との比較ができるようになり、それが体系的な理解にもつながるという実感があります。

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

-Bot