
Нейросети в трейдинге: Устойчивые торговые сигналы в любых режимах рынка (Модули внимания)
Введение
Финансовые рынки — это живой организм, который дышит, развивается и постоянно меняется. Здесь нет абсолютной стабильности. Привычные корреляции между активами могут исчезнуть всего за несколько торговых сессий. Рынок — это не только числа на графиках, но и отражение человеческих эмоций, ожиданий и решений, которые переплетаются в сложнейшую сеть взаимосвязей. И в этом мире хаоса и неопределённости особенно ценны инструменты, способные уловить скрытый порядок.
Фреймворк ST-Expert, о котором мы начали разговор в предыдущей статье, как раз и был создан для того, чтобы искать закономерности там, где обычные методы анализа бессильны. Его главная особенность — умение сочетать в себе два измерения: временное и пространственное. Временной аспект — это классика финансового прогнозирования: анализ исторических данных, попытка на основе прошлых котировок предсказать будущее. Пространственный аспект добавляет новый уровень понимания: рынок рассматривается как сеть, где каждый актив связан с другими, а изменения в одном сегменте неизбежно отражаются на соседних.
Представьте, что мы анализируем рынок акций. Если цена нефти начинает расти, это почти сразу же отражается на котировках нефтяных компаний. А за ними могут последовать валюты нефтедобывающих стран и целые сектора смежной промышленности. Классическая модель, построенная только на временных рядах цен, видит лишь прямую линию движения конкретного инструмента. ST-Expert же способен уловить весь каскад изменений, потому что он работает не только со временем, но и с пространством рыночных связей.
Архитектура фреймворка основана на принципе Mixture Of Experts — смеси экспертов. Этот подход можно сравнить с работой инвестиционного совета. В нём собираются аналитики разного профиля: один специализируется на техническом анализе, другой на макроэкономике, третий следит за корреляциями между секторами. По отдельности каждый из них ограничен в своих возможностях. Но когда их мнения складываются, итоговое решение оказывается куда ближе к реальности. Так же и в ST-Expert — каждый экспертный блок отвечает за свой участок анализа, а система объединяет их прогнозы, формируя целостное решение.
Такое распределение задач даёт системе три ключевых преимущества:
- Адаптивность. Рынок меняется каждую минуту, и система должна меняться вместе с ним. Если вчера ключевой драйвер движения был в сырьевом секторе, а сегодня им стали валюты, ST-Expert перестраивается, меняя акценты между экспертами.
- Устойчивость. Когда один из экспертов ошибается, общий прогноз сохраняет качество за счёт коллективного согласования. Это снижает риск сваливания в одну стратегию и делает систему более надёжной.
- Интерпретируемость. Для трейдера важно понимать не только результат прогноза, но и его причины. В ST-Expert всегда можно увидеть, какой эксперт внёс основной вклад, какие взаимосвязи оказались значимыми. Это делает систему не чёрным ящиком, а инструментом, с которым можно работать осознанно.
Если говорить языком трейдинга, то ST-Expert — это не просто индикатор, а целый навигационный комплекс. Он учитывает трендовые течения, ловит локальные импульсы, анализирует корреляции между активами и строит карту рынка в режиме реального времени. Более того, система умеет обновлять эту карту, когда привычные маршруты оказываются перекрыты. Для практической торговли это означает меньше ложных сигналов и больше решений, которые соответствуют реальной динамике рынка.
Архитектура фреймворка включает несколько ключевых элементов. В центре — графовая модель, где вершины представляют активы или временные точки, а рёбра — связи между ними. Эти связи могут быть прямыми и очевидными, как зависимость золота от курса доллара, или скрытыми, как корреляции между отдельными акциями внутри сектора. Задача системы — не просто зафиксировать эти связи, но и уметь их перестраивать, когда рынок диктует новые условия.
Важнейшую роль играет модуль адаптивного комбинирования графов. Его можно сравнить с дирижёром оркестра. У каждого инструмента своя партия, но именно дирижёр решает, какой голос должен зазвучать громче в нужный момент. Если одни связи теряют значимость, система усиливает другие, сохраняя целостность прогноза.
Не менее важен и обучающий механизм ST-Expert. Он построен так, чтобы система знакомилась с разными сценариями. Это позволяет ей быть готовой к новым ситуациям — будь то внезапный новостной всплеск, смена корреляций или появление новых рыночных драйверов. В языке трейдинга это сродни подготовке к любым рыночным сценариям: от спокойного флэта до бурного тренда.
Авторская визуализация фреймворка ST-Expert представлена ниже.
В предыдущей статье мы не ограничились теорией и сделали первый шаг к практической реализации подходов ST-Expert средствами MQL5. Нами был построен объект генерации графонов — ключевой элемент архитектуры, отвечающий за моделирование вероятностных связей. Этот фундаментальный компонент закладывает основу для адаптивного анализа пространственных структур.
Сегодня мы продолжаем начатую работу. Наша цель — развивать реализацию фреймворка шаг за шагом, превращая его из набора идей в полноценный инструмент алгоритмической торговли.
Идеи использования
Переходя к работе над моделью, стоит вспомнить ещё одну важную особенность фреймворка ST-Expert — его универсальность. Авторы фреймворка подчёркивают, что предложенные ими подходы не ограничиваются лишь использованием в графовых моделях. Напротив, они могут быть встроены в самые разные классические модели, усиливая их и наделяя новыми свойствами. По сути, речь идёт о модульности. ST-Expert можно воспринимать как набор адаптивных компонентов, которые способны работать как самостоятельно, так и в составе более крупных систем.
Эта универсальность особенно ценна в финансовом контексте. Ведь трейдер или исследователь редко ограничивается одной моделью — чаще всего используется целый набор инструментов. Возможность интегрировать в них подходы ST-Expert открывает простор для экспериментов. Можно усиливать их устойчивость к пространственным сдвигам, расширять возможности анализа или добавлять новый уровень интерпретируемости.
В данной работе мы решили развить именно это направление. В качестве экспериментальной базы выбран фреймворк Extralonger. В его основе лежит идея сочетания локальных и глобальных факторов, что позволяет более точно моделировать сложные процессы на финансовом рынке.
Интеграция ST-Expert и Extralonger выглядит логичным шагом. Первый привносит в систему механизм работы с динамическими графами и смесью экспертов, умеющих адаптироваться к изменению структуры связей. Второй предоставляет отлаженную среду для работы с временными рядами и уже реализованные модули пространственно-временного анализа. Вместе они образуют симбиоз, где каждая сторона усиливает другую. Extralonger получает дополнительный уровень адаптивности и устойчивости, а ST-Expert — проверенную платформу для внедрения в алгоритмическую торговлю.
Представим, как это может выглядеть на практике. Допустим, Extralonger анализирует поведение валютной пары и формирует прогноз на основе временной динамики. Однако в какой-то момент на рынок начинают активно влиять сырьевые котировки или фондовые индексы. Если использовать только базовый функционал Extralonger, такая смена драйверов может остаться незамеченной. Но встроенные модули ST-Expert, работающие с графонами и экспертными блоками, уловят новые связи и перестроят картину. В результате прогноз сохраняет точность, даже если рыночные условия радикально изменились.
Чтобы перейти от идеи к практической работе, необходимо определить точки интеграции. Напомню, что в основе фреймворка Extralonger лежит трёхмаршрутный Transformer, построенный по модульному принципу. Он объединяет три типа анализа: временной, пространственный и смешанный. Каждый из маршрутов отвечает за собственный слой понимания рыночных данных, а их совместная работа позволяет формировать целостный прогноз.
Временной анализ в Extralonger реализован через классический Transformer, который работает с последовательностью данных и извлекает закономерности из временных рядов. Этот модуль напоминает фундамент — он отвечает за прочное основание анализа, улавливая привычные динамические зависимости.
Пространственный анализ построен на глобально-локальном подходе. Здесь архитектура рассматривает рынок как сеть взаимосвязанных узлов, в которых соседние элементы формируют локальные паттерны, а дальние связи добавляют глобальное понимание. Такой подход особенно важен в финансовом контексте, где инструменты могут быть связаны как напрямую, так и через более сложные механизмы (например, технологический сектор и фондовые индексы).
Наконец, смешанный маршрут объединяет оба подхода — временной и пространственный. Он позволяет модели синтезировать разные уровни анализа, создавая своего рода объёмное зрение. Благодаря этому, Extralonger способен одновременно видеть динамику трендов и учитывать сетевые зависимости.
Именно здесь открывается возможность для интеграции ST-Expert. Его эксперты-графоны могут быть органично встроены в модуль, усилив его способность работать с изменяющимися структурами связей. Более того, сама идея смеси экспертов (Mixture of Experts) отлично ложится на модульную концепцию Extralonger, где уже предусмотрено разделение задач по маршрутам.
Начнем с временного маршрута. Архитектуру классического Transformer удобно сравнить с графовой моделью. Механизм Self-Attention фактически строит таблицу логитов, отражающую вероятностное влияние каждого элемента последовательности на остальные. Если рассматривать элементы последовательности как узлы, то таблица логитов превращается в граф, где ребра выражают силу взаимного воздействия. В этом смысле Transformer не просто анализирует последовательность, он превращает её в динамическую сеть взаимодействий, позволяя выявлять локальные и глобальные зависимости, которые иначе оставались бы скрытыми.
И здесь открывается интересная возможность. Мы можем отказаться от классического механизма генерации логитов в Self-Attention и вместо него использовать граф, построенный на принципах фреймворка ST-Expert. Такой подход позволяет задавать более осмысленные связи между элементами последовательности, опираясь на статистическую корреляцию, экспертные правила и структурированные зависимости. Мы ожидаем, что модель сможет понимать рынок глубже, фиксируя тонкие взаимосвязи, которые обычный Self-Attention может упустить.
Аналогичный подход мы можем использовать и в модуле анализа глобально-локальных зависимостей. Здесь модель строит своего рода каркас рынка, фиксируя широкие паттерны и тенденции. Глобальные связи помогают предсказывать общие движения рынка, позволяя модели видеть лес, а не только отдельные деревья.
Однако, для локальных зависимостей требуется иной подход. Необходимо построить разреженный граф, в котором учитываются лишь наиболее значимые и вероятные связи между узлами последовательности. Это похоже на работу опытного трейдера, который концентрируется на ключевых точках входа и выхода, игнорируя шумные, малозначимые сигналы. Здесь также можно применять методы фреймворка ST-Expert, но с особым вниманием к отбору важнейших взаимосвязей. Такой подход позволяет модели одновременно сохранять широкий обзор и внимательность к деталям, выявляя микровлияния, которые оказывают существенное воздействие на краткосрочные колебания.
Концепция ясна. Теперь пришло время перейти от теории к практике — к реализации этих идей средствами MQL5.
Модуль графового внимания
Для реализации предложенного выше варианта Transformer на основе графонов, мы создаем объект CNeuronGraphAttention, который наследует базовую функциональность от объекта полносвязного слоя CNeuronBaseOCL и становится центром нашей модели графового внимания. Этот объект не просто соединяет классическую обработку значений с графовыми структурами — он превращает последовательность данных в динамическую сеть взаимосвязей.
class CNeuronGraphAttention : public CNeuronBaseOCL { protected: CNeuronConvOCL cValue; CNeuronGraphons cGraphs; CNeuronSoftMaxOCL cScores; CNeuronBaseOCL cAttention; CNeuronBaseOCL cResidual; CNeuronConvOCL cFeedForward[2]; //--- virtual bool feedForward(CNeuronBaseOCL *NeuronOCL) override; virtual bool updateInputWeights(CNeuronBaseOCL *NeuronOCL) override; virtual bool calcInputGradients(CNeuronBaseOCL *NeuronOCL) override; public: CNeuronGraphAttention(void) {}; ~CNeuronGraphAttention(void) {}; virtual bool Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint units, uint window, uint emb_dimension, uint experts, float dropout, ENUM_OPTIMIZATION optimization_type, uint batch); //--- virtual int Type(void) override const { return defNeuronGraphAttention; } //--- methods for working with files virtual bool Save(int const file_handle) override; virtual bool Load(int const file_handle) override; //--- virtual bool WeightsUpdate(CNeuronBaseOCL *source, float tau) override; virtual void SetOpenCL(COpenCLMy *obj) override; virtual void SetActivationFunction(ENUM_ACTIVATION value) override { }; virtual void TrainMode(bool flag) override; };
Внутри модели графы, построенные по принципам ST-Expert, задают структуру отношений между элементами последовательности, позволяя выделять значимые связи и игнорировать шум. При этом функция SoftMax формирует вероятностное распределение внимания по графу, делая акцент на наиболее критичных взаимовлияниях, а остаточные связи обеспечивают сохранение и интеграцию как новых, так и уже накопленных зависимостей, создавая некую память модели.
Важно подчеркнуть, что внутри объекта мы отходим от привычной схемы Self-Attention и не формируем стандартные сущности Query и Key. В классическом Transformer они служат для построения матрицы логитов, отражающей взаимное влияние элементов последовательности. Но в нашем подходе эту функцию берет на себя граф, построенный по принципам ST-Expert. Благодаря этому, мы можем напрямую управлять связями между узлами и делать акцент на наиболее значимых взаимовлияниях, без лишней генерации промежуточных таблиц. При этом остаются только значения Value, которые используются для формирования окончательных результатов внимания. Они, как материал в руках искусного мастера, проходят через граф и Feed-Forward блоки, преобразуясь в информативные сигналы для прогнозирования рынка.
Такой подход позволяет модели работать более целенаправленно. Внимание концентрируется на ключевых взаимосвязях, которые действительно важны для финансового ряда, а не распыляется на все элементы одинаково. В результате мы получаем механизм, который сохраняет эффективность Transformer, но получает гораздо более осмысленную и структурированную интерпретацию взаимосвязей внутри последовательности.
За подготовку объекта CNeuronGraphAttention к работе отвечает метод Init. Именно здесь модель собирается воедино, словно тщательно отлаженный механизм, готовый к обработке финансовых данных.
bool CNeuronGraphAttention::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint units, uint window, uint emb_dimension, uint experts, float dropout, ENUM_OPTIMIZATION optimization_type, uint batch) { if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, units * window, optimization_type, batch)) return false; activation = None;
Сначала вызывается инициализация родительского класса, где задаются общие параметры нейрона. После этого поочередно настраиваются внутренние компоненты объекта.
Первым инициализируется блок cValue, который отвечает за первичное кодирование значений последовательности. Ему задается окно анализа и количество элементов анализируемой последовательности. Для него отключается функция активация, чтобы передавать данные максимально чистыми.
int index = 0; if(!cValue.Init(0, index, OpenCL, window, window, window, units, 1, optimization, iBatch)) return false; cValue.SetActivationFunction(None);
Далее создается блок графонов cGraphs, построенный по принципам ST-Expert, который определяет структуру взаимосвязей между элементами последовательности.
index++; if(!cGraphs.Init(0, index, OpenCL, units, window, emb_dimension, experts, dropout, optimization, iBatch)) return false;
На выходе модуля cGraphs мы получаем граф, который отражает вероятности наличия связей между узлами последовательности. Это уже ценная информация, но для модуля внимания нам необходимо превратить эти вероятности в полноценное распределение влияния каждого элемента последовательности на анализируемый объект. Здесь на сцену выходит блок cScores с функцией SoftMax. Она аккуратно нормализует вероятности, усиливая ключевые связи и ослабляя второстепенные, формируя таким образом точное и интерпретируемое распределение внимания. В результате, каждый элемент последовательности получает свою весовую значимость в анализе, а модель способна сосредоточиться на действительно значимых взаимовлияниях, игнорируя шум и несущественные колебания.
Такой подход позволяет плавно интегрировать графовые связи, созданные ST-Expert, с классическим механизмом внимания, превращая абстрактные вероятности в конкретные сигналы, готовые к дальнейшей обработке и использованию в прогнозировании финансовых рядов.
index++; if(!cScores.Init(0, index, OpenCL, units * units, optimization, iBatch)) return false; cScores.SetHeads(units);
Затем настраиваются блоки cAttention и cResidual, предназначенные для сохранения результатов механизма внимания и остаточных связей. Для них также отключается активация, чтобы не искажать сигналы.
index++; if(!cAttention.Init(0, index, OpenCL, Neurons(), optimization, iBatch)) return false; cAttention.SetActivationFunction(None); index++; if(!cResidual.Init(0, index, OpenCL, Neurons(), optimization, iBatch)) return false; cResidual.SetActivationFunction(None);
Наконец, настраиваются два последовательных слоя блока Feed-Forward. Первый использует активацию SoftPlus, усиливая нелинейность и способность модели выявлять сложные зависимости, а второй служит для сжатия и трансформации информации в итоговый сигнал внимания без дополнительной активации.
index++; if(!cFeedForward[0].Init(0, index, OpenCL, window, window, 2 * window, units, 1, optimization, iBatch)) return false; cFeedForward[0].SetActivationFunction(SoftPlus); index++; if(!cFeedForward[1].Init(0, index, OpenCL, cFeedForward[0].GetFilters(), cFeedForward[0].GetFilters(), window, units, 1, optimization, iBatch)) return false; cFeedForward[1].SetActivationFunction(None); //--- return true; }
В результате метод Init аккуратно соединяет все компоненты в единую систему. Блоки обработки значений, графы, распределение внимания и Feed-Forward слои становятся готовыми к работе, создавая полноценно функционирующий механизм графового внимания, способный эффективно анализировать динамику финансовых рядов.
После инициализации объекта, следующим этапом работы модели является прямой проход, реализованный в методе feedForward.
bool CNeuronGraphAttention::feedForward(CNeuronBaseOCL *NeuronOCL) { if(!cValue.FeedForward(NeuronOCL)) return false;
Это экспедиция по сложному и динамичному финансовому ландшафту. Всё начинается с объекта cValue, который действует как разведчик. Он внимательно изучает каждый сигнал рынка, преобразует сырые данные в удобное для анализа представление, словно собирает карту местности перед походом.
Затем на сцену выходит модуль графов cGraphs, который строит маршруты взаимодействий между узлами последовательности.
if(!cGraphs.FeedForward(NeuronOCL)) return false;
Можно представить его как систему дорог и тропинок на этой карте, где каждая связь отражает силу влияния одного элемента на другой. Блок cScores с SoftMax работает как командир экспедиции, определяя, какие пути наиболее важны и по каким стоит пройти в первую очередь. Он превращает сырые вероятности связей в четкую стратегию движения, выделяя ключевые взаимовлияния и игнорируя малозначимые шумные сигналы.
if(!cScores.FeedForward(cGraphs.AsObject())) return false;
Когда разведка завершена, информация из cValue и распределения внимания объединяется через матричное умножение, формируя результаты модуля внимания в cAttention. Это как собрать все разведданные на одной карте, чтобы видеть, какие силы и влияния действуют на анализируемый объект.
if(!MatMul(cScores.getOutput(), cValue.getOutput(), cAttention.getOutput(), cValue.GetUnits(), cValue.GetUnits(), cValue.GetFilters(), 1, false)) return false;
Следующий шаг — объединение с остаточным сигналом и нормализация в cResidual, которые стабилизируют данные, не позволяя случайным колебаниям сбить модель с курса.
if(!SumAndNormilize(NeuronOCL.getOutput(), cAttention.getOutput(), cResidual.getOutput(), cValue.GetFilters(), true, 0, 0, 0, 1)) return false;
Затем начинается этап блока Feed-Forward, действующий как искусные аналитики экспедиции. Первый блок с активацией SoftPlus выявляет скрытые взаимозависимости и усиливает важные сигналы, словно исследователи определяют, где на карте скрыты ключевые ресурсы. Второй блок преобразует информацию в компактное и понятное представление, готовое к использованию в прогнозе.
if(!cFeedForward[0].FeedForward(cResidual.AsObject())) return false; if(!cFeedForward[1].FeedForward(cFeedForward[0].AsObject())) return false; if(!SumAndNormilize(cFeedForward[1].getOutput(), cResidual.getOutput(), Output, cFeedForward[1].GetFilters(), true, 0, 0, 0, 1)) return false; //--- return true; }
Финальная нормализация объединяет остаточные данные с результатами Feed-Forward, формируя окончательный сигнал, который показывает модельный взгляд на рынок — одновременно широкий и детализированный.
В итоге метод feedForward превращает сложную последовательность финансовых данных в структурированное, осмысленное и готовое к прогнозированию знание.
Прямой проход — это только первая часть работы модели. Чтобы получать действительно информативные прогнозы, необходимо организовать процесс анализа ошибок, который реализован в методе calcInputGradients.
bool CNeuronGraphAttention::calcInputGradients(CNeuronBaseOCL *NeuronOCL) { if(!NeuronOCL) return false;
Этот этап можно представить как обратную экспедицию по уже пройденному маршруту, где модель оценивает, какие решения были точными, а где произошли отклонения.
Процесс начинается с последнего блока прямого прохода Feed-Forward, где сигнал проходит через обратную активацию, чтобы корректно вычислить градиенты ошибки.
if(!DeActivation(cFeedForward[1].getOutput(), cFeedForward[1].getGradient(), Gradient, cFeedForward[1].Activation())) return false; if(!cFeedForward[0].CalcHiddenGradients(cFeedForward[1].AsObject())) return false; if(!cResidual.CalcHiddenGradients(cFeedForward[0].AsObject())) return false;
Далее градиенты передаются на первый слой блока и на остаточный блок cResidual, обеспечивая корректировку информации, которая была обработана на предыдущих этапах. На этом этапе выполняется суммирование градиентов, что помогает сохранять устойчивость модели и предотвращает расходимость сигналов при обратном распространении.
if(!SumAndNormilize(Gradient, cResidual.getGradient(), cAttention.getOutput(), cValue.GetFilters(), false, 0, 0, 0, 1)) return false;
Далее матричное умножение распространяет градиенты через блок распределения внимания cScores и блок cValue, позволяя корректировать силу влияния каждого элемента на другие. Графовый блок cGraphs получает свои градиенты, уточняя вероятности связей между узлами на основе накопленных ошибок.
if(!MatMulGrad(cScores.getOutput(), cScores.getGradient(), cValue.getOutput(), cValue.getGradient(), cAttention.getGradient(), cValue.GetUnits(), cValue.GetUnits(), cValue.GetFilters(), 1, false)) return false; if(!cGraphs.CalcHiddenGradients(cScores.AsObject())) return false;
Затем градиенты проходят до уровня исходных данных в объект NeuronOCL из блока cValue, где окончательно уточняется, как каждый элемент последовательности повлиял на ошибку модели.
if(!NeuronOCL.CalcHiddenGradients(cGraphs.AsObject())) return false;
Важно подчеркнуть, что в процессе прямого прохода исходные данные проходят одновременно по трём маршрутам: через блок значений cValue, через графовые связи cGraphs и через механизм остаточных связей. Каждый из этих маршрутов формирует собственный поток информации, отражающий разные аспекты взаимосвязей внутри последовательности.
Поэтому в обратном проходе необходимо аккуратно собрать градиенты со всех трёх маршрутов. Это похоже на экспедицию, где после возвращения с нескольких параллельных троп, исследователи объединяют свои наблюдения, чтобы получить полную картину ландшафта. Сначала градиенты формируются на выходе каждого маршрута. Затем они суммируются с учётом остаточных связей, чтобы информация оставалась целостной и сбалансированной. Только после такой интеграции модель получает точное представление о том, где были ошибки, какие влияния оказались недооценены, а какие переоценены.
if(!DeActivation(NeuronOCL.getOutput(), cResidual.getGradient(), cAttention.getGradient(), NeuronOCL.Activation())) return false; if(!SumAndNormilize(NeuronOCL.getGradient(), cResidual.getGradient(), cResidual.getGradient(), cValue.GetWindow(), false, 0, 0, 0, 1)) return false; if(!NeuronOCL.CalcHiddenGradients(cValue.AsObject())) return false; if(!SumAndNormilize(NeuronOCL.getGradient(), cResidual.getGradient(), NeuronOCL.getGradient(), cValue.GetWindow(), false, 0, 0, 0, 1)) return false; //--- return true; }
Благодаря такому подходу графовый Transformer может эффективно обучаться. Он учитывает как глобальные, так и локальные взаимосвязи, корректируя их по всему маршруту, а не только на отдельных сегментах. В результате каждый узел последовательности получает справедливую оценку своей роли в ошибке модели, а обучение становится последовательным, стабильным и максимально информативным.
Обучение параметров модели реализовано в методе updateInputWeights, который можно рассматривать как координацию работы всей системы. На этом этапе объект не выполняет прямую корректировку весов самостоятельно, а делегирует эту задачу своим внутренним компонентам.
bool CNeuronGraphAttention::updateInputWeights(CNeuronBaseOCL *NeuronOCL) { if(!cValue.UpdateInputWeights(NeuronOCL)) return false; if(!cGraphs.UpdateInputWeights(NeuronOCL)) return false; if(!cFeedForward[0].UpdateInputWeights(cResidual.AsObject())) return false; if(!cFeedForward[1].UpdateInputWeights(cFeedForward[0].AsObject())) return false; //--- return true; }
Иными словами, метод updateInputWeights действует как дирижёр, который координирует работу всех музыкантов внутри объекта. Каждый блок вносит свой вклад, но общая гармония достигается только благодаря их совместной работе. В результате, модель постепенно улучшает прогнозы, обучаясь на ошибках, выявленных в процессе обратного прохода, и усиливая значимые взаимосвязи между элементами последовательности.
Полный код класса CNeuronGraphAttention с реализацией всех методов представлен во вложении, демонстрируя, как концептуальные идеи графового внимания превращаются в работоспособный механизм, готовый к анализу динамичных финансовых рядов.
Разреженный SoftMax
Следующим этапом перед нами становится задача создания разреженного графа для модуля локального внимания. При этом нет необходимости полностью перестраивать объект генерации графов — можно обойтись минимальными изменениями, сохранив базовую архитектуру и логику работы. В представленном выше модуле графового внимания генерируемый граф зависимостей на выходе преобразуется в вероятностное распределение с помощью функции SoftMax, которая расставляет акценты на более значимых взаимовлияниях.
На этапе локального внимания можно пойти дальше. Давайте обнулим менее вероятностные связи, сохранив только ключевые взаимозависимости. Такой прием позволяет сконцентрировать вычислительные ресурсы на самых значимых взаимовлияниях между узлами, что особенно важно при анализе локальных колебаний финансовых рядов. На практике это похоже на работу опытного аналитика: вместо того чтобы изучать каждый малозначимый сигнал, он обращает внимание только на критически важные факторы, которые реально влияют на текущий тренд.
Чтобы дополнительно оптимизировать модель, на выходе создается разреженная матрица, которая хранит только активные, значимые связи. Это значительно снижает потребление памяти и ускоряет вычисления, не теряя при этом информативности. Разреженная структура позволяет модели обрабатывать большие временные окна и сложные последовательности, не перегружая вычислительный блок, а локальное внимание становится быстрым и точным инструментом для выявления микро-влияний.
В итоге такой подход объединяет два стратегических преимущества. С одной стороны, сохраняются глобальные связи, заданные графами ST-Expert, которые обеспечивают понимание общей картины рынка, а с другой — локальные связи, выделенные через разреженную матрицу, позволяют фокусироваться на критически важных мелких взаимодействиях.
Практическую реализацию разреженного локального внимания мы начинаем с создания алгоритма Sparse SoftMax на стороне OpenCL-программы. Его задача — преобразовать вероятности связей, полученные из графового блока, в распределение внимания, одновременно обнуляя менее значимые элементы и создавая разреженную матрицу.
__kernel void SparseSoftMax(__global const float *data, __global float *outputs, __global float *indexes, const int out_dimension ) { const size_t row = get_global_id(0); const size_t col_in = get_local_id(1); const int total_rows = (int)get_global_size(0); const int total_cols_in = (int)get_local_size(1); //--- __local float Temp[LOCAL_ARRAY_SIZE]; const int ls = min(total_cols_in, (int)LOCAL_ARRAY_SIZE);
Каждый рабочий поток обрабатывает отдельную строку исходного массива данных, представляющую вероятности взаимосвязей узлов. Сначала значения проверяются на корректность — исключаются NaN или бесконечности, заменяясь минимальным значением. Затем каждый поток вычисляет позицию своего значения относительно других элементов строки. Если значение оказывается среди наименее значимых, оно будет обнулено.
const int shift_in = RCtoFlat(row, col_in, total_rows, total_cols_in, 0); //--- calc position float value = IsNaNOrInf(data[shift_in], MIN_VALUE); int position = 0; for(int l = 0; l < total_cols_in; l += ls) { if(col_in >= l && col_in < (l + ls)) Temp[col_in - l] = value; BarrierLoc; for(int i = 0; i < ls; i++) { if(i == (col_in - l)) continue; if(Temp[i] > value) position++; else if(Temp[i] == value && i < (col_in - l)) position++; } BarrierLoc; }
После этого, к оставшимся элементам применяется классическая функция SoftMax, но с локальной нормализацией по подгруппам элементов.
//--- SoftMax if(position >= out_dimension) value = MIN_VALUE; value = LocalSoftMax(value, 1, Temp); //--- result const int shift_out = RCtoFlat(row, position, total_rows, out_dimension, 0); if(position < out_dimension) { outputs[shift_out] = value; indexes[shift_out] = (float)col_in; } }
Такой подход позволяет одновременно учитывать только наиболее значимые связи и поддерживать стабильность численных вычислений. На выходе формируется разреженная матрица: значения, превышающие заданный порог, сохраняются, а все остальные игнорируются, что резко сокращает объем данных и повышает эффективность работы модели.
Итоговый результат — два массива:
- outputs, содержащий нормализованные значения только для значимых связей,
- indexes, фиксирующий позиции этих активных элементов.
Эта информация готова к использованию в модуле локального внимания, позволяя модели концентрироваться на критических локальных взаимосвязях узлов последовательности, не перегружая память и вычислительные ресурсы.
На стороне основной программы создаем новый объект CNeuronSparseSoftMax, который наследует базовую функциональность от слоя CNeuronSoftMaxOCL и специализируется на работе с разреженными распределениями. В отличие от стандартного SoftMax, этот объект позволяет акцентировать внимание только на наиболее значимых связях между элементами последовательности, одновременно сокращая потребление памяти и повышая эффективность вычислений.
class CNeuronSparseSoftMax : public CNeuronSoftMaxOCL { protected: uint iDimensionIn; CBufferFloat cIndexes; //--- virtual bool feedForward(CNeuronBaseOCL *NeuronOCL) override; virtual bool calcInputGradients(CNeuronBaseOCL *NeuronOCL) override; public: CNeuronSparseSoftMax(void) {}; ~CNeuronSparseSoftMax(void) {}; //--- virtual bool Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint units, uint dimension_in, uint dimension_out, ENUM_OPTIMIZATION optimization_type, uint batch); //--- virtual bool Save(int const file_handle) override; virtual bool Load(int const file_handle) override; virtual int Type(void) override const { return defNeuronSparseSoftMax; } virtual void SetOpenCL(COpenCLMy *obj) override; virtual CBufferFloat* GetIndexes(void) { return GetPointer(cIndexes); } virtual uint DimensionOut(void) const { return uint(Neurons() / iHeads); } };
Внутри класса хранится информация о размерности исходных данных iDimensionIn и буфер индексов активных связей cIndexes, значения которого указывают на элементы, учтенные при формировании распределения внимания. Методы feedForward и calcInputGradients являются обертками кернелов прямого и обратного проходов на стороне OpenCL-программы, позволяя модели корректно обрабатывать данные и обучаться на ошибках с учетом только активных, значимых связей.
Инициализация объекта осуществляется в методе Init, который готовит модель к работе с разреженными распределениями внимания.
bool CNeuronSparseSoftMax::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint units, uint dimension_in, uint dimension_out, ENUM_OPTIMIZATION optimization_type, uint batch) { if(dimension_in < dimension_out) return false; if(!CNeuronSoftMaxOCL::Init(numOutputs, myIndex, open_cl, units * dimension_out, optimization_type, batch)) return false;
Сначала проверяется корректность входной размерности: dimension_in должна быть не меньше dimension_out, чтобы модель могла корректно формировать распределение. Далее вызывается инициализация базового класса.
После этого устанавливается число голов внимания, сохраняется размерность исходных данных, а также создается буфер индексов cIndexes, который будет хранить только активные, значимые связи.
SetHeads(units); iDimensionIn = dimension_in; if(!cIndexes.BufferInit(Neurons(), -1) || !cIndexes.BufferCreate(OpenCL)) return false; //--- return true; }
Буфер инициализируется и резервируется в памяти, включая возможность работы с OpenCL для ускоренных вычислений. В результате объект полностью готов к работе: он способен принимать исходные данные, выделять ключевые связи и формировать разреженное распределение внимания, сохраняя при этом эффективность использования ресурсов.
Таким образом, CNeuronSparseSoftMax превращает разреженный граф локального внимания в удобное и эффективное распределение, где внимание сосредоточено на наиболее значимых взаимосвязях. Это позволяет модели одновременно сохранять глобальную картину, заданную основным графом ST-Expert, и точно учитывать локальные влияния, формируя детализированные и информативные прогнозы финансовых рядов.
А для организации локального внимания в нашей модели достаточно минимального, но эффективного изменения. Мы можем взять уже реализованный объект графового Transformer и заменить в нём модуль вероятностной проекции SoftMax на разреженную версию CNeuronSparseSoftMax. Такой подход позволяет не перестраивать всю архитектуру, сохранив глобальные графовые связи и логику блоков, одновременно обеспечивая точное выделение локальных взаимовлияний.
Разреженный SoftMax будет акцентировать внимание на наиболее значимых соединениях между узлами, игнорируя малозначимые связи и тем самым оптимизируя использование памяти и вычислительных ресурсов.
class CNeuronSparseGraphAttention : public CNeuronBaseOCL { protected: CNeuronConvOCL cValue; CNeuronGraphons cGraphs; CNeuronSparseSoftMax cScores; CNeuronBaseOCL cAttention; CNeuronBaseOCL cResidual; CNeuronConvOCL cFeedForward[2]; //--- virtual bool feedForward(CNeuronBaseOCL *NeuronOCL) override; virtual bool updateInputWeights(CNeuronBaseOCL *NeuronOCL) override; virtual bool calcInputGradients(CNeuronBaseOCL *NeuronOCL) override; public: CNeuronSparseGraphAttention(void) {}; ~CNeuronSparseGraphAttention(void) {}; virtual bool Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint units, uint window, uint experts, float dropout, uint emb_dimension, uint sparse_dimension, ENUM_OPTIMIZATION optimization_type, uint batch); //--- virtual int Type(void) override const { return defNeuronSparseGraphAttention; } //--- methods for working with files virtual bool Save(int const file_handle) override; virtual bool Load(int const file_handle) override; //--- virtual bool WeightsUpdate(CNeuronBaseOCL *source, float tau) override; virtual void SetOpenCL(COpenCLMy *obj) override; virtual void SetActivationFunction(ENUM_ACTIVATION value) override { }; virtual void TrainMode(bool flag) override; };
Полный код всех представленных классов и реализация каждого из их методов доступны во вложении к статье.
Мы проделали значительную работу и достигли важных результатов. Сейчас самое время сделать небольшую паузу, чтобы информация усвоилась и отложилась в памяти. В следующей статье мы продолжим начатое, перейдем к оценке эффективности реализованных решений и посмотрим, насколько успешно модель справляется с анализом финансовых рядов.
Заключение
В ходе нашей работы мы прошли сложный путь, исследуя архитектуру графового Transformer и осваивая её ключевые компоненты: блоки значений, графы зависимостей, распределение внимания. Каждый из этих элементов сыграл свою роль, словно участники экспедиции, которые вместе прокладывают маршрут по сложному и изменчивому ландшафту финансового рынка.
Особое внимание мы уделили внедрению разреженного SoftMax для локального внимания. Этот инструмент позволил модели выделять самые значимые взаимосвязи, игнорируя малозначимые сигналы, экономя ресурсы и повышая точность ориентации в рыночной динамике. Прямой и обратный проходы, а также обновление весов обеспечили непрерывную корректировку курса, позволяя модели учиться на ошибках и постепенно улучшать свои прогнозы.
Полученные результаты стали прочной базой для дальнейших исследований. В следующей статье мы продолжим путешествие, оценивая эффективность реализованных решений, анализируя точность прогнозов и выявляя, насколько успешно модель справляется с динамикой финансовых рядов.
Ссылки
Программы, используемые в статье
# | Имя | Тип | Описание |
---|---|---|---|
1 | Study.mq5 | Советник | Советник офлайн обучения моделей |
2 | StudyOnline.mq5 | Советник | Советник онлайн обучения моделей |
3 | Test.mq5 | Советник | Советник для тестирования модели |
4 | Trajectory.mqh | Библиотека класса | Структура описания состояния системы и архитектуры моделей |
5 | NeuroNet.mqh | Библиотека класса | Библиотека классов для создания нейронной сети |
6 | NeuroNet.cl | Библиотека | Библиотека кода OpenCL-программы |
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.




- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Опубликована статья Нейросети в трейдинге: Устойчивые торговые сигналы в любых режимах рынка (Модули внимания):
Автор: Dmitriy Gizlyk