Возможности Мастера MQL5, которые вам нужно знать (Часть 08): Перцептроны
Введение
Класс сигналов советников Мастера MQL5 содержит множество примеров в папке Include\Expert\Signal. Каждый из них можно использовать независимо или комбинировать друг с другом при создании советника в Мастере. В этой статье мы создадим и используем один такой файл в советнике. Этот подход, помимо минимизации усилий по предварительному кодированию, позволяет тестировать более одного сигнала в одном советнике, присваивая вес каждому используемому сигналу.
Классы перцептрона Alglib представлены в виде обширных и взаимосвязанных сетевых интерфейсов в файле Include\Math\Alglib\dataanalysis.mqh. При первом взгляде легко растеряться, но мы рассмотрим здесь несколько важных классов, которые, мы надеемся, облегчат навигацию в этой области.
Основная цель использования этих классов Alglib для разработки советника та же, что и для использования Мастера MQL5, - тестирование идей. Как я могу кратко определить, стоит ли идея x или набор входных данных y моих усилий по дальнейшему развитию торговой системы? Статья может помочь ответить на этот вопрос.
Прежде чем мы начнем, возможно, было бы полезно более широко объяснить, почему перцептроны и, возможно, нейронные сети в целом набирают популярность. Если мы сосредоточимся на финансах и рынках, мы увидим, что существует немало проблем в прогнозировании действий рынка, связанных с ограничениями традиционного анализа.
Эти проблемы существуют, поскольку рынки представляют собой очень сложные и часто динамичные системы, на которые влияет нечто большее, чем то, что появляется в новостях (публичная информация). Отношения между различными рыночными переменными в большинстве случаев не линейны и очень переменчивы. Традиционные методы анализа, основанные на линейности могут оказать неспособными эффективно уловить и учесть эти сложности. Примеры этих традиционных методов могут включать такие подходы, как корреляция, PCA или линейная регрессия. Всё это очень разумные тактики, но они все чаще оказываются неуместными. Кроме того, рыночные данные зашумлены, поскольку на движения рынка влияют не только настроения инвесторов, но и поведенческие предубеждения потребителей. Таким образом, традиционный технический анализ становится ограниченным из-за того, что он полагается на исторические данные без должного учета всей рыночной динамики. В свою очередь, фундаментальный анализ, который учитывает долгосрочную перспективу, склонен к краткосрочному риску, особенно в том, что касается ценового движения. Хотя кредитное плечо обычно не используется теми, кто полагается на фундаментальный анализ, большинство согласится, что оно является важным компонентом в выравнивании активов под управлением и, следовательно, риска в долгосрочной перспективе. Тем не менее, кредитное плечо не может использоваться без учета краткосрочного ценового движения.
Новые альтернативы этим двум традиционным подходам - поведенческая экономика и методы искусственного интеллекта на основе нейронных сетей. Мы остановимся на втором варианте.
Недавно на финансовых рынках произошел переворот из-за внедрения технологий искусственного интеллекта в связи с запуском ChatGPT. Например, довольно много крупных компаний вступило в гонку. В этой связи можно упомянуть BloombergGPT и EinsteinGPT от Salesforce. Однако в этой статье мы поговрим не о GPT, а об их очень упрощенной - перцептронах.
Тем не менее, растущий интерес к методам искусственного интеллекта для прогнозирования отчасти объясняется обширными финансовыми данными, которые сейчас собираются и хранятся во все возрастающих объемах. Помните времена, когда технические аналитики заботились только о дневной цене закрытия ценных бумаг? Сегодня все знают о ценах OHLC одноминутного бара, не говоря уже о тиках, частота которых варьируется от брокера к брокеру.
Эта перегрузка данными происходит одновременно с повышением вычислительной мощности благодаря здоровой конкуренции среди поставщиков чипов. Вчера было объявлено, что NVIDIA вскоре станет крупнейшим в мире поставщиком чипов, во многом благодаря растущему спросу на графические процессоры, которые сейчас в моде в связи с распространением GPT. Таким образом, увеличение объема хранения данных и рост вычислительных возможностей ведут к более алгоритмической торговле. И хотя алгоритмическую торговлю можно вести с помощью традиционного технического и фундаментального анализа, методы искусственного интеллекта, использующие нейронные сети, привлекают все больше внимания.
Нейронные сети, как правило, лучше справляются с обработкой больших объемов данных и выявлением сложных нелинейных закономерностей. Обычно они достигают этого, адаптируясь к изменяющейся среде посредством глубокого обучения - многоуровневой сети, в которой определенные скрытые слои специализируются на определенных задачах, так что прогнозирование в типичных турбулентных/меняющихся условиях является для них наилучшей областью применения. Помимо финансов, они могут анализировать неструктурированные данные, такие как новостные статьи или сообщения в социальных сетях, оценивать настроения рынка, эффективность клинических испытаний лекарств, а также применяться во многих других областях.
Обзор классов перцептронов Alglib
Как уже упоминалось, иерархия классов перцептронов Alglib представляет собой обширную библиотеку классов, реализующих нейронные сети, от простых перцептронов, которые мы рассматриваем в этой статье, до ансамблей, которые, будучи синонимами преобразователей, представляют собой стеки нейронных сетей. Так как мы рассматриваем только самую простую нейронную сеть, называемую перцептроном, мы рассмторим только классы CMLPBase, CMLPTrain, CMLPTrainer и CMultilayerPerceptron. Мы будем использовать и другие второстепенные вспомогательные классы, например класс, обрабатывающий отчеты, или класс, помогающий нормализовать наборы данных, но мы выделим основные из них.
Класс CMLPBase используется для инициализации сети, определяя количество скрытых слоев, которые будет иметь сеть, а также количество нейронов на каждом слое. Класс CMLPTrain инициализирует класс тренера, устанавливая количество входных данных, которые будет принимать сеть, а также количество ее выходных данных. Кроме того, он заполняет тренер набором обучающих данных, который должен быть в матричной форме, где первые столбцы содержат независимые переменные, а последний столбец содержит регрессор или классификатор в зависимости от типа используемой сети. В нашем случае это будет классификатор, поскольку перцептроны обычно выдают логический результат. Класс CMLPTrainer используется при обучении при вызове функции MLPTrainNetwork класса CMLPTrain. Существуют альтернативные очень интересные методы обучения, такие как агрегирование начальной загрузки (boot-strap-aggregating), вызываемое функцией MLPEBaggedLM, но их можно использовать только с ансамблями (стеками сетей). Кроме того, для обучения сети можно использовать такие алгоритмы, как ранняя остановка, LBFGS и Левенберга-Марквадта.
Методы, используемые этими классами, охватывают типичный маршрут нейронных сетей от загрузки обучающих данных до выполнения фактического обучения и, наконец, перехода к текущему набору данных для прогнозирования.
Итак, классы написаны так, как работает нейронная сеть. Во время работы входные данные передаются через сеть, начиная с первого уровня, который в этих классах называется входным слоем, затем на скрытые слои и, наконец, на выходной уровень. Кроме того, активация значений обычно выполняется на каждом нейроне, и именно эта активация позволяет сетям обрабатывать сложные отношения, выходящие за рамки линейных, действуя как фильтр, который позволяет выбранным значениям перейти на следующий уровень. Этот процесс является итеративным, но относительно простым, поскольку почти всегда он представляет собой случай умножения и сложения, при этом на результат выходного слоя в основном влияют веса и смещения на каждом уровне. Таким образом, именно эти веса и смещения составляют суть нейронных сетей, и процесс их корректировки не только требует больших вычислительных ресурсов, но и привел к развитию различных подходов, поскольку он не так прост, как прямой проход. Этот метод лучше всего работает в различных типах сетей, поскольку нейронные сети имеют множество приложений.
Итак, функция прямой передачи для сетей в AlgLib называется MLPProcess. У него есть разновидности, но принцип действия неизменен - он принимает входные данные в векторе или массиве и предоставляет значения выходного слоя, как правило, также в векторе или массиве. Существуют сети с одним нейроном на выходном слое, и в таких случаях происходит перегрузка этой функции, которая возвращает одно значение, а не массив.
Важно отметить, что даже несмотря на то, что мы кодируем и используем один перцептрон скрытого слоя, наш эталонный класс называется многослойным перцептроном, поскольку он масштабируем, так как количество скрытых слоев для любой инициализированной сети может быть установлено во время выполнения, и оно варьируются в диапазоне от 0 до 2.
Если мы попытаемся немного приблизить работу типичной прямой связи, мы сможем взглянуть на функцию MLPInternalProcessVector. Одним из первых действий для этой функции является нормализация строки входных данных, чтобы все значения этого входного массива были более взаимосвязанными.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CBdSS::DSNormalize(CMatrixDouble &xy,const int npoints, const int nvars,int &info,CRowDouble &means, CRowDouble &sigmas) { //--- function call DSNormalizeC(xy,npoints,nvars,info,means,sigmas); //--- calculation for(int j=0; j<nvars; j++) { //--- change values for(int i=0; i<npoints; i++) xy.Set(i,j,(xy.Get(i,j)-means[j])/sigmas[j]); } }
Для этого необходимо использовать средние значения и стандартное отклонение (сигмы) каждого столбца входного вектора для получения значений в диапазоне 0 – 1. Поэтому средние значения и сигмы необходимо определять вручную из наборов обучающих данных, а затем назначать сети. Уже существуют функции, которые могут выполнять подобные вычисления в том же файле Alglib DSNormalize, как показано ниже:
//+------------------------------------------------------------------+ //| Normalize | //+------------------------------------------------------------------+ void CBdSS::DSNormalize(CMatrixDouble &xy,const int npoints, const int nvars,int &info,double &means[], double &sigmas[]) { CRowDouble Means,Sigmas; DSNormalize(xy,npoints,nvars,info,Means,Sigmas); Means.ToArray(means); Sigmas.ToArray(sigmas); }
Также стоит отметить массив m_structinfo, который используется для хранения ключевой информации о сети, такой как общее количество нейронов, тип используемой активации, общее количество весов, количество нейронов во входном слое и количество нейронов в выходном слое.
После нормализации данные передаются через сеть, и каждый нейрон на каждом уровне может иметь свою собственную функцию активации. Эту настройку можно определить с помощью функции MLPSetNeuronInfo, которую можно легко использовать в качестве преимущества при построении сети.
Прямая подача перцептрона относительно проста по сравнению с обучением и регулировкой весов сети. Alglib предоставляет в основном два подхода к обучению - алгоритм Левенберга-Марквардта и LBFGS.
При поиске нелинейного решения методом наименьших квадратов алгоритм Левенберга-Марквардта объединяет скорость алгоритма Гаусса-Ньютона и гибкость алгоритма градиентного спуска в точках высокой кривизны решения. При этом он использует матрицу Гессе для регистрации кривизны поверхности как оценку близости к решению. Его приложения в основном находятся в нейронных сетях, где он эффективен при обработке невыпуклых поверхностей ошибок, особенно в ситуациях, когда задействованы небольшие наборы данных с относительно простой сетевой архитектурой, поскольку вычисление матрицы Гессе довольно трудоемко.
LBFGS (алгоритм Бройдена-Флетчера-Гольдфарба-Шанно с ограниченной памятью), вместо вычисления матрицы Гессе, аппроксимирует ее, используя ограниченную память, регистрируя самые последние обновления веса сети, что делает его в целом очень эффективным в вычислениях и управлении памятью. В связи с этим он больше подходит для ситуаций с большими наборами данных и относительно сложной сетевой архитектурой.
Сравнение свойств сходимости этих двух методов наводят на мысль о предпочтительности алгоритма Левенберга-Марквардта, поскольку он может быстро находить точное решение даже в ситуациях, когда первоначальное предположение было далеко от истины (например, когда сеть инициализируется со случайными весами). Кроме того, в отличие от градиентного спуска, он менее склонен к застреванию в локальных минимумах. Это делает его немного более надежным, отчасти благодаря использованию лямбда-коэффициента демпфирования. С другой стороны, на LBFGS больше влияет первоначальное предположение (в нашем случае начальные веса сети) и он должен сходиться медленнее или застревать на локальных минимумах.
Экземпляр класса сигнала советника
Дополнительную информацию о перцептронах можно найти здесь. Рассмотрим реализацию экземпляра в коде. Создание советника с помощью Мастера MQL5 требует знания трех типичных классов, которые определяют советник на основе Мастера, а именно класс сигнала, который является предметом нашей внимания в этой статье, класс трейлинга, который определяет, как устанавливаются стоп-лоссы для открытых позиций, и класс управления капиталом, который помогает устанавливать размеры торговых лотов. Об этом уже говорилось в предыдущих статьях. Все три класса необходимо определить и выбрать в Мастере во время сборки. Несмотря на то, что класс управления капиталом предлагает оптимизированный по размеру объем торговли, можно было бы использовать четвертый класс Мастера, который рассматривает риск - насколько безопасно советнику размещать несколько ордеров в пределах одной позиции - на основе торговой истории или данных какого-либо индикатора, но это не является предметом статьи.
Чтобы реализовать экземпляр классов перцептронов Alglib как однослойный перцептрон, мы должны начать с объявления экземпляров наших ключевых классов в интерфейсе нашего пользовательского класса сигналов советника. В файлах классов сигналов всегда есть функции LongCondition и ShortCondition. Другой важный метод, который нам понадобится помимо функций инициализации и проверки, - функция для вычисления или обработки сигнала перцептрона.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CSignalPerceptron : public CExpertSignal { protected: int m_hidden; // int m_features; // int m_hidden_1_size; // int m_hidden_2_size; // int m_training_points; // int m_training_restarts; // int m_activation_type; // double m_hidden_1_bias; // double m_hidden_2_bias; // double m_output_bias; // public: ... protected: CBdSS m_norm; CMLPBase m_base; CMLPTrain m_train; CMatrixDouble m_xy; CMLPReport m_report; CMLPTrainer m_trainer; CMultilayerPerceptron m_network; bool m_in_training; int m_inputs; int m_outputs; double m_last_long_y; double m_last_short_y; bool ReadWeights(); bool WriteWeights(CRowDouble &Export); void Process(double &Y); };
Функция проверки де-факто служит нашей функцией инициализации в этом экземпляре класса сигналов советника. У нас есть встроенная функция инициализации, но проверка подходит нам больше. Прежде всего, мы назначаем количество входов и выходов для нашего перцептрона. Количество входных данных можно будет оптимизировать, поэтому оно считывается из параметра, но количество выходных данных, поскольку это классификация, а не регрессия, должно быть не менее 2.
С этой целью, для простоты, мы установим количество выходов на 2. Затем мы изменяем размер матрицы обучающих данных, чтобы в ней были строки, соответствующие количеству обучающих точек, которые мы будем учитывать на каждом столбце при обработке направления. Его столбцы должны соответствовать сумме количества входов и выходов. Выходные данные, равные 2, представляют собой обучение двух весовых коэффициентов для бычьего и медвежьего направления, и фактически проход вперед аналогичным образом вернет две вероятности: одну для длинной позиции, а другую для короткой, обе из которых в сумме равны единице. После этого создаем трейнер, задав для него количество входов и выходов.
m_train.MLPCreateTrainerCls(m_inputs,m_outputs,m_trainer);
После этого следует создание сети, и в зависимости от количества выбранных скрытых слоев мы будем использовать для этого разные функции, причем каждая функция позволяет определить количество входных нейронов, количество нейронов в каждом скрытом слое (если они используются) и, наконец, количество нейронов в выходном слое.
if(m_hidden==0) { m_base.MLPCreateC0(m_inputs,m_outputs,m_network); } else if(m_hidden==1) { m_base.MLPCreateC1(m_inputs,m_hidden_1_size,m_outputs,m_network); } else if(m_hidden==2) { m_base.MLPCreateC2(m_inputs,m_hidden_1_size,m_hidden_2_size,m_outputs,m_network); } else if(m_hidden>2||m_hidden<0) { printf(__FUNCSIG__+" invalid number of hidden layers should be 0, 1, or 2. "); return(false); }
Наконец мы настраиваем функции активации скрытого и выходного слоев, а также смещения слоев. Классы Alglib достаточно универсальны, поэтому функции активации и смещения можно настраивать не только для каждого слоя, но и для каждого нейрона. Однако в этой статье мы рассмотрим нечто упрощенное.
Помимо инициализации и проверки нашей сети, нам необходимо иметь необходимые средства для изучения идеальных весов сети через систему их хранения и чтения, когда это необходимо. Здесь можно рассмотреть ряд различных подходов, но мы используем просто запись файла в массив весов сети после прохождения теста, когда критерий тестирования советника превосходит предыдущий тест. При следующем запуске наша сеть инициализируется, считывая эти веса, и с каждым последующим обучением они улучшаются. Запись весов в файл и их чтение осуществляется функциями WriteWeights и ReadWeights соответственно.
Наконец, функция Process выполняется на каждом новом баре для обучения нашей сети новым данным, а затем обработки текущего сигнала, который называется переменной Y. Здесь следует отметить несколько вещей: во-первых, матрицу тестовых данных m_xy необходимо нормализовать по столбцам так, чтобы каждое значение в матрице находилось в диапазоне от -1,0 до +1,0. Как упоминалось выше, это может быть сделано с помощью других функций в классах Alglib, и они находятся в том же файле, что и классы перцептрона. Конечно, можно настроить этот подход, чтобы сделать его более подходящим для конкретной ситуации, но для наших целей будут использоваться встроенные функции.
//normalise data
CRowDouble _means,_sigmas;
m_norm.DSNormalize(m_xy,m_training_points,m_inputs,_info,_means,_sigmas);
Во-вторых, обучение сети осуществляется двумя функциями в зависимости от того, начинаем ли мы процесс обучения или уже выполнили обучающий прогон. Как только мы начнем обучение, мы можем сохранить веса, полученные на предыдущем проходе, и продолжить обучение с ними, чтобы постоянно не возиться со случайными весами. Обучающая функция по умолчанию всегда рандомизирует веса, и если бы мы ее использовали, мы бы рандомизировали наши веса на каждом новом баре!
m_train.MLPSetDataset(m_trainer,m_xy,m_training_points); // if(!m_in_training) { m_train.MLPStartTraining(m_trainer,m_network,false); m_in_training=true; } else if(m_in_training) { while(m_train.MLPContinueTraining(m_trainer,m_network)) { // } }Благодаря Мастеру интеграция этого завершенного класса сигналов с классом трейлинга и классом управления капиталом в советник осуществляется без проблем за 6 шагов, которые были сокращены до пяти на скриншотах в разделе "Сборка и тестирование". Выполнив их, мы должны получить советник со следующими включаемыми файлами:
//+------------------------------------------------------------------+ //| Include | //+------------------------------------------------------------------+ #include <Expert\Expert.mqh> //--- available signals #include <Expert\Signal\My\SignalPerceptron.mqh> //--- available trailing #include <Expert\Trailing\TrailingNone.mqh> //--- available money management #include <Expert\Money\MoneyFixedMargin.mqh>
Сборка и тестирование советника
Соберем советник на основе нашего пользовательского класса сигналов с помощью Мастера. Здесь всё довольно просто (см. скриншоты).
Если мы протестируем нашего скомпилированного советника перед любой оптимизацией, мы получим следующий отчет:
Если мы проведем оптимизацию нашего советника с форвард-окном, то получим следующие результаты:
Мы обучили наш перцептрон и экспортировали его веса на основе критериев оптимизации нашего советника. Более краткий способ сделать это — использовать встроенные возможности перекрестной проверки или даже использовать что-то более простое, например, значение среднеквадратичной ошибки отчета, если бэггинг не используется. В любом сценарии мы сохраняем веса, которые с большей вероятностью будут соответствовать обучающим классификаторам. Судя по нашим тестам, сеть показывает многообещающие результаты, но, как всегда, следует помнить о большей тщательности в тестировании на более длительных промежутках времени с использованием тиковых данных вашего брокера.
Заключение
Мы рассмотрели, как перцептроны могут быть реализованы с минимальным количеством кода благодаря классам кода Alglib. Мы выделили несколько предварительных шагов, таких как нормализация набора данных, которые необходимо предпринять, прежде чем перцептроны станут достойными тестирования и изучения. Кроме того, мы показали дополнительные меры, которые стоит рассмотреть, когда у вас есть перцептроны, готовые к тестированию. Все эти шаги и дополнительные меры, такие как экспорт настраиваемых параметров, выполняются с помощью вспомогательного кода из классов Alglib.
Итак, преимущества использования классов Alglib заключаются, прежде всего, в минимизации количества кода и времени, необходимого для создания тестируемой системы. Но есть и недостатки, особенно когда дело касается настройки. Например, наши перцептроны не могут иметь более двух скрытых слоев. В сценариях, где моделируются сложные наборы данных, это может стать узким местом.
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/13832
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования