
知っておくべきMQL5ウィザードのテクニック(第54回):SACとテンソルのハイブリッドによる強化学習
はじめに
Soft Actor Critic (SAC)は、ニューラルネットワークを訓練する際に使用される強化学習アルゴリズムの一つです。復習として、強化学習は、教師あり学習や教師なし学習と並ぶ、機械学習における新しい訓練手法の一つです。
リプレイバッファ
リプレイバッファは、SACのオフポリシー型強化学習アルゴリズムにおいて非常に重要なコンポーネントです。これは、過去の経験(状態、行動、報酬、次の状態、およびエピソードの完了を示すdoneフラグ)を記録し、それらを小さなミニバッチに分けて訓練データとして使用します。リプレイバッファの主な目的は、経験同士の相関を減らすことであり、それによってエージェントはより多様な経験から学習できるようになり、学習の安定性やサンプル効率の向上につながります。
SACを実装する際にはMQL5言語を使用することも可能ですが、Pythonおよびそのオープンソースライブラリ(たとえばTensorFlowやPyTorch)で作成されたネットワークほど効率的に学習させることはできません。そのため、前回の強化学習に関する記事ではPythonを用いて基本的なSACネットワークを構築しましたが、今回はその続きとして、Pythonのテンソルグラフを活用することを目的とします。リプレイバッファをPythonで実装する方法は、基本的に2通りあり、手動で構築する方法と、テンソルベースの方法です。
手動の方法では、リストやNumPy配列などの基本的なPythonデータ構造を用います。一方、テンソルベースの方法では、TensorFlowやPyTorchといった深層学習フレームワークを活用し、ニューラルネットワークの訓練パイプラインとの統合性が高く、GPUによる高速処理が可能になるため、より効率的です。手動アプローチでは、NumPy配列を使ってリプレイバッファを作成します。これは小規模な問題に対してはシンプルかつ効果的な方法です。以下にその実装例を示します。
import numpy as np class ReplayBuffer: def __init__(self, max_size, state_dim, action_dim): self.max_size = max_size self.states = np.zeros((max_size, state_dim), dtype=np.float32) self.actions = np.zeros((max_size, action_dim), dtype=np.float32) self.rewards = np.zeros(max_size, dtype=np.float32) self.next_states = np.zeros((max_size, state_dim), dtype=np.float32) self.dones = np.zeros(max_size, dtype=np.float32) self.ptr = 0 self.size = 0 def add(self, state, action, reward, next_state, done): ... def sample(self, batch_size): idx = np.random.randint(0, self.size, size=batch_size) return ( self.states[idx], ... self.dones[idx], )
この手法は比較的実装が簡単であり、前回のSACに関する記事で使用した方法とも非常に似ています。ただし、大規模な問題やGPUによる高速化にはうまく対応できない可能性があります。
一方、テンソルベースの方法では、リプレイバッファをPyTorchまたはTensorFlowのいずれかを用いて実装します。私のこれまでの経験では、TensorFlowの方はやや扱いにくい印象があります。GPU環境でTensorFlowを動作させる設定は一応成功しましたが、特定のGPUを使う際には、TensorFlowやPythonのバージョンに加えて、対応するドライバや関連ライブラリのバージョンなど、非常に多くの要素を正確に整える必要があり、その構成作業は圧倒されるほど複雑です。
その後、PyTorchを試してみたところ、おそらくTensorFlowでの経験が役に立ったのかもしれませんが、はるかにスムーズに導入できました。PyTorchにおけるニューラルネットワークの学習とGPUの統合は非常にシームレスで、1,000,000行近いデータセットでも、1,000万パラメータのやや複雑なネットワークに対して、基本的なNVIDIA T4 GPU上で1エポックを約4分で処理できます。以下に、Pythonでの簡易的な実装例を示します。
import torch class ReplayBuffer: def __init__(self, max_size, state_dim, action_dim): self.max_size = max_size self.states = torch.zeros((max_size, state_dim), dtype=torch.float32) ... self.ptr = 0 self.size = 0 def add(self, state, action, reward, next_state, done): self.states[self.ptr] = torch.tensor(state, dtype=torch.float32) self.actions[self.ptr] = torch.tensor(action, dtype=torch.float32) self.rewards[self.ptr] = torch.tensor(reward, dtype=torch.float32) self.next_states[self.ptr] = torch.tensor(next_state, dtype=torch.float32) self.dones[self.ptr] = torch.tensor(done, dtype=torch.float32) self.ptr = (self.ptr + 1) % self.max_size self.size = min(self.size + 1, self.max_size) def sample(self, batch_size): idx = torch.randint(0, self.size, (batch_size,)) return ( self.states[idx], self.actions[idx], self.rewards[idx], self.next_states[idx], self.dones[idx], )
このメソッドは、PyTorchの自動微分(autograd)や最適化パイプラインと直接統合されているため、非常に効率的です。したがって、手動アプローチとテンソルベースアプローチの比較をまとめると、手動アプローチの利点は、実装がシンプルであり、深層学習フレームワークに依存しない点にあります。一方で、スケーラビリティが限られており、GPUによる高速化ができないという欠点もあります。テンソルベースのアプローチでは、ニューラルネットワークとの統合が非常にスムーズであり、GPUアクセラレーションが可能で、大規模な問題にも対応できます。ただし、TensorFlowやPyTorchといった深層学習フレームワークにある程度精通している必要があるという点が、実装上のハードルとなることがあります。
したがって、どちらの手法を選ぶかは、問題の規模や使用可能なハードウェアに応じて判断すべきです。大規模な問題やGPUを用いた学習をおこなう場合は、テンソルベースの手法が推奨されます。さらに、状態のサイズが可変(たとえば画像など)な環境であれば、テンソルベースの実装が適しています。また、SACはPrioritized Experience Replay (PER)を導入することで強化することができます。PERでは、リプレイバッファ内の各状態の重要度(たとえば時間差分誤差(TD誤差)など)に基づいてサンプリングをおこないます。テンソルベースのリプレイバッファは、優先度の更新やサンプリングの効率が高いため、PERの実装にも適しています。
すべてのコード実装と同様に、デプロイ前にはテストとデバッグを行うことが重要です。リプレイバッファにおいては、ダミーデータを追加してサンプリング動作が正しくおこなわれているかを検証することで確認が可能です。また、バッファが空または満杯といったエッジケースにも正しく対応しているかを確認する必要があります。assert文や単体テストを使用して、機能の正当性を検証できます。リプレイバッファの準備が整ったら、次のステップはSACの訓練ループへの統合です。具体的には、各訓練ステップ後に経験を保存し、ミニバッチをサンプリングしてネットワークを更新します。
リプレイバッファのサイズ設定には注意が必要で、多様な経験を蓄積できるだけの十分な容量を確保しつつ、サンプリング処理が遅くならないようにする必要があります。処理速度とメモリ効率を向上させるには、PyTorchのテンソルなど効率的なデータ構造を使うこと、不要なデータコピーを避けること、あらかじめバッファのメモリを確保しておくことが効果的です。また、プロファイリングをおこなうことで、性能上のボトルネックを特定し、改善する手がかりを得ることができます。
総じて、SACをはじめとする多くのオフポリシーアルゴリズムにおいて、しっかりと実装されたリプレイバッファは、その学習の成功において不可欠な要素であると言えます。多様で非相関な経験を提供することで、安定的かつ効率的なトレーニングを可能にします。
Criticネットワーク
SACアルゴリズムにおいて、CriticネットワークはQ値(状態-行動の価値、またはActorが次に取るべき行動の評価)を推定する役割を担います。これは、現在の環境状態とActorが選択した次の行動を入力として受け取り、それに対する価値を評価します。SACでは、過大評価バイアスを抑え、学習の安定性を向上させるために、2つのCriticネットワークを同時に用いるのが特徴です。このCriticは、Actorネットワークが出力する行動の確率分布と、環境の状態(たとえば位置や速度などの座標情報)という、2つのクランプ(範囲を制限された)データセットを入力として受け取るニューラルネットワークです。そのため、NumPyを使うか、PyTorchやTensorFlowなどのテンソルライブラリを使うかの選択は、ネットワークの規模や訓練データの量、さらには利用可能なハードウェア(特にGPU)の有無によって左右されます。
テンソルを用いない手動実装では、NumPyが行列演算や勾配の更新などに便利です。ネットワークの層数が5未満で、各層のユニット数も15以下といった小規模なモデル、あるいは教育や説明目的での使用であれば、NumPyによる手動実装でも実用に足ります。このような場合、順伝播(フォワードパス)や逆伝播(バックワードパス)は手動で実装する必要がありますが、それはエラーを起こしやすく、大規模なデータセットに対しては効率的ではありません。以下に、Pythonによる手動実装の一例を示します。
import numpy as np class CriticNetwork: def __init__(self, state_dim, action_dim, hidden_dim=256): self.state_dim = state_dim self.action_dim = action_dim self.hidden_dim = hidden_dim # Initialize weights and biases self.W1 = np.random.randn(state_dim + action_dim, hidden_dim) self.b1 = np.zeros(hidden_dim) self.W2 = np.random.randn(hidden_dim, hidden_dim) self.b2 = np.zeros(hidden_dim) self.W3 = np.random.randn(hidden_dim, 1) self.b3 = np.zeros(1) def forward(self, state, action): x = np.concatenate([state, action], axis=-1) x = np.maximum(0, x @ self.W1 + self.b1) # ReLU activation x = np.maximum(0, x @ self.W2 + self.b2) # ReLU activation q_value = x @ self.W3 + self.b3 return q_value def update(self, states, actions, targets, learning_rate=1e-3): # Manual gradient descent (simplified) q_values = self.forward(states, actions) error = q_values - targets # Backpropagation and weight updates (not shown for brevity)
すでに述べたように、この手動実装のアプローチは、大規模なネットワークやデータセットにはスケーラブルではなく、GPUによる高速化も利用できません。一方で、テンソルを使用する方法、特にPyTorchを用いた実装を選択すれば、自動微分機能とGPUアクセラレーションの恩恵を受けることができます。これらの特性は、大規模な問題や本番レベルの実装に非常に適していると言えるでしょう。以下は、そのような実装の非常に基本的なコード例です。
import torch import torch.nn as nn import torch.nn.functional as F class CriticNetwork(nn.Module): def __init__(self, state_dim, action_dim, hidden_dim=256): super(CriticNetwork, self).__init__() self.fc1 = nn.Linear(state_dim + action_dim, hidden_dim) self.fc2 = nn.Linear(hidden_dim, hidden_dim) self.fc3 = nn.Linear(hidden_dim, 1) def forward(self, state, action): x = torch.cat([state, action], dim=-1) x = F.relu(self.fc1(x)) x = F.relu(self.fc2(x)) q_value = self.fc3(x) return q_value
特にPyTorchには、ネットワークの最適化を担当するtorch.optimや、ネットワークの構築を支援するtorch.nnといった重要なライブラリが揃っており、これらを活用することで非常にスケーラブルかつ効率的な実装が可能です。
したがって、先に示したような手動実装は、シンプルで深層学習フレームワークに依存しないという利点はあるものの、スケーラビリティに欠け、GPUの性能を十分に活用できないため、実用面では制約が大きいと言わざるを得ません。
Criticネットワークにテンソルを用いる場合も、先に述べたリプレイバッファでの長所・短所とほぼ同様の利点・課題があります。総じて言えば、問題の規模が非常に小さく、ニューラルネットワークの詳細を説明・教育することが主目的であれば、手動実装が適している場面もあります。しかし実務上は、既に述べた理由からテンソルを用いた方法がより実用的です。
SACネットワークは、過大評価バイアスを抑えるために2つのCriticネットワーク(一般的にQ1とQ2と呼ばれます)を使用する点が特徴でした。ターゲットとなるQ値は、この2つのCriticネットワークが出すQ値のうち最小値で決定されます。具体的には、CriticネットワークはActorネットワークの一部出力と同様に、利用可能な各行動に対する予測報酬のベクトルを生成します。
そのため、各ベクトル内で最も値の高いインデックス(行動)が、最も高い報酬を予測するものとみなされます。Criticネットワークの主な役割は、この報酬予測をもとに、Actorネットワークの勾配を保守的に計算し、逆伝播を通じて更新をおこなうことにあります。
Actorネットワークの更新時には、目的関数の勾配が計算され、Criticネットワークの2つのQ値のうち最小値から逆伝播されます。この最小値の選択により、ActorはCriticにおける過大評価の影響を受けにくい、より堅牢な行動を選択するよう最適化されます。
このActorの更新に用いる勾配を提供するCriticネットワーク自体も訓練が必要です。しかし、Criticは将来の報酬を推定しているため、その訓練目標はどのように決められるのでしょうか。SACにおいてCriticの更新は、2つのQ値の最小値を直接使うわけではなく、それぞれのCritic(Q1とQ2)に対して、ソフトベルマン方程式に基づくターゲットを用いて更新をおこないます。
class DoubleCriticNetwork(nn.Module): def __init__(self, state_dim, action_dim, hidden_dim=256): super(DoubleCriticNetwork, self).__init__() self.Q1 = CriticNetwork(state_dim, action_dim, hidden_dim) self.Q2 = CriticNetwork(state_dim, action_dim, hidden_dim) def forward(self, state, action): q1 = self.Q1(state, action) q2 = self.Q2(state, action) return q1, q2
さらに、SACにおけるTargetネットワークは、CriticのQ1とQ2それぞれに対応した別のネットワーク群で、Criticネットワークのバックプロパゲーションにおいて重要なターゲットQ値を計算するために使われます。これらのTargetネットワークはPolyak averagingによってゆっくりと更新され、訓練の安定性を保つ役割を果たしています。Targetネットワークの利用は、ベルマン方程式の安定したターゲットを提供する必要性から来ています。これがなければ、CriticネットワークのQ値推定は、Q値とターゲット値の間に生じるフィードバックループの影響で発散したり振動したりすると考えられています。
for target_param, param in zip(target_critic.parameters(), critic.parameters()): target_param.data.copy_(tau * param.data + (1 - tau) * target_param.data) ``` where `tau` is the polyak averaging coefficient (e.g., 0.005).
Criticの損失関数は、予測されたQ値と、先述のTargetネットワークを用いてベルマン方程式によって算出されたターゲットQ値との平均二乗誤差(MSE)となります。
target_q_value = reward + (1 - done) * gamma * min(Q1_target(next_state, next_action), Q2_target(next_state, next_action)) ``` where `gamma` is the discount factor.
Criticを訓練するために、まずリプレイバッファからミニバッチをサンプリングし、ターゲットQ値を計算します。それを用いて、Criticネットワークは勾配降下法によって更新されます。訓練効率を高めるために、Adamのようなオプティマイザを使用することも可能です。
optimizer = torch.optim.Adam(critic.parameters(), lr=learning_rate)
デバッグやテストは、ダミーの入力を与えて出力の形状や値を確認することでおこなうことができます。また、ネットワークが小さなデータセットに過学習の可能性があるかどうかを確かめることも重要です。前述の通り、Pythonのアサーションを使って入力データの検証をおこなうことも推奨されます。Criticネットワークは、SACの学習ループ内でQ値を計算し、Criticの損失を更新し、Targetネットワークを同期させることで統合されます。CriticネットワークはActorネットワークやValueネットワークと連動して更新されるため、この点でテンソルの利用、特にGPUの活用が非常に重要となります。
さらに、Criticネットワークに対しては、いくつかの追加的な最適化手法も試すことができます。まずバッチ正規化やレイヤー正規化などの手法が挙げられます。次に、ReLUやLeaky ReLUなど異なる活性化関数を試すことで、データの特性やネットワークの構造によって結果が変わることがあります。また、学習率やネットワークの深さといったハイパーパラメータの調整も重要です。学習中にCriticの損失をモニタリングすることで、過学習や学習の不安定さといった問題を早期に検出できます。
まとめると、CriticネットワークはSACにおいて極めて重要な要素であり、Q値の推定とActorの方策更新を導く役割を果たしています。適切に実装されたCriticネットワークは、安定かつ効率的な学習を支える基盤となります。
Valueネットワーク
Valueネットワーク(状態価値関数とも呼ばれる)は、SACにおいて利用が任意のネットワークです。その目的は、現在の方策のもとで状態が得られる期待累積報酬をソフト価値関数を用いて推定することにあります。Valueネットワークの使用は「任意」ですが、適切に実装されれば多くの利点があります。まず、ソフト価値関数はエントロピーを明示的に組み込むため、方策がより効率的かつ効果的に探索をおこなうことを促します。ソフト価値関数による行動は、訓練のターゲットをより滑らかにする傾向があり、学習の安定化に寄与します。
この安定性は、入力データの次元数が高い場合(入力データのベクトルサイズが10以上など)、行動空間が連続的な場合(たとえば、(0.14、0.67、1.51)のような数値の組み合わせが行動としてあるケース。これに対して、(買う、売る、保持)のような離散的選択肢のケースとは異なります)、あるいは複数の局所最適解が存在する環境に直面した際に非常に有用です。局所最適解のシナリオとは、異なるネットワークの重み構成が別々のデータセットや環境に対しては良好な結果を出すものの、どれもより広範なデータセットでの汎化や安定した性能を維持できない状態を指します。
まとめると、SACにおけるValueネットワークは、任意の行動に依存せず状態の期待リターン(期待収益)を推定するために用いられ、これによりQ値推定のばらつきを抑え、ソフトなターゲットを提供します。テンソルを使うか手動実装にするかの議論は、前述のCriticネットワークの場合とほぼ同じです。現代のほとんどのSACネットワークはValueネットワークを実装せず、代わりに2つのTargetネットワークを利用して2つのCriticネットワークの訓練ターゲットの設定を助けています。
単一のValueネットワークをCriticの訓練ターゲット調整に用いる以外に、2つのValueネットワークを使う方法もあります。この場合、ValueネットワークとTarget Valueネットワークの両方が価値関数の推定に使われ、任意の状態から得られる期待リターン(累積報酬)を予測します。特にTarget Valueネットワークは、Deep-Qネットワークなど他の手法と同様に、訓練の安定化のために用いられます。Valueネットワークのコピーとしての役割を持ち、更新頻度を低く抑えることで、安定した訓練ターゲットを提供するのです。
Actorネットワーク
ActorネットワークはSACにおける主要なネットワークで、ポリシーネットワークとも呼ばれます。環境の状態を入力として受け取り、行動の確率分布のパラメータとなる平均値と標準偏差の2つのベクトルを出力します。これらのパラメータを使って確率的に行動をサンプリングすることができます。2つのCriticネットワークおよびそれに対応するTargetネットワーク、さらに使用していればValueネットワークも、このActorネットワークのバックプロパゲーションと学習を助ける役割を果たします。
ネットワークである以上、SACにおいて中心的役割を担うActorネットワークにはテンソルの活用が大きなメリットをもたらします。また、これらの関連する複数のネットワークの並列・同時学習を可能にするGPUの利用は、多くの効率化をもたらすため、ぜひ積極的に検討すべきです。
import torch import torch.nn as nn import torch.nn.functional as F import torch.distributions as dist class ActorNetwork(nn.Module): def __init__(self, state_dim, action_dim, hidden_dim=256): super(ActorNetwork, self).__init__() self.fc1 = nn.Linear(state_dim, hidden_dim) self.fc2 = nn.Linear(hidden_dim, hidden_dim) self.fc_mean = nn.Linear(hidden_dim, action_dim) self.fc_log_std = nn.Linear(hidden_dim, action_dim) def forward(self, state): x = F.relu(self.fc1(state)) x = F.relu(self.fc2(x)) mean = self.fc_mean(x) log_std = self.fc_log_std(x) return mean, log_std def sample_action(self, state): mean, log_std = self.forward(state) std = torch.exp(log_std) normal = dist.Normal(mean, std) action = normal.rsample() # Reparameterization trick return torch.tanh(action), normal.log_prob(action).sum(dim=-1, keepdim=True)
前回のSACの記事でも述べたように、Actorネットワークは2つの重要な「ベクトル」、すなわち正規分布(ガウス分布)の平均と標準偏差を出力します。これら2つを組み合わせて次の行動を確率的に決定します。この確率的な行動選択は、連続的な行動空間における探索や最適化において非常に重要です。平均は行動の分布の中心を表し、標準偏差は分布の広がりやランダム性を制御します。この2つのパラメータによって定義される正規分布から、行動を選択することができます。以下のPythonコードは、この処理を実現する例です。
import torch def select_action(mean, log_std): """ Given the SAC actor's output (mean and log_std), this function selects an action index. Args: mean (torch.Tensor): Mean of the action distribution, shape (n_actions,) log_std (torch.Tensor): Log standard deviation, shape (n_actions,) Returns: int: The index of the selected action. """ std = log_std.exp() # Convert log standard deviation back to standard deviation .... return selected_index # Example inputs mean = torch.tensor([0.2, -0.5, 1.0, 0.3]) # Example mean values for 4 actions log_std = torch.tensor([-1.0, -0.7, -0.2, -0.5]) # Example log std values # Select action action_index = select_action(mean, log_std) print("Selected Action Index:", action_index)
ただし実際には、この関数はMQL5で実行する必要があります。というのも、Pythonでモデルをトレーニングした後、それはONNXファイルとしてエクスポートされ、ONNX形式では順伝播時の出力がトレーニング時のPythonでの出力と類似するからです。したがって、これら2つの出力がMQL5で受け取られるため、このアクション選択関数もMQL5で実装する必要があります。
これら2つの出力は、行動を選択するための正規分布を定義します。行動の選択は確率的におこなわれ、エージェントがある状態に対して毎回同じ行動を選ばないよう、探索を促進します。バックプロパゲーションの際には、効率性のために再パラメータ化が用いられ、サンプリングの過程に対しても勾配が伝播できるようにします。
また、上記のPython関数において、現実世界の多くの行動には範囲がある(制限されている)ため、SACではサンプルされた行動にTanh関数を適用して、-1から+1の範囲に収めます。これにより、行動が制御可能な範囲に留まりつつ、確率的な特性も維持されます。
エージェント
SACのエージェントは、ここではPythonのagentクラスとして表現され、方策最適化(Actorネットワーク)と価値関数近似(2つのCriticネットワーク、すなわちValueネットワークとTarget Valueネットワークのペア)を組み合わせています。このエージェントは、サンプル効率が高く、連続行動空間に対応できる必要があります。なぜなら、これらのネットワークだけでなく、リプレイバッファも統合し、「最適な方策」、すなわちActorネットワークに対する適切な重みとバイアスを学習することを目的としているからです。
このように、エージェントは高レベルの設計要素を多く含んでいるため、ネットワークのトレーニングを効率よくおこなうにはテンソルの使用が不可欠であることは言うまでもありません。以下に、これをPythonでどのように実装できるかを示します。
import torch import torch.nn.functional as F import torch.optim as optim class SACAgent: def __init__(self, state_dim, action_dim, hidden_dim=256, replay_buffer_size=1e6, batch_size=256, gamma=0.99, tau=0.005, alpha=0.2): self.state_dim = state_dim ... self.alpha = alpha # Initialize networks and replay buffer self.actor = ActorNetwork(state_dim, action_dim, hidden_dim) .... self.value_optimizer = optim.Adam(self.value_network.parameters(), lr=3e-4) def select_action(self, state): state = torch.FloatTensor(state).unsqueeze(0) action, _ = self.actor.sample_action(state) return action.detach().numpy()[0] def update(self): # Sample a batch from the replay buffer states, actions, rewards, next_states, dones = self.replay_buffer.sample(self.batch_size) # Convert to tensors states = torch.FloatTensor(states) ... dones = torch.FloatTensor(dones).unsqueeze(1) # Update value network target_value = self.target_value_network(next_states) ... # Update critic networks q1_value = self.critic1(states, actions) ... self.critic2_optimizer.step() # Update actor network new_actions, log_probs = self.actor.sample_action(states) ... self.actor_optimizer.step() # Update target networks for target_param, param in zip(self.target_value_network.parameters(), self.value_network.parameters()): target_param.data.copy_(self.tau * param.data + (1 - self.tau) * target_param.data)
テンソルを使用する以上、PyTorchを用いる場合には、GPUデバイスが利用可能であればそれを手動で指定することが重要です。これにより、テンソルの実行効率が大きく向上することが強調されるべきです。
環境
環境とは、トレーニングデータやターゲット/行動のデータセットが定義される場所です。環境は本質的に、エージェントが解決しようとしている問題を定義するものであり、エージェントの行動に基づいて、状態、報酬、および終了シグナルを提供します。環境は、最初の原理から手動で実装することも可能ですし、OpenAIのGymのようなライブラリを継承することで、標準化された環境として構築することもできます。テンソルベースの環境は、以下のように実装することができます。
import torch class TensorEnvironment: def __init__(self): self.state_space = 4 # Example: state dimension self.action_space = 2 # Example: action dimension self.state = torch.zeros(self.state_space) # Initial state def reset(self): self.state = torch.randn(self.state_space) # Reset to a random state return self.state def step(self, action): # Define transition dynamics and reward function next_state = self.state + action # Simple transition ... self.state = next_state return next_state, reward, done, {} def render(self): print(f"State: {self.state}")
次に、ループを使用して経験を収集し、エージェントを定期的に更新します。
for episode in range(num_episodes): state = env.reset() episode_reward = 0 for step in range(max_steps): action = agent.select_action(state) next_state, reward, done, _ = env.step(action) agent.replay_buffer.add(state, action, reward, next_state, done) state = next_state episode_reward += reward if len(agent.replay_buffer) > batch_size: agent.update() if done: break print(f"Episode {episode}, Reward: {episode_reward}")
ウィザードの組み立てとテスト
Actorネットワークと2つのCriticネットワークに加え、2つのTargetネットワークの代わりにValueネットワークとTarget Valueネットワークを使用するテンソルベースのハイブリッドSACをテストします。テスト対象としては、2023年のEUR/USDペアの4時間足チャートを使用します。
TensorFlowとPyTorchの両方は、モデルのトレーニングだけでなく、交差検証にも対応しています。TensorFlowでは、fit関数に検証用のxとyのデータを渡すことでこれを実現し、PyTorchでは同様にこの検証データをdata_loaderに渡すことで対応可能です。交差検証(または推論)をおこなわないMQL5でコンパイルされたエキスパートアドバイザーでのテスト実行では、2023年のEUR/USDについて以下のような結果が得られました:
前回のSACに関する記事と同様に、Pythonからモデルをエクスポートし、MQL5にインポートする際にはONNXを使用します。今回は5つのニューラルネットワークを扱っていますが、実際に予測(推論)をおこなうのは1つだけで、残りの4つはすべてバックプロパゲーションのために使われます。私たちが必要としてエクスポートするのはActorネットワークです。ただし、このネットワークはすでに述べたように、出力として1つではなく2つのベクトルを返します。それは平均値と標準偏差です。したがって、ONNXモデルをMQL5で利用できるようにするには、このモデルの出力の形状を正確に定義する必要があります。入力の形状は単純です。MQL5では、以下のように入力および出力の形状を設定します。
//+------------------------------------------------------------------+ //| Validation arch protected data. | //+------------------------------------------------------------------+ bool CSignalSAC::ValidationSettings(void) { if(!CExpertSignal::ValidationSettings()) return(false); //--- initial data checks if(m_period != PERIOD_H4) { Print(" time frame should be H4 "); return(false); } if(m_actor_handle == INVALID_HANDLE) { Print("Actor OnnxCreateFromBuffer error ", GetLastError()); return(false); } // Set input shapes const long _actor_in_shape[] = {1, __STATES}; // Set output shapes const long _actor_out_shape[] = {1, __ACTIONS}; if(!OnnxSetInputShape(m_actor_handle, ONNX_DEFAULT, _actor_in_shape)) { Print("Actor OnnxSetInputShape error ", GetLastError()); return(false); } if(!OnnxSetOutputShape(m_actor_handle, 0, _actor_out_shape)) { Print("Actor OnnxSetOutputShape error ", GetLastError()); return(false); } if(!OnnxSetOutputShape(m_actor_handle, 1, _actor_out_shape)) { Print("Actor OnnxSetOutputShape error ", GetLastError()); return(false); } //read best weights //--- ok return(true); }これは、カスタムシグナルクラスの検証関数内で処理されます。というのも、これらの形状が正確に定義されていなければ、ONNXモデルは実行できないためです。新しい読者は、カスタムシグナルの*.mqhファイルからEAを組み立てる方法について、こちらとこちらのガイドを参照してください。
結論
テンソルを活用してPythonでSoft Actor-Critic (SAC)強化学習アルゴリズムを実装しました。テンソルは機械学習において非常に重要な役割を果たします。というのも、テンソルを使うことでモデルのトレーニングにおける効率が大幅に向上するからです。これは、特にトレーダーにとって非常に重要なポイントです。トレーニングデータセットのサイズが大きくなり、より複雑なネットワーク設計が求められるようになると、トレーニング処理が遅くなる傾向があります。こうした問題に対処するために、テンソルの活用だけでなく、GPUの性能を最大限に引き出すことが極めて有効となります。
名前 | 詳細 |
---|---|
hybrid_sac.mq5 | 使用されたファイルを表示するヘッダーを備えた、ウィザードで組み立てられたEA |
SignlWZ_54.mqh | カスタムシグナルクラスファイル |
model.onnx | ONNXネットワークファイル |
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/17159





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索