English Deutsch 日本語
preview
Возможности Мастера MQL5, которые вам нужно знать (Часть 51): Обучение с подкреплением с помощью SAC

Возможности Мастера MQL5, которые вам нужно знать (Часть 51): Обучение с подкреплением с помощью SAC

MetaTrader 5Торговые системы |
46 1
Stephen Njuki
Stephen Njuki

Введение

Soft Actor Critic (SAC) — это еще один алгоритм обучения с подкреплением в ряду уже рассмотренных, таких как проксимальная оптимизация политикиглубокие Q-сетиSARSA и другие. Этот алгоритм, как и некоторые другие, которые мы уже рассматривали, использует нейронные сети, но с одной важной оговоркой. Всего используется три сети: две сети критиков и одна сеть актеров. Две критические сети делают прогнозы вознаграждения (Q-значения) при вводе действия и состояния среды, а минимум выходных данных этих двух сетей используется для модуляции функции потерь, применяемой для обучения сети актеров.

Входными данными для сети актеров являются координаты состояния среды, а выходные данные состоят из среднего вектора и вектора логарифмического стандартного отклонения. Используя гауссовский процесс, эти два вектора используются для получения распределения вероятностей возможных действий, доступных субъекту. Итак, в то время как две сети критиков можно тренировать традиционным способом, сеть актеров, очевидно, представляет собой нечто совершенно иное. Здесь есть во что вникнуть, поэтому давайте сначала повторим основы, прежде чем двигаться дальше. Две сети критиков для ввода данных принимают текущее состояние окружающей среды и действие. Их выход - это оценка ожидаемой доходности (значения Q) от выполнения этого действия в этом состоянии. Использование двух критиков помогает снизить смещение оценки, распространенную проблему Q-обучения.

В качестве входных данных для сети актеров используется текущее состояние среды. Вывод этого состояния фактически представляет собой распределение вероятностей возможных действий, причем это распределение является стохастическим, чтобы стимулировать исследования. Я использовал слово "фактически", потому что реальные выходные данные сети актеров представляют собой два вектора, которые необходимо подать на гауссово распределение вероятностей, чтобы получить веса каждого действия. В MQL5 это достигается следующим образом:

//+------------------------------------------------------------------+
// Function to compute the Gaussian probability distribution and log
// probabilities
//+------------------------------------------------------------------+
vectorf CSignalSAC::LogProbabilities(vectorf &Mean, vectorf &Log_STD)
{  vectorf _log_probs;
   // Compute standard deviations from log_std
   vectorf _std = exp(Log_STD);
   // Sample actions and compute log probabilities
   float _z = float(rand() % USHORT_MAX / USHORT_MAX); // Generate N(0, 1) sample
   // Sample action using reparameterization trick: action = mean + std * N(0, 1)
   vectorf _actions = Mean + (_std * _z);
   // Compute log probability of the sampled action
   vectorf _variance = _std * _std;
   vectorf _diff = _actions - Mean;
   _log_probs = -0.5f * (log(2.0f * M_PI * _variance) + (_diff * _diff) / _variance);
   return(_log_probs);
}


Как работает SAC?

При этом SAC работает как большинство алгоритмов обучения с подкреплением. Сначала происходит выбор действия, когда субъект выбирает действие из распределения вероятностей выходных данных сети актеров. Затем следует "взаимодействие со средой", когда агент выполняет выбранное действие в среде и выполняет наблюдение за его следующим состоянием и вознаграждением. Затем следует обновление критика, при котором две сети критика обновляются с помощью функции потерь, которая использует сравнение между прогнозируемыми и целевыми значениями Q. Однако, как уже отмечалось выше, обновление сети актеров является новшеством, поскольку оно представляет собой обновление с использованием градиента политики, целью которого является максимизация ожидаемой доходности с учетом энтропии распределения политики. Это призвано стимулировать исследования и предотвращать преждевременный переход к неоптимальным решениям.

Энтропия распределения политики в SAC измеряет случайность или неопределенность. В SAC более высокая энтропия соответствует большему количеству исследовательских действий, тогда как более низкая энтропия призвана подчеркнуть детерминированный выбор. Выходные данные для сети актеров в SAC по своей сути являются стохастической политикой, параметризованной распределением вероятностей, которое в нашем случае (для этой статьи) является Гауссовым распределением. Энтропия этого распределения рисует картину действий, которые агент может предпринять при наличии определенного состояния (входных данных).

Значение такого поведения заключается в поощрении исследования агентом, что типично для большинства алгоритмов обучения с подкреплением, чтобы снизить риск преждевременного схождения к неоптимальным политикам. Таким образом, создается баланс между исследованием и эксплуатацией. SAC включает показатель энтропии в свою задачу оптимизации политики, чтобы максимизировать как ожидаемые вознаграждения, так и энтропию. Объективное уравнение выглядит следующим образом:

где:

  • α - температура энтропии, балансирующая между максимизацией вознаграждения и исследованием.
  • logπ(a∣s) - логарифмическая вероятность действия (выход нашей функции, код которой приведен выше), которая зависит как от μ, так и от log(σ).
  • Q(s,a) - минимальное значение Q двух выходов сетей критики.

Более высокая энтропия, как правило, приводит к более надежному принятию решений в условиях неполной информации, поскольку она предотвращает чрезмерное приспособление к определенным сценариям. Таким образом, максимизация энтропии — это полезная вещь в SAC. Помимо улучшенного исследования, агенту предлагается попробовать более широкий спектр действий или избежать неоптимальных политик, что предотвращает застревание в локальных оптимумах. Это, по сути, служит формой регуляризации, предотвращая чрезмерно детерминированные политики, которые склонны давать сбой в непредвиденных сценариях. Кроме того, постепенная корректировка энтропии часто приводит к более плавному обновлению политики, обеспечивая стабильное обучение и сходимость.

Параметр управления температурой очень чувствителен к энтропии и процессу обучения сети актеров. Для наших целей мы фиксируем это значение на уровне 0,5, как это видно в функции логарифмических вероятностей, код MQL5 которой приведен выше. Однако температурный параметр альфа определяет, какой вес придается энергии в цели. Более высокое значение альфа поощряет исследования, а более низкое способствует детерминированной политике или эксплуатации. Таким образом, назначение этого значения равным 0,5 для наших целей действительно обеспечивает некую форму баланса.

Однако часто бывает так, что SAC использует механизм автоматической настройки энтропии, который динамически регулирует альфу для поддержания целевого уровня энтропии, адаптируя баланс между исследованием и эксплуатацией. Практическими последствиями этого являются: надежное обучение при выполнении задач, создание политик генерализации, которые эффективны в условиях неполной информации (стохастические политики), а также обеспечение основы для непрерывного обучения. Потенциальных компромиссов здесь два. Слишком много исследований и вычислительных затрат.

Избыточная энтропия может привести к неэффективному обучению из-за чрезмерного акцента на исследовательских действиях в ущерб использованию известных высокодоходных стратегий. Это всегда будет происходить за счет расчета и оптимизации условий энтропии, что увеличивает вычислительные затраты по сравнению с алгоритмами, которые фокусируются только на максимизации вознаграждения. Сеть актеров выводит два вектора - MU и Log-STD, как упоминалось выше. Как они влияют на эту энтропию?

MU представляет собой центральную тенденцию распределения действий политики. Он не влияет на энтропию напрямую, однако в некотором смысле определяет среднее поведение политики. С другой стороны, Log-STD контролирует разброс или неопределенность распределения действий и напрямую влияет на энтропию. Более высокий Log-STD означает более широкое и неопределенное распределение действий, тогда как более низкий Log-STD указывает на обратное. Какое отношение имеет величина данного действия в Log-STD к вероятности выбора?

Как уже упоминалось, конкретные характеристики определяются гауссовым процессом, однако низкий Log-STD часто означает, что сеть актеров уверена в оптимальном действии, MU. Это часто подразумевает меньшую вариативность в выборочных действиях. Выбранные действия будут более плотно сконцентрированы вокруг соответствующего значения MU этого действия, имеющего низкое значение Log-STD, что будет способствовать более активному использованию политик вокруг этого действия. Таким образом, хотя низкие показания Log-STD напрямую не делают действие более вероятным, они по сути сужают круг потенциальных действий, тем самым увеличивая вероятность выбора действий, близких к MU.

Существуют также некоторые практические корректировки, которые часто применяются к энтропии. Во-первых, поскольку Log-STD напрямую определяет энтропию, для стабильного обучения SAC обычно ограничивает log(σ) определенным диапазоном, чтобы избежать слишком высокой или слишком низкой энтропии. Это делается с помощью параметра z, показанного в нашей функции логарифмических вероятностей выше. Во-вторых, настройка альфы (которую мы устанавливаем на 0,5f в нашей функции логарифмических вероятностей) имеет решающее значение для достижения баланса между исследованием и эксплуатацией, как уже упоминалось. С этой целью часто используется автоматическая регулировка альфа-канала как средство динамического достижения баланса между исследованием и разведкой.

Это достигается с помощью использования целевой энтропии H. Уравнение представлено ниже:

где:

  • alpha (α t ) - текущая температура, которая контролирует вес энтропийного члена в цели, причем более высокие значения стимулируют исследование, а меньшие значения склоняют к эксплуатации.

  • alpha (αt+1 ) - обновленный параметр температуры после включения обратной связи от текущей энтропии относительно цели.

  • lambda (λ) - скорость обучения для процесса настройки альфа, которая контролирует, насколько быстро альфа адаптируется к отклонениям от целевого значения энтропии H.

  • E (E a~π) - ожидание от действий, выбранных из текущего плана политики.

  • Log(a|s) - логарифмическая вероятность для действия a в состоянии s при текущей политике. Количественно определяет неопределенность выбора действий политики.

  • H target - целевое значение энтропии политики. Часто оно назначается на основе размерности пространства действий (количества и объема доступных действий) в случаях, когда действия дискретны, например, реализации покупки, продажи и удержания, которые мы рассматривали до сих пор; его также можно масштабировать, если действия непрерывны (например, если бы у нас были 2-мерные рыночные ордера, оба масштабируемые от 0,01 до 0,10 для определения размера позиции двух ценных бумаг, которые должны быть куплены одновременно, а значения представляли бы собой процент от свободной маржи).

Таким образом, использование автоматической настройки альфа, хотя и не исследовалось в нашей реализации для этой статьи, обеспечивает динамическую адаптацию агента к изменениям в окружающей среде, исключает необходимость ручной настройки параметра альфа и способствует эффективному обучению за счет поддержания предустановленного компромисса между исследованием и эксплуатацией.


Сравнение SAC и DQN

До сих пор мы рассматривали другой алгоритм обучения с подкреплением, который использует одну нейронную сеть, а именно Deep-Q-Networks. Какие преимущества, если таковые имеются, можно получить от использования SAC с его несколькими сетями? Чтобы обосновать преимущества SAC, давайте рассмотрим сценарий использования задачи манипуляции роботом. Итак, задача: роботизированная рука должна удерживать и перемещать объекты в указанные целевые места. Сочленениям его рычагов будут назначены непрерывные пространства действия, которые количественно определяют необходимые регулировки крутящего момента и угла и т. д.

Проблема использования DQN в данном случае заключается в том, что, во-первых, DQN лучше всего подходит для пространств дискретных действий. Попытка расширить его для охвата непрерывного пространства привела бы к экспоненциальному росту числа доступных дискретных действий для пространств действий более высокой размерности, что сделало бы обучение слишком дорогим и неэффективным. DQN также полагаются на эпсилон-жадные стратегии при балансировании между исследованием и эксплуатацией, и они могут испытывать трудности с эффективной работой в дискретизированных пространствах более высоких размерностей. Наконец, DQN склонен к переоценке, что может привести к некоторой нестабильности при обучении, особенно в сложных условиях с высокой изменчивостью вознаграждения.

В данном случае SAC был бы более подходящим вариантом, в первую очередь из-за своей постоянной поддержки пространства действий. Это проявляется в том, как SAC оптимизирует стохастическую политику в непрерывных пространствах действиях, что устраняет необходимость в дискретизации действий (или классификации). Это обеспечивает плавное и точное управление рукой робота. Три сети в SAC работают в синергии, где сеть актеров генерирует стохастическую политику для задания распределения (вероятностного взвешивания) непрерывных действий. Это повышает эффективность и позволяет избежать преждевременных исследований.

Со своей стороны, сети критиков используют двойной метод оценки Q-значения, который помогает избежать смещения переоценки за счет пересылки минимального значения в обратном распространении в сеть актеров. Это стабилизирует обучение и обеспечивает более точные результаты. Подводя итог, можно сказать, что цель, дополненная энтропией, которая поощряет более глубокие исследования (особенно в сочетании с автоматической настройкой температуры, как обсуждалось выше с альфа), а также надежность и стабильность, обеспечиваемые способностью SAC работать с многомерными пространствами действий, явно ставят его на ступень выше DQN. Опубликованные примеры производительности задач Reacher и Fetch от Open AI Gym ясно показывают, что DQN испытывает трудности в обеспечении плавных движений руки из-за дискретных выходных действий и плохого исследования, при этом политики во многом сходятся на неоптимальных стратегиях. С другой стороны, SAC действительно генерирует плавные и точные действия со стохастической политикой, что приводит к более быстрому выполнению задач, меньшему количеству столкновений и лучшей адаптации к изменению положения объектов или целевых местоположений, опять же благодаря подходу стохастической политики.


Python и TensorFlow

В этой статье, в отличие от предыдущих работ по машинному обучению, мы углубимся в TensorFlow от Python. Язык MQL5 от MetaQuotes, очевидно, остается актуальным, поскольку собранные в Мастере советники во многом полагаются на него. Новички могут найти руководства по использованию кодов для сборки советника здесь и здесь. Однако Python, безусловно, наступает на пятки MQL5 как основного языка программирования при разработке финансовых моделей. Краткое наблюдение показывает, что MetaQuotes нужно просто выпустить больше библиотек на Python, чтобы идти в ногу с инновациями и продолжать подтверждать свое доминирование. 

Однако я достаточно стар, чтобы помнить появление MetaTrader 5 в 2009(?) и как медленно она внедрялась пользователями, несмотря на все преимущества, которые предлагала эта технология. Поэтому я могу понять, почему они не решаются развернуть и поддерживать активные библиотеки на Python. При этом представленные здесь инновации в основном исходят от "клиентов" (т. е. сообщества Python), а не от MetaQuotes, как это было в случае с MetaTrader 5, где во главу угла были поставлены позиции, а не ордера. Возможно, компании следует как можно скорее прислушаться к этому совету? Время покажет, но пока преимущества в эффективности использования TensorFlow и PyTorch при разработке и, что самое важное, обучении сетей на самом деле колоссальны!

Я думаю, что MetaQuotes может рассмотреть возможность корпоративного спонсорства, стать участником или даже создать ответвления для ключевых библиотек Python Pandas, NumPy и Sci-Kit; и среди прочего разрешить чтение своих сильно сжатых форматов файлов *.*hcc и *.*tkc. Но это общие мысли. Вернемся к TensorFlow. Во-первых, он предлагает расширенные возможности глубокого обучения по части программного обеспечения и графической/аппаратной части.  

MQL5 поддерживает OpenCL, поэтому можно утверждать, что эти два языка могли бы конкурировать, однако этого определенно нельзя сказать о расширенных библиотеках и инструментах Python для создания, обучения и оптимизации моделей глубокого обучения. Эти библиотеки включают поддержку сложных архитектур, таких как SAC, с помощью агентов TensorFlow.

TensorFlow также отличается богатой экосистемой с готовыми инструментами, в том числе для обучения с подкреплением (помимо тензорных агентов); обеспечивает гибкость и экспериментирование, поскольку можно быстро создавать прототипы и тестировать самые разные реализации моделей; обладает высокой воспроизводимостью и легко отлаживается (особенно при использовании таких инструментов, как Tensor-Board для визуализации и обучения матриц/ядер); обеспечивает взаимодействие с экспортируемыми форматами, такими как ONNX; имеет очень обширную и растущую поддержку сообщества и регулярные обновления.

SAC можно реализовать на Python множеством способов. Для иллюстрации основных принципов остановимся лишь на двух подходах. Первый вручную определяет три основные сети SAC и с помощью итераций цикла for обучает и тестирует модель SAC. Второй использует уже упомянутые тензорные агенты, которые поставляются с библиотекой Python и специально предназначены для помощи в обучении с подкреплением.

Шаги ручного итеративного подхода таковы: проектирование компонентов SAC в TensorFlow/Keras, где для сети актеров определяется нейронная сеть, которая выводит стохастическую политику для гауссовой выборки; для сетей критиков это означает построение 2 сетей Q-значений для двойного Q-обучения с целью управления смещением оценки; а также определение режима регуляризации n-энтропии для эффективного исследования. Для наших целей, как указано выше, наша энтропия использует фиксированное значение альфы, равное 0,5. Ниже приведен исходный код, иллюстрирующий эти особенности:

import numpy as np
import pandas as pd

from sklearn.model_selection import train_test_split

import tensorflow as tf

print("Num GPUs Available: ", len(tf.config.list_physical_devices('GPU')))

from tensorflow.keras import optimizers
from tensorflow.keras.layers import Input
from tensorflow.keras import layers
from tensorflow import keras
import tf2onnx
import onnx

import os

# Define the actor network
def ActorNetwork(state_dim, action_dim, hidden_units=256):
    """
    Creates a simple SAC actor network.
    
    :param state_dim: The dimension of the state space.
    :param action_dim: The dimension of the action space.
    :param hidden_units: The number of hidden units in each layer.
    :return: A Keras model representing the actor network.
    """
    # Input layer
    state_input = layers.Input(shape=(state_dim, ))
    
    # Hidden layers (Dense layers with ReLU activation)
    x = layers.Dense(hidden_units, activation='relu')(state_input)
    x = layers.Dense(hidden_units, activation='relu')(x)
    
    # Output layer: output means (mu) and log standard deviation (log_std) for Gaussian distribution
    
    output_size = action_dim + action_dim
    stacked_mean_logs = layers.Dense(output_size)(x)
    
    # Create the model

    actor_model = tf.keras.Model(inputs=state_input, outputs=stacked_mean_logs)
    
    return actor_model

# Define the critic network
def CriticNetwork(state_dim, action_dim, hidden_units=256):
    """
    Creates a simple SAC critic network (Q-value approximation).
    
    :param state_dim: The dimension of the state space.
    :param action_dim: The dimension of the action space.
    :param hidden_units: The number of hidden units in each layer.
    :return: A Keras model representing the critic network.
    """
    
    input_size = state_dim + action_dim
    state_action_inputs = layers.Input(shape=(None, input_size, 1))  # Concatenate state and action
    
    # Hidden layers (Dense layers with ReLU activation)
    x = layers.Dense(hidden_units, activation='relu')(state_action_inputs)
    x = layers.Dense(hidden_units, activation='relu')(x)
    
    # Output layer: Q-value for the given state-action pair
    q_value_output = layers.Dense(1)(x)  # Single output for Q-value
    
    # Create the model 
    critic_model = tf.keras.Model(inputs=state_action_inputs, outputs=q_value_output)
    
    return critic_model

После этого нам необходимо будет обучить модель SAC в TensorFlow/Keras. Обычно это предполагает использование старой библиотеки MetaTrader 5 для импорта данных MetaTrader 5 из терминала MetaTrader, а затем разделение этих данных на тестовые и обучающие. Мы используем коэффициент обучения в размере двух третей, оставляя одну треть для тестирования. Мы моделируем торговую настройку MetaTrader 5 в утомительном и крайне неэффективном цикле for, который, как и ожидалось, имеет размер, соответствующий количеству эпох и размеру обучающих данных. Кроме того, мы стремимся оптимизировать сети акторов и критиков с помощью целевой функции SAC, включая условие энтропии. Ниже представлен код, задействованный в этом:

# Filter the DataFrame to keep only the '<state>' column
df = pd.read_csv(name_csv)

states = df.filter(['<STATE>']).astype(int).values
# Extract the '<state>' column as an integer array
rewards = df.filter(['<REWARD>']).values

states_size = int(len(states)*(2.0/3.0))
actor_x_train = states[0:states_size,:]
actor_x_test = states[states_size:,:1]
rewards_size = int(len(rewards)*(2.0/3.0))
critic_y_train = rewards[0:rewards_size,:]
critic_y_test = rewards[rewards_size:,:1]

# Initialize networks and optimizers
input_dim = 1  # 2 states, of 3 gradations are flattened into a single index
output_dim = 3  # possible actions buy, sell, hold
actor = ActorNetwork(input_dim, output_dim)
critic1 = CriticNetwork(input_dim, output_dim)  # Input paired with action
critic2 = CriticNetwork(input_dim, output_dim)  # Input paired with action

critic_optimizer_1 = tf.keras.optimizers.Adam(learning_rate=0.001)
critic_optimizer_2 = tf.keras.optimizers.Adam(learning_rate=0.001)
actor_optimizer = tf.keras.optimizers.Adam(learning_rate=0.001)

# Training loop
for e in range(epoch_size):
    train_critic_loss1 = 0
    train_critic_loss2 = 0
    train_actor_loss = 0
    
    for i in range(actor_x_train.shape[0]):
        input_state = tf.expand_dims(actor_x_train[i], axis=0)  # Select a single sample and maintain batch dim
        target_q = tf.expand_dims(critic_y_train[i], axis=0)

        # Actor forward pass and sampling
        with tf.GradientTape(persistent=True) as tape:

            actor_output = actor(input_state)
            # Split the vector into mean and log_std
            mu = actor_output[:, :output_dim]     # First 3 values
            log_std = actor_output[:, output_dim:]  # Last 3 values
            std = tf.exp(log_std)
            sampled_action = tf.random.normal(shape=mu.shape, mean=mu, stddev=std)  # Sample action from Gaussian

            # Concatenate the state and action tensors
            in_state = tf.convert_to_tensor(tf.cast(input_state, dtype=tf.float32), dtype=tf.float32)  # Ensure it's a tensor
            in_action = tf.convert_to_tensor(tf.cast(sampled_action, dtype=tf.float32), dtype=tf.float32)
            
            # Concatenate along the last axis
            critic_raw_input = tf.concat([in_state, in_action], axis=-1)  # Ensure correct axis
            critic_input = tf.reshape(critic_raw_input, [-1, 1, 4, 1])  # -1 infers the batch size dynamically

            q_value1 = critic1(critic_input)
            q_value2 = critic2(critic_input)

            # Critic loss (mean squared error)
            critic_loss1 = tf.reduce_mean((tf.cast(q_value1, tf.float32) - tf.cast(target_q, tf.float32)) ** 2)
            critic_loss2 = tf.reduce_mean((tf.cast(q_value2, tf.float32) - tf.cast(target_q, tf.float32)) ** 2)

            # Actor loss (maximize expected Q-value based on minimum critic output)
            min_q_value = tf.minimum(q_value1, q_value2)  # Take the minimum Q-value
            actor_loss = tf.reduce_mean(min_q_value)  # Maximize expected Q-value (negative for minimization)

        # Backpropagation
        critic_gradients1 = tape.gradient(critic_loss1, critic1.trainable_variables)
        critic_gradients2 = tape.gradient(critic_loss2, critic2.trainable_variables)
        actor_gradients = [-grad for grad in tape.gradient(actor_loss, actor.trainable_variables)]

        del tape  # Free up resources from persistent GradientTape
        
        critic_optimizer_1.apply_gradients(zip(critic_gradients1, critic1.trainable_variables))
        critic_optimizer_2.apply_gradients(zip(critic_gradients2, critic2.trainable_variables))
        actor_optimizer.apply_gradients(zip(actor_gradients, actor.trainable_variables))
        
        # Accumulate losses for epoch summary
        train_critic_loss1 += critic_loss1.numpy()
        train_critic_loss2 += critic_loss2.numpy()
        train_actor_loss += actor_loss.numpy()

    print(f"  Epoch {e + 1}/{epoch_size}:")
    print(f"  Train Critic Loss 1: {train_critic_loss1 / actor_x_train.shape[0]:.4f}")
    print(f"  Train Critic Loss 2: {train_critic_loss2 / actor_x_train.shape[0]:.4f}")
    print(f"  Train Actor Loss: {train_actor_loss / actor_x_train.shape[0]:.4f}")
    print("-" * 40)

critic2.summary()
critic1.summary()
actor.summary()

После обучения мы будем открыты для экспорта нашей модели, которая включает три сети, в ONNX. ONNX (Open Neural Network Exchange) представляет собой открытую среду машинного обучения, в которой модели, обученные на Python с использованием различных библиотек, таких как PyTorch или SciKit-Learn, можно экспортировать в этот формат для использования на более широком спектре платформ и языков программирования, одним из которых является MQL5. Такая совместимость устраняет необходимость копирования сложной логики машинного обучения, что экономит время и снижает количество ошибок.

Импорт ONNX в качестве ресурса позволяет скомпилировать один файл ex5, включающий модель машинного обучения ONNX и логику исполнения сделок MQL5, поэтому трейдерам не приходится иметь дело с несколькими файлами. С учетом сказанного, процесс экспорта из Python в ONNX характеризуется несколькими вариантами, один из которых - tf2onnx, но он не единственный: есть также onnxmltools, skl2onnx, transformers.onnx (для облегчения движения) и mxnet.contrib.onnx. Однако на этапе экспорта крайне важно обеспечить корректную регистрацию и запись форм входных и выходных слоёв каждой сети, поскольку в MQL5 эта информация критически важна для точной инициализации соответствующих хэндлов ONNX для каждой сети. Мы делаем это следующим образом:

# Check input and output layer shapes for importing ONNX
import onnxruntime as ort

session_critic2 = ort.InferenceSession(path_critic2_onnx)
session_critic1 = ort.InferenceSession(path_critic1_onnx)
session_actor = ort.InferenceSession(path_actor_onnx)

for i in session_critic2.get_inputs():
    print(f"in critic2 Name: {i.name}, Shape: {i.shape}, Type: {i.type}")

for i in session_critic1.get_inputs():
    print(f"in critic1 Name: {i.name}, Shape: {i.shape}, Type: {i.type}")

for i in session_actor.get_inputs():
    print(f"in actor Name: {i.name}, Shape: {i.shape}, Type: {i.type}")
    

for o in session_critic2.get_outputs():
    print(f"out critic2 Name: {o.name}, Shape: {o.shape}, Type: {o.type}")

for o in session_critic1.get_outputs():
    print(f"out critic1 Name: {o.name}, Shape: {o.shape}, Type: {o.type}")

for o in session_actor.get_outputs():
    print(f"out actor Name: {o.name}, Shape: {o.shape}, Type: {o.type}")

Производительность кода Python с этой реализацией, использующей циклы for, как указано выше, далека от эффективности. По сути, она очень похожа на MQL5, поскольку использование тензоров/графиков не реализовано должным образом. Однако это необходимо, поскольку функция подгонки TensorFlow, которая часто применяется для использования эффективности обучения TensorFlow, в данном случае не была бы применима, учитывая тот факт, что при обратном распространении выходные данные двух сетей-критиков (значения Q) используются для обучения сети актеров. В отличие от сетей критиков или большинства типичных нейронных сетей, у сети актеров нет целевых векторов или целевых наборов данных.

Второй подход к реализации этого подхода использует тензорные агенты, которые представляют собой встроенные библиотеки для обработки подкрепления в TensorFlow и Python. Мы рассмотрим его подробно в следующих статьях, но достаточно сказать, что инициализация охватывает не только составляющие сети, но также учитывает окружающую среду и агентов - ключевые аспекты обучения с подкреплением, которые можно упустить из виду, если чрезмерно сосредоточиться на эффективности обучения сети.



Сочетание с MQL5

Мы импортируем наши экспортированные модели ONNX в ресурсы MQL5, что дает нам следующий заголовок для нашего файла класса пользовательских сигналов.

//+------------------------------------------------------------------+
//|                                                    SignalSAC.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include <Expert\ExpertSignal.mqh>
#include <My\Cql.mqh>
#resource "Python/EURUSD_H1_D1_critic2.onnx" as uchar __CRITIC_2[]
#resource "Python/EURUSD_H1_D1_critic1.onnx" as uchar __CRITIC_1[]
#resource "Python/EURUSD_H1_D1_actor.onnx" as uchar __ACTOR[]
#define  __ACTIONS 3
#define  __ENVIONMENTS 3

Данные, которые мы экспортировали в Python, относились к символу EURUSD на часовом таймфрейме с 2023.12.12 по 2024.12.12. Обучение заняло две трети времени, что составляет восемь месяцев, то есть мы обучались с 12.12.2023 по 12.08.2024. Таким образом, мы сможем проводить испытания с 12.08.2024. Это составляет период чуть более 4 месяцев, что на самом деле не так уж много, но поскольку мы используем часовой таймфрейм, это может быть существенно.

Поскольку обратное распространение уже реализовано в Python, мы не включаем никаких специальных входных параметров для оптимизации в этом прямом проходе. Таким образом, интерфейс нашего класса выглядит так:

//+------------------------------------------------------------------+
//| SACs CSignalSAC.                                                 |
//| Purpose: Soft Actor Critic for Reinforcement-Learning.           |
//|            Derives from class CExpertSignal.                     |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CSignalSAC   : public CExpertSignal
{
protected:

   long                          m_critic_2_handle;
   long                          m_critic_1_handle;
   long                          m_actor_handle;

public:
   void                          CSignalSAC(void);
   void                          ~CSignalSAC(void);

   //--- methods of setting adjustable parameters

   //--- method of verification of arch
   virtual bool      ValidationSettings(void);
   //--- method of creating the indicator and timeseries
   virtual bool      InitIndicators(CIndicators *indicators);
   //--- methods of checking if the market models are formed
   virtual int       LongCondition(void);
   virtual int       ShortCondition(void);

protected:
   vectorf           GetOutput();
   vectorf           LogProbabilities(vectorf &Mean, vectorf &Log_STD);
};

Мы инициализируем и проверяем размеры входного и выходного слоев каждой модели ONNX следующим образом:

//+------------------------------------------------------------------+
//| Validation arch protected data.                                  |
//+------------------------------------------------------------------+
bool CSignalSAC::ValidationSettings(void)
{  if(!CExpertSignal::ValidationSettings())
      return(false);
//--- initial data checks
   if(m_period > PERIOD_H1)
   {  Print(" time frame too large ");
      return(false);
   }
   ResetLastError();
   if(m_critic_2_handle == INVALID_HANDLE)
   {  Print("Crit 2 OnnxCreateFromBuffer error ", GetLastError());
      return(false);
   }
   if(m_critic_1_handle == INVALID_HANDLE)
   {  Print("Crit 1 OnnxCreateFromBuffer error ", GetLastError());
      return(false);
   }
   if(m_actor_handle == INVALID_HANDLE)
   {  Print("Actor OnnxCreateFromBuffer error ", GetLastError());
      return(false);
   }
   // Set input shapes
   const long _critic_in_shape[] = {1, 4, 1};
   const long _actor_in_shape[] = {1};
   // Set output shapes
   const long _critic_out_shape[] = {1, 4, 1, 1};
   const long _actor_out_shape[] = {1, 6};
   if(!OnnxSetInputShape(m_critic_2_handle, ONNX_DEFAULT, _critic_in_shape))
   {  Print("Crit 2  OnnxSetInputShape error ", GetLastError());
      return(false);
   }
   if(!OnnxSetOutputShape(m_critic_2_handle, 0, _critic_out_shape))
   {  Print("Crit 2  OnnxSetOutputShape error ", GetLastError());
      return(false);
   }
   if(!OnnxSetInputShape(m_critic_1_handle, ONNX_DEFAULT, _critic_in_shape))
   {  Print("Crit 1 OnnxSetInputShape error ", GetLastError());
      return(false);
   }
   if(!OnnxSetOutputShape(m_critic_1_handle, 0, _critic_out_shape))
   {  Print("Crit 1 OnnxSetOutputShape error ", GetLastError());
      return(false);
   }
   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);
   }
//read best weights
//--- ok
   return(true);
}

Что касается форм слоев, то следует отметить, что нам пришлось внести изменения, которые облегчили наш экспорт в ONNX, хотя они и противоречили базовой логике SAC. Во-первых, сеть актеров должна экспортировать 2 вектора: векторы средних значений и вектор логарифмического стандартного отклонения. Необходимость определения их в форме слоя ONNX привела бы к ошибкам, поэтому мы объединили их в один вектор в Python, как указано в коде цикла for выше. Кроме того, входные данные для сетей критиков представляют собой состояние окружающей среды и распределение вероятностей действий, предоставляемое сетью актеров. Обычно это также можно представить в виде двух тензоров, однако для простоты мы снова объединили их в один вектор размером 4. Наша функция получения выходных данных выглядит так:

//+------------------------------------------------------------------+
//| This function calculates the next actions to be selected from    |
//| the Reinforcement Learning Cycle.                                |
//+------------------------------------------------------------------+
vectorf CSignalSAC::GetOutput()
{  vectorf _out;
   int _load = 1;
   static vectorf _x_states(1);
   _out.Init(__ACTIONS);
   _out.Fill(0.0);
   vector _in, _in_row, _in_row_old, _in_col, _in_col_old;
   if
   (
      _in_row.Init(_load) &&
      _in_row.CopyRates(m_symbol.Name(), PERIOD_H1, 8, 0, _load) &&
      _in_row.Size() == _load
      &&
      _in_row_old.Init(_load) &&
      _in_row_old.CopyRates(m_symbol.Name(), PERIOD_H1, 8, 1, _load) &&
      _in_row_old.Size() == _load
      &&
      _in_col.Init(_load) &&
      _in_col.CopyRates(m_symbol.Name(), PERIOD_D1, 8, 0, _load) &&
      _in_col.Size() == _load
      &&
      _in_col_old.Init(_load) &&
      _in_col_old.CopyRates(m_symbol.Name(), PERIOD_D1, 8, 1, _load) &&
      _in_col_old.Size() == _load
   )
   {  _in_row -= _in_row_old;
      _in_col -= _in_col_old;
      Cql *QL;
      Sql _RL;
      _RL.actions  = __ACTIONS;//buy, sell, do nothing
      _RL.environments = __ENVIONMENTS;//bullish, bearish, flat
      QL = new Cql(_RL);
      vector _e(_load);
      QL.Environment(_in_row, _in_col, _e);
      delete QL;
      _x_states[0] = float(_e[0]);
      static matrixf _y_mu_logstd(6, 1);
//--- run the inference
      ResetLastError();
      if(!OnnxRun(m_actor_handle, ONNX_NO_CONVERSION, _x_states, _y_mu_logstd))
      {  Print("Actor OnnxConversion error ", GetLastError());
         return(_out);
      }
      else
      {  vectorf _mu(__ACTIONS), _logstd(__ACTIONS);
         _mu.Fill(0.0); _logstd.Fill(0.0);
         for(int i=0;i<__ACTIONS;i++)
         {  _mu[i] = _y_mu_logstd[i][0];
            _logstd[i] = _y_mu_logstd[i+__ACTIONS][0];
         }
         _out = LogProbabilities(_mu, _logstd);
      }
   }
   return(_out);
}

Мы придерживаемся той же модели, которую использовали до сих пор: 9 состояний среды и 3 возможных действия. Для обработки распределения вероятностей действий нам понадобится функция логарифмических вероятностей, код которой был представлен в начале этой статьи. Компиляция с помощью Мастера и выполнение тестового запуска для оставшихся 4 месяцев окна данных дают следующие результаты:

r1

c1


Заключение

Мы рассмотрели очень простой сценарий реализации обучения с подкреплением SAC на Python, в котором не использовалась библиотека тензорного агента со всеми ее преимуществами. Такой подход позволил изучить основы SAC и понять, почему обратное распространение ошибки занимает немного больше времени - поскольку оно включает в себя объединение нескольких сетей, и ни одна из них не имеет типичного набора данных для обучения. SAC призван безопасно способствовать дальнейшему исследованию посредством энтропии, модулируемой параметром альфа (который мы применили в гауссовом распределении). Читателю предлагается изучить этот вопрос более подробно, рассмотрев нефиксированную альфу, например, автоматизированную корректировку, имеющую целевое значение энтропии. Нам также следует рассмотреть подобные примеры в будущих статьях.

Название файла Описание
WZ_51.mq5 Собранный в Мастере советник, заголовок которого отображает используемые файлы
SignalWZ_51.mqh
Файл класса пользовательских сигналов
EURUSD_H1_D1_critic2.onnx Сеть Critic 2 ONNX
EURUSD_H1_D1_critic1.onnx Сеть Critic 1 ONNX
EURUSD_H1_D1_actor.onnx Сеть актеров

Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/16695

Прикрепленные файлы |
WZ_51.mq5 (6.19 KB)
SignalWZ_51.mqh (10.32 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (1)
MuhireInnocent
MuhireInnocent | 20 дек. 2024 в 11:54
Здравствуйте, Стивен, спасибо за ваши познавательные статьи, я предлагаю вам добавить в экономические календари исторические данные по nfp, cpi и процентным ставкам, поскольку эти данные сильно влияют на рынок.
Особенности написания Пользовательских Индикаторов Особенности написания Пользовательских Индикаторов
Написание пользовательских индикаторов в торговой системе MetaTrader 4
От новичка до эксперта: Система автогеометрического анализа От новичка до эксперта: Система автогеометрического анализа
Геометрические паттерны предлагают трейдерам лаконичный способ интерпретации ценового движения. Многие аналитики рисуют линии тренда, прямоугольники и другие фигуры вручную, а затем основывают торговые решения на тех формациях, которые они видят. В настоящей статье мы рассмотрим автоматизированную альтернативу: использование MQL5 для обнаружения и анализа наиболее популярных геометрических паттернов. Мы разберем методологию, обсудим детали реализации и расскажем о том, как автоматическое распознавание паттернов может улучшить понимание рынка трейдером.
Особенности написания экспертов Особенности написания экспертов
Написание и тестирование экспертов в торговой системе MetaTrader 4.
Изучение передовых методов машинного обучения в стратегии пробоя «коридора Дарваса» (Darvas Box Breakout) Изучение передовых методов машинного обучения в стратегии пробоя «коридора Дарваса» (Darvas Box Breakout)
Стратегия Darvas Box Breakout, созданная Николасом Дарвасом, представляет собой подход в технической торговле, который выявляет потенциальные сигналы на покупку, когда цена акций поднимается выше установленного диапазона «коридора», что указывает на сильный восходящий импульс. В этой статье мы применим эту стратегическую концепцию в качестве примера для изучения трех передовых методов машинного обучения. К ним относятся использование модели машинного обучения для генерации сигналов вместо фильтрации сделок, применение непрерывных сигналов вместо дискретных и использование для подтверждения сделок моделей, обученных на разных таймфреймах.