Русский
preview
Redes neurais em trading: Dos transformers aos neurônios spiking (Componentes principais)

Redes neurais em trading: Dos transformers aos neurônios spiking (Componentes principais)

MetaTrader 5Sistemas de negociação |
17 1
Dmitriy Gizlyk
Dmitriy Gizlyk

Introdução

Os mercados financeiros lembram um mar revolto, no qual o capitão de uma embarcação precisa tomar decisões em questão de instantes, apoiando-se não apenas em mapas e bússolas, mas também na intuição desenvolvida ao longo de anos de prática. No mundo atual, os modelos computacionais assumem cada vez mais esse papel, enquanto o desafio do pesquisador passa a ser criar um algoritmo capaz de competir com a flexibilidade e a capacidade de antecipação humanas. Em meio aos métodos tradicionais de machine learning e às arquiteturas de redes neurais já amplamente consolidadas, o framework SpikingBrain surgiu como um sopro de renovação, capaz de impulsionar o pesquisador rumo a novas possibilidades de análise de dados.

A principal vantagem do SpikingBrain está em sua abordagem orientada a eventos para o processamento das informações. Ao contrário dos modelos clássicos, que tratam os dados como um fluxo contínuo, as redes spiking reagem a eventos discretos, aproximando-se mais da natureza dos sinais financeiros reais. O mercado bursátil nunca evolui de forma uniforme: há períodos de calmaria, em que as oscilações mal são perceptíveis, e outros que se assemelham a uma rajada repentina, trazendo movimentos bruscos nas cotações. Uma rede neural tradicional precisa processar ambos os regimes com a mesma intensidade, aumentando a carga computacional e perdendo precisão justamente nos momentos de maior turbulência. O SpikingBrain, por outro lado, permite concentrar a atenção nos eventos relevantes, conferindo ao sistema maior eficiência e capacidade de adaptação.

Outro aspecto igualmente importante é a eficiência energética dos modelos spiking. Nos mercados financeiros, em que os algoritmos muitas vezes precisam operar continuamente, a possibilidade de reduzir a carga sobre o hardware sem comprometer a qualidade da análise torna-se uma vantagem significativa. Em uma época na qual os recursos computacionais são compartilhados entre milhares de estratégias e dezenas de servidores, extrair o máximo benefício com o mínimo de energia passa a ter importância não apenas técnica, mas também estratégica. Pode-se dizer que o SpikingBrain abre caminho para uma computação mais inteligente. A ênfase deixa de estar na força bruta do hardware e passa para uma arquitetura mais elegante e para a otimização de processos.

Uma das maiores vantagens do framework está em sua capacidade de transferir conhecimento e se adaptar com eficiência. Ao contrário de modelos de grande porte, que exigem conjuntos de dados enormes para funcionar de maneira estável, a arquitetura spiking consegue aproveitar com mais facilidade o conhecimento acumulado previamente e se adaptar mais rapidamente a novos dados. Isso permite aplicar estruturas já treinadas a tarefas de análise financeira, aplicando uma atualização incremental às estruturas já treinadas mesmo com conjuntos de dados relativamente pequenos. Essa abordagem acelera significativamente a aplicação prática do modelo e o transforma em uma ferramenta flexível, capaz de levar em conta as particularidades do mercado em questão. Dessa forma, o SpikingBrain torna-se um elo entre a universalidade das soluções pré-treinadas e as características únicas dos dados locais.

Vale destacar que o SpikingBrain não se limita a uma declaração de princípios, mas constitui um sistema coerente, sustentado por uma engenharia bem estruturada. Em sua base está a ideia de converter os sinais originais em uma sequência de spikes, que servem de fundamento para todo o processamento subsequente. Isso permite criar uma representação rica das séries temporais, na qual cada spike carrega informações relevantes sobre o estado do sistema.

Em seguida, a transmissão e a integração desses sinais ficam a cargo de blocos especializados que reproduzem mecanismos biológicos. Eles oferecem flexibilidade para lidar com diferentes tipos de dados e formam a base para a construção de uma estrutura de análise em múltiplos níveis.

Aqui, vale analisar com mais detalhes um elemento importante da arquitetura: o mecanismo de codificação dos sinais em spikes. Para séries temporais financeiras, essa tarefa é particularmente complexa. Não basta simplesmente registrar valores de preços ou volumes; é necessário convertê-los para uma representação na qual cada pulso reflita uma alteração significativa nos dados. Os autores propõem várias estratégias de codificação, desde uma abordagem simples baseada em limiares, na qual um spike é gerado quando determinado valor crítico é ultrapassado, até esquemas mais complexos que consideram a velocidade de variação e o contexto dos eventos anteriores. Essa variedade de métodos torna o modelo versátil. Ele pode funcionar tanto com dados de alta frequência, nos quais cada milissegundo importa, quanto com gráficos diários mais tranquilos, em que a tendência global assume papel decisivo.

Outro bloco fundamental é formado pelos integradores. Eles acumulam e somam os impulsos recebidos, determinando o momento em que o neurônio entra em estado ativo. No contexto dos mercados financeiros, esse mecanismo se assemelha a uma filtragem de ruído: várias pequenas oscilações podem ser ignoradas, mas seu efeito acumulado, ao atingir determinado limiar, provoca uma resposta do modelo. Esse princípio permite manter um equilíbrio entre sensibilidade e estabilidade, algo especialmente valioso em ambientes sujeitos ao ruído de mercado.

Os autores do framework propuseram um modelo cuja arquitetura se baseia em uma estrutura híbrida de camadas. Algumas delas são especializadas na identificação de padrões de curto prazo, captando oscilações rápidas do mercado. Outras integram os sinais ao longo de horizontes mais extensos, permitindo gerar previsões mais estáveis. Essa combinação proporciona ao sistema uma espécie de visão dupla. Ele consegue, ao mesmo tempo, captar os menores detalhes do momento atual e manter uma perspectiva estratégica. Nos mercados financeiros, isso equivale à capacidade de observar tanto o movimento do ponteiro de um cronômetro quanto o ritmo geral do pêndulo que marca a passagem do tempo.

Um elemento importante da arquitetura são os módulos responsáveis pela normalização e pela estabilidade do funcionamento da rede. Ao contrário das redes neurais tradicionais, nas quais o equilíbrio é obtido por meio de extensos procedimentos de regularização, o SpikingBrain utiliza mecanismos mais refinados, incorporados à própria natureza dos sinais spiking. Graças a isso, a rede mantém a estabilidade mesmo diante de dados caóticos, preservando sua capacidade de gerar previsões precisas. Nos mercados financeiros, é difícil superestimar a importância dessa característica: afinal, o caos é uma presença constante, e a capacidade de identificar padrões ocultos nesse ambiente determina o sucesso de qualquer modelo.

Também vale destacar a modularidade da arquitetura proposta. Os autores estabeleceram princípios que permitem expandir o modelo tanto em largura quanto em profundidade, combinar elementos spiking com blocos tradicionais de deep learning e criar soluções híbridas. Essa integração amplia ainda mais as possibilidades de aplicação em finanças. É possível, por exemplo, construir modelos nos quais os mecanismos spiking sejam responsáveis pelo processamento de sinais orientados a eventos, enquanto os blocos clássicos cuidam de previsões de longo prazo ou da análise de informações textuais. O resultado é uma ferramenta multifacetada, em que cada componente desempenha sua própria função e, em conjunto, todos formam um mecanismo integrado para analisar a complexa dinâmica do mercado.

A visualização do framework SpikingBrain proposta pelos autores é apresentada a seguir.

Na parte prática do artigo anterior, concentramos nossa atenção na preparação da base necessária para transferir as ideias do SpikingBrain para o ambiente MQL5. Era importante demonstrar como os princípios teóricos poderiam assumir a forma de algoritmos concretos, prontos para processar dados reais de trading. Começamos implementando um componente básico capaz de converter um sinal contínuo em um impulso discreto de spike. Com isso, estabelecemos uma ponte entre a ideia abstrata e uma ferramenta capaz de operar em uma plataforma de trading amplamente utilizada. Essa etapa foi o primeiro passo na construção de um sistema completo, no qual as inovações propostas pelos autores do framework são colocadas em prática em condições reais de mercado.

Definimos a direção e preparamos a base. Agora passamos a uma análise mais aprofundada da arquitetura e dos princípios de funcionamento do modelo.



Discussão da implementação

Antes de avançarmos para a implementação propriamente dita das abordagens propostas, vale delinear os princípios fundamentais que orientarão nossa abordagem. Os autores do framework utilizam modelos pré-treinados baseados em Transformers clássicos, e esse é um aspecto importante. Essa abordagem permite evitar o gasto de recursos com o treinamento de modelos do zero e permite aproveitar diretamente o conhecimento acumulado em arquiteturas cuja eficácia já foi comprovada. Portanto, em nossa implementação também podemos utilizar algoritmos e módulos já existentes, o que torna o processo mais confiável e flexível.

A ideia dos módulos híbridos de atenção é particularmente interessante. Os autores propõem combinar diferentes soluções arquiteturais dentro de um único sistema, por exemplo, utilizando em paralelo atenção linear, atenção por janelas ou atenção completa. Em certa medida, essa abordagem lembra os princípios implementados no framework Extralonger, em que a atenção global-local serviu de base para uma análise mais profunda das relações temporais e espaciais.

Ainda assim, as aplicações financeiras têm suas particularidades, o que exige cautela na escolha da arquitetura. Assim, a atenção por janelas, embora tenha apresentado bons resultados em diversas tarefas, nem sempre é a solução ideal para analisar séries temporais de mercado. Por um lado, as cotações mais recentes realmente refletem a tendência que está se formando; por outro, é justamente nessa região que se concentra o maior nível de ruído. Além disso, os sinais mais relevantes para a dinâmica de longo prazo nem sempre estão nos elementos mais recentes da série temporal. Muitas vezes, eles estão distribuídos ao longo de intervalos mais amplos e se manifestam por meio de dependências esparsas, e simplesmente ampliar a janela de análise nem sempre resolve o problema. Quanto maior a janela, maior a parcela dos recursos computacionais destinada não à identificação de padrões úteis, mas ao processamento de ruído. Como resultado, a carga sobre o modelo aumenta sem que a qualidade da previsão necessariamente melhore.

Por isso, no desenvolvimento de modelos para os mercados financeiros, é preferível utilizar mecanismos de atenção mais adaptativos. Sua função é filtrar informações irrelevantes e identificar sinais raros, mas realmente valiosos. Muitas vezes, são justamente esses sinais que determinam mudanças nas tendências de mercado e permitem ao modelo gerar previsões com relevância prática.

Nesse contexto, a ideia de atenção esparsa baseada em graphons parece particularmente interessante. Essa abordagem permite não apenas ampliar mecanicamente a janela de análise, mas estruturar as relações entre os elementos da série temporal, identificando as principais dependências de maneira mais eficiente. Em essência, os graphons definem uma estrutura que direciona a atenção apenas para as relações em que realmente existem correlações e sinais relevantes. Isso reduz a carga sobre o modelo e permite concentrar os recursos nas regiões dos dados que de fato importam.

Além disso, o uso de graphons se integra naturalmente à proposta dos autores do framework de incorporar algoritmos do tipo Mixture of Experts (MoE). A estrutura esparsa da atenção e a arquitetura modular do MoE reforçam uma à outra. Os graphons definem as rotas de interação, enquanto o MoE permite ativar seletivamente os blocos de especialistas mais adequados. Como resultado, obtém-se um sistema com potencial para lidar melhor com séries financeiras ruidosas e se adaptar de forma flexível às condições atuais do mercado.

É justamente a ideia de combinar mecanismos de processamento de sinais biologicamente plausíveis com a flexibilidade arquitetural das redes neurais modernas que está na base do SpikingBrain. Os autores do framework propõem uma solução original que vai além das arquiteturas recorrentes tradicionais ou baseadas em Transformer. O ponto de partida é um modelo de neurônio spiking, no qual a informação é transmitida por impulsos discretos e o treinamento está associado à dinâmica das interações ao longo do tempo. Essa abordagem se aproxima mais dos princípios de funcionamento do cérebro humano e abre caminho para um processamento de dados mais refinado e eficiente em termos de recursos.

Além disso, chama atenção a interpretação do módulo de atenção linear proposta pelos autores. Em um de nossos trabalhos anteriores, já implementamos um objeto de atenção linear e conhecemos bem suas possibilidades. No entanto, os autores do framework propuseram uma variante mais flexível. A atenção linear é implementada como um bloco recorrente com gates, o que permite ampliar significativamente o alcance da atenção sem aumentar de forma substancial o custo computacional.

Nessa modificação, o estado da atenção é calculado de forma recorrente. A cada etapa, o bloco acumula informações do estado anterior, ponderando-as por meio dos gates, e acrescenta novas interações entre chaves e valores.

onde St é a matriz do estado acumulado, gt é o vetor de gates, kt e vt são as chaves e os valores da etapa atual, e qt é a consulta. Essa estrutura permite preservar informações sobre dependências de longo alcance e, ao mesmo tempo, controlar sua relevância por meio dos gates.

Do ponto de vista da análise financeira, essa solução é especialmente valiosa. Ela permite considerar eventos distribuídos ao longo do tempo sem sobrecarregar o modelo com o processamento de sinais irrelevantes e ruído. Em conjunto com a atenção esparsa baseada em graphons e a arquitetura MoE, a atenção linear recorrente se torna uma ferramenta poderosa para gerar previsões adaptativas, capazes de identificar as principais tendências e reagir rapidamente às mudanças nas condições de mercado.

Os autores do framework SpikingBrain propõem duas abordagens principais para o uso da atenção híbrida: sequencial e paralela. Na variante sequencial, utiliza-se uma pilha de camadas de atenção com diferentes arquiteturas, entre as quais são inseridos blocos do tipo MoE que desempenham o papel de módulos FeedForward. Essa abordagem permite que o modelo processe sucessivamente sinais de diferentes naturezas, preservando a flexibilidade estrutural e a possibilidade de ativar especialistas de forma seletiva.

A mistura paralela, por sua vez, pressupõe o uso simultâneo de diferentes blocos de atenção dentro de uma mesma camada. O resultado combinado é então encaminhado para processamento por um único bloco MoE, integrando informações obtidas sob diferentes perspectivas. Além disso, mesmo no esquema paralelo, é possível combinar diferentes módulos de atenção entre as camadas do modelo. Com isso, o modelo alcança máxima flexibilidade, permitindo adaptar a estrutura de atenção às características específicas dos dados.

Do ponto de vista dos mercados financeiros, essa abordagem parece especialmente promissora. Diferentes fontes de informação, horizontes temporais e tipos de sinais podem ser processados simultaneamente, enquanto o sistema permanece gerenciável e eficiente em termos de recursos. Em conjunto com a atenção esparsa baseada em graphons e os blocos recorrentes de atenção linear, os modos híbridos permitem construir modelos complexos capazes de captar tanto oscilações locais ruidosas quanto tendências globais do mercado.


Módulo de atenção linear com gates

Hoje iniciaremos a parte prática com a implementação de um bloco recorrente de atenção linear com gates, que será implementado como um novo objeto CNeuronGateLineAttention. Esse objeto é criado como um módulo especializado dentro da arquitetura SpikingBrain e herda a funcionalidade básica de CNeuronSpikeConv. Essa herança facilita sua integração à infraestrutura existente de objetos neurais spiking, permite aproveitar as vantagens da dinâmica de impulsos e do acúmulo recorrente de informações, além de utilizar computação paralela via OpenCL para acelerar o processamento dos dados.

A solução permite combinar o melhor dos dois mundos. Dentro do objeto, são utilizados os valores contínuos convencionais da atenção linear multi-head, o que permite identificar com precisão os principais sinais e as dependências de longo alcance. Ao mesmo tempo, os algoritmos da classe pai reduzem a dimensionalidade da atenção multi-head à dimensionalidade original dos dados e convertem os valores resultantes em impulsos spiking discretos. Dessa forma, o modelo preserva a precisão da análise contínua e, simultaneamente, passa a operar dentro de uma dinâmica de impulsos orientada a eventos, o que é especialmente importante no processamento de séries temporais financeiras ruidosas e esparsas.

A estrutura do novo objeto é apresentada a seguir.

class CNeuronGateLineAttention  :  public CNeuronSpikeConv
  {
protected:
   uint              iDimensionK;
   uint              iVariables;
   uint              iHeads;
   //---
   CNeuronConvOCL    cQKV;
   CNeuronBaseOCL    cQ;
   CNeuronBaseOCL    cK;
   CNeuronBaseOCL    cV;
   CNeuronBaseOCL    cKV;
   CNeuronConvOCL    cGate;
   CNeuronBaseOCL    cState;
   CNeuronBaseOCL    cMHAttention;
   //---
   virtual bool      feedForward(CNeuronBaseOCL *NeuronOCL) override;
   virtual bool      updateInputWeights(CNeuronBaseOCL *NeuronOCL) override;
   virtual bool      calcInputGradients(CNeuronBaseOCL *NeuronOCL) override;

public:
                     CNeuronGateLineAttention(void) {};
                    ~CNeuronGateLineAttention(void) {};
   //---
   virtual bool      Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                          uint variables, uint dimension, uint dimension_k, uint heads,
                          ENUM_OPTIMIZATION optimization_type, uint batch);
   //---
   virtual int       Type(void) override const  {  return defNeuroneGateLineAttention;   }
   //--- methods for working with files
   virtual bool      Save(int const file_handle) override;
   virtual bool      Load(int const file_handle) override;
   virtual void      SetOpenCL(COpenCLMy *obj)   override;
   virtual bool      Clear(void) override;
   virtual void      SetActivationFunction(ENUM_ACTIVATION value) override { };
   //---
   virtual bool      WeightsUpdate(CNeuronBaseOCL *source, float tau) override;
   virtual uint      GetVariables(void) const { return iVariables; }
  };

Dentro do objeto, são definidas variáveis para armazenar os principais parâmetros de sua arquitetura:

  • iDimensionK define a dimensionalidade das chaves, determinando o volume de informações acumulado em cada cabeça de atenção;
  • iVariables armazena o número de variáveis analisadas durante o processamento dos sinais;
  • iHeads define o número de cabeças de atenção paralelas, proporcionando uma análise multicanal da série temporal.

O processamento das consultas, chaves e valores é implementado por meio de uma combinação dos objetos CNeuronBaseOCL e CNeuronConvOCL. O objeto cQKV desempenha um papel especial, pois gera simultaneamente todos os componentes da atenção multi-head. Em seguida, o resultado único é dividido em três tensores (q, k e v) para executar as operações matemáticas da atenção. Essa abordagem permite gerar simultaneamente todos os componentes necessários, acelera os cálculos na GPU e reduz a carga sobre a CPU, preservando ao mesmo tempo a precisão da análise multi-head.

Todos os componentes internos do nosso objeto CNeuronGateLineAttention são declarados estaticamente, por isso o construtor e o destrutor da classe permanecem vazios. A inicialização completa do bloco ocorre no método Init, que define os parâmetros de funcionamento do objeto, conecta o kernel computacional OpenCL e configura os componentes internos.

bool CNeuronGateLineAttention::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                                    uint variables, uint dimension, uint dimension_k, uint heads,
                                    ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronSpikeConv::Init(numOutputs, myIndex, open_cl, heads * dimension_k, heads * dimension_k,
                                                  dimension, 1, variables, optimization_type, batch))
      return false;

O algoritmo do método começa transferindo o controle para a classe pai CNeuronSpikeConv. Isso estabelece a estrutura básica do objeto. Essa abordagem garante uniformidade na configuração e permite utilizar os algoritmos de processamento e computação paralela herdados da classe pai.

Além disso, como mencionado anteriormente, a classe pai fornece os mecanismos necessários para combinar os resultados da atenção multi-head em um único sinal de saída, o que se reflete nos parâmetros transmitidos e facilita a integração do novo objeto à arquitetura geral do modelo.

Após a conclusão bem-sucedida da inicialização da classe pai, armazenamos os parâmetros específicos do objeto: a dimensionalidade das chaves iDimensionK, o número de cabeças de atenção iHeads e o número de variáveis analisadas iVariables.

   iDimensionK = dimension_k;
   iHeads = heads;
   iVariables = variables;

Esses parâmetros determinam as dimensões da atenção multi-head e a estrutura dos tensores que acumularão e transmitirão informações dentro do bloco.

Em seguida, passamos à inicialização dos componentes internos. O primeiro a ser inicializado é o objeto cQKV. Ele gera simultaneamente os três componentes da atenção multi-head: as consultas q, as chaves k e os valores v.

   int index = 0;
   if(!cQKV.Init(0, index, OpenCL, dimension, dimension, 3*iHeads*iDimensionK, 1, iVariables, optimization, iBatch))
      return false;
   cQKV.SetActivationFunction(SIGMOID);

Essa abordagem acelera os cálculos, distribui de forma eficiente os recursos da GPU e garante a geração precisa de todos os componentes da atenção multi-head. Para cQKV, é definida uma função de ativação sigmoide, que regula suavemente o sinal de entrada e o prepara para o processamento recorrente.

Em seguida, são inicializados os objetos cQ, cK e cV.

   index++;
   if(!cQ.Init(0, index, OpenCL, iHeads * iDimensionK * iVariables, optimization, iBatch))
      return false;
   cQ.SetActivationFunction(None);
   index++;
   if(!cK.Init(0, index, OpenCL, cQ.Neurons(), optimization, iBatch))
      return false;
   cK.SetActivationFunction(None);
   index++;
   if(!cV.Init(0, index, OpenCL, cQ.Neurons(), optimization, iBatch))
      return false;
   cV.SetActivationFunction(None);

Eles são destinados ao armazenamento dos respectivos tensores e participam das operações de multiplicação matricial. Aqui, não são utilizadas funções de ativação.

O próximo componente fundamental é cKV. Ele armazena os resultados da multiplicação matricial dos tensores de chaves e valores.

   index++;
   if(!cKV.Init(0, index, OpenCL, cQ.Neurons()*iDimensionK, optimization, iBatch))
      return false;
   cKV.SetActivationFunction(None);

Dessa forma, o objeto acumula as informações necessárias para o cálculo da atenção e as encaminha às etapas subsequentes de processamento.

O objeto cGate é responsável pela implementação dos gates da atenção recorrente. Com o auxílio de uma função de ativação sigmoide, ele regula dinamicamente qual parte do estado anterior será preservada e como ela será combinada com os dados de entrada atuais.

   index++;
   if(!cGate.Init(0, index, OpenCL, dimension, dimension, iDimensionK * iHeads, 1, iVariables, optimization, iBatch))
      return false;
   cGate.SetActivationFunction(SIGMOID);

Isso é especialmente importante para séries temporais financeiras, nas quais os sinais podem ser esparsos e ruidosos: o mecanismo de gates permite filtrar oscilações irrelevantes e, ao mesmo tempo, preservar informações importantes de etapas anteriores.

O objeto cState acumula o estado anterior da atenção, viabilizando o processamento recorrente. Ele armazena as informações acumuladas sobre dependências relevantes ao longo do tempo, que depois participam da geração da saída final do bloco.

   index++;
   if(!cState.Init(0, index, OpenCL, cKV.Neurons(), optimization, iBatch))
      return false;
   cState.SetActivationFunction(None);

Em conjunto com cKV e cGate, isso permite implementar uma atenção linear recorrente com gates, capaz de considerar tanto oscilações locais de curto prazo do mercado quanto tendências globais, identificando com precisão os sinais relevantes.

O componente que conclui a configuração interna é cMHAttention. Ele combina os diferentes canais de atenção e gera o sinal final para os cálculos subsequentes.

   index++;
   if(!cMHAttention.Init(0, index, OpenCL, cQ.Neurons(), optimization, iBatch))
      return false;
   cMHAttention.SetActivationFunction(None);
//---
   return true;
  }

O método Init verifica se cada componente foi inicializado com sucesso e retorna false caso alguma etapa falhe. Dessa forma, obtemos um objeto totalmente preparado para operar, capaz de processar séries temporais financeiras com eficiência ao combinar a precisão da atenção contínua multi-head com as vantagens da dinâmica spiking discreta e do acúmulo recorrente de informações.

Após concluir a inicialização do objeto, a próxima etapa é a implementação da propagação para frente. Essa etapa é implementada no método feedForward, que realiza o processamento sequencial dos dados de entrada, o acúmulo recorrente do estado e a geração dos resultados da atenção multi-head.

bool CNeuronGateLineAttention::feedForward(CNeuronBaseOCL *NeuronOCL)
  {
   if(!cQKV.FeedForward(NeuronOCL))
      return false;

Na primeira etapa, é chamado o método de mesmo nome do objeto cQKV. Aqui são gerados os tensores combinados das consultas q, chaves k e valores v para a atenção multi-head. Essa abordagem acelera os cálculos, pois os três componentes são gerados simultaneamente, e sua separação posterior em tensores individuais permite executar as operações padrão da atenção.

Em seguida, é chamado o método FeedForward do objeto cGate, que gera os sinais dos gates.

   if(!cGate.FeedForward(NeuronOCL))
      return false;

O mecanismo de gates regula qual parte do estado anterior será preservada e qual parte será atualizada com os dados de entrada atuais. Isso é especialmente importante para séries temporais financeiras, nas quais os sinais são esparsos e sujeitos a ruído: os gates ajudam a filtrar oscilações ruidosas e a preservar padrões relevantes.

A próxima etapa consiste em dividir a saída combinada de cQKV em três tensores separados por meio da função DeConcat.

   if(!DeConcat(cQ.getOutput(), cK.getOutput(), cV.getOutput(), cQKV.getOutput(),
                     iDimensionK, iDimensionK, iDimensionK, iHeads * iVariables))
      return false;

Depois disso, é realizada a multiplicação matricial das chaves pelos valores usando MatMul. O resultado é armazenado no objeto cKV, que acumula as informações necessárias para a atualização recorrente do estado, garantindo o armazenamento eficiente dos dados para os cálculos subsequentes.

   if(!MatMul(cK.getOutput(), cV.getOutput(), cKV.getOutput(), iDimensionK, 1,
                                      iDimensionK, iHeads * iVariables, true))
      return false;

A atualização recorrente do estado é realizada no objeto cState. Primeiro, chamamos SwapOutputs para preservar os valores da propagação para frente anterior.

   if(!cState.SwapOutputs())
      return false;
   if(!DiagMatMul(cGate.getOutput(), cState.getPrevOutput(), cState.getOutput(),
              iDimensionK, iDimensionK, iHeads * iVariables, cState.Activation()))
      return false;
   if(!SumAndNormilize(cState.getOutput(), cKV.getOutput(), cState.getOutput(),
                                                  iDimensionK, false, 0, 0, 0, 1))
      return false;

Em seguida, aplicamos DiagMatMul à saída dos gates e ao estado anterior. Nessa etapa, é formado o novo estado atual do bloco. Depois disso, somamos o resultado aos valores de cKV. Essas operações permitem destacar sinais relevantes e suprimir o ruído, proporcionando uma representação precisa das informações para a etapa seguinte.

A etapa final consiste na multiplicação matricial da consulta cQ pelo estado atualizado cState, formando a saída final da atenção multi-head no objeto cMHAttention.

   if(!MatMul(cQ.getOutput(), cState.getOutput(), cMHAttention.getOutput(), 1,
                          iDimensionK, iDimensionK, iVariables * iHeads, true))
      return false;

Esse resultado é então passado para a classe pai CNeuronSpikeConv para a geração de impulsos spiking discretos.

   if(!CNeuronSpikeConv::FeedForward(cMHAttention.AsObject()))
      return false;
//---
   return true;
  }

Dessa forma, os cálculos contínuos da atenção são convertidos em eventos que podem ser utilizados na dinâmica spiking e no posterior prognóstico das tendências de mercado.

Em conjunto, o método feedForward estabelece um fluxo de dados integrado: geração dos tensores, regulação por gates, acúmulo recorrente do estado e combinação da atenção multi-head. Essa abordagem permite ao modelo processar com eficiência séries temporais financeiras, considerando tanto oscilações de curto prazo quanto dependências de longo alcance e gerando sinais precisos para prever as tendências do mercado.

Aqui, optamos deliberadamente por não adicionar as conexões residuais características da arquitetura Transformer clássica, pois planejamos utilizar vários módulos de atenção em paralelo. A presença de uma conexão residual independente em cada um deles poderia deslocar excessivamente o foco para os dados de entrada e reduzir a influência dos sinais extraídos. Para evitar esse efeito, prevemos uma única rota compartilhada de conexões residuais entre todos os módulos de atenção dentro de uma mesma camada do modelo. Isso permite preservar o equilíbrio entre a análise dos dados de entrada e o aprendizado com base nos padrões identificados.

No entanto, a propagação para frente é apenas metade do caminho. Para treinar o modelo, é necessário distribuir corretamente o erro de previsão entre todos os componentes envolvidos, de acordo com sua contribuição para o resultado final. No objeto CNeuronGateLineAttention, esse mecanismo é implementado no método calcInputGradients, responsável pela retropropagação do erro e pela geração dos gradientes de todos os componentes internos.

bool CNeuronGateLineAttention::calcInputGradients(CNeuronBaseOCL *NeuronOCL)
  {
   if(!NeuronOCL)
      return false;
//---
   if(!CNeuronSpikeConv::calcInputGradients(cMHAttention.AsObject()))
      return false;

Na primeira etapa, o método verifica se o ponteiro recebido para o objeto de dados de entrada NeuronOCL é válido. Em seguida, chama o método de mesmo nome da classe pai, passando o ponteiro para o objeto cMHAttention, a fim de propagar para a atenção multi-head os gradientes recebidos dos objetos subsequentes do modelo e distribuir a contribuição de cada cabeça para o resultado global.

A etapa seguinte consiste no cálculo dos gradientes da consulta cQ e do estado acumulado cState. O método MatMulGrad distribui o erro proporcionalmente à influência de cada sinal de entrada sobre o resultado final da atenção multi-head.

   if(!MatMulGrad(cQ.getOutput(), cQ.getGradient(),
                  cState.getOutput(), cState.getGradient(),
                  cMHAttention.getGradient(), 1, iDimensionK,
                  iDimensionK, iVariables * iHeads, true))
      return false;

Depois disso, os gradientes são propagados para o objeto cKV, com o ajuste dos valores levando em conta a função de ativação.

   if(!DeActivation(cKV.getOutput(), cKV.getGradient(), cState.getGradient(), cKV.Activation()))
      return false;

A parte dos gates exige atenção especial. O método DiagMatMulGrad distribui o erro entre a saída dos gates cGate e o estado anterior cState.

   if(!DiagMatMulGrad(cGate.getOutput(), cGate.getGradient(),
                      cState.getPrevOutput(), cKV.getPrevOutput(),
                      cState.getGradient(), iDimensionK, iDimensionK,
                      iHeads * iVariables))
      return false;
   Deactivation(cGate)

Em seguida, o bloco de gates é desativado, levando em conta a função de ativação para ajustar corretamente os gradientes.

Os gradientes das chaves cK e dos valores cV são calculados por meio de MatMulGrad.

   if(!MatMulGrad(cK.getOutput(), cK.getGradient(),
                  cV.getOutput(), cV.getGradient(),
                  cKV.getGradient(), iDimensionK, 1,
                  iDimensionK, iHeads * iVariables, true))
      return false;

Depois, todos os gradientes (cQ, cK, cV) são novamente combinados em um único tensor cQKV com a função Concat.

   if(!Concat(cQ.getGradient(), cK.getGradient(), cV.getGradient(), cQKV.getGradient(),
              iDimensionK, iDimensionK, iDimensionK, iHeads * iVariables))
      return false;
   Deactivation(cQKV)

Isso permite preservar a consistência dos gradientes e distribuir corretamente o erro entre os componentes da atenção multi-head.

Em seguida, precisamos propagar os gradientes do erro até o nível dos dados de entrada. Aqui, os valores são transmitidos por dois fluxos de informação. Primeiro, passamos os valores provenientes do objeto cQKV.

   if(!NeuronOCL.CalcHiddenGradients(cQKV.AsObject()))
      return false;

Depois disso, substituímos temporariamente o ponteiro para o buffer de gradientes, a fim de preservar os valores obtidos anteriormente.

   CBufferFloat* temp = NeuronOCL.getGradient();
   if(!NeuronOCL.SetGradient(PrevOutput, false))
      return false;

Em seguida, propagamos os gradientes provenientes do objeto cGate e somamos os valores dos dois fluxos de informação.

   if(!NeuronOCL.CalcHiddenGradients(cGate.AsObject()))
      return false;
   if(!SumAndNormilize(PrevOutput, temp, temp, GetFilters(), false, 0, 0, 0, 1))
      return false;
   if(!NeuronOCL.SetGradient(temp, false))
      return false;
//---
   return true;
  }

A etapa final consiste em restaurar os ponteiros dos buffers de dados ao estado original, deixando-os prontos para a atualização dos pesos durante a otimização dos parâmetros.

Dessa forma, o método calcInputGradients estabelece todo o fluxo de distribuição do erro, desde a saída final da atenção multi-head até cada componente interno. Isso garante o treinamento correto da atenção linear recorrente com gates e permite que o modelo se adapte com eficiência às séries temporais financeiras, levando em conta tanto oscilações locais quanto tendências globais do mercado.

A última etapa da implementação do objeto CNeuronGateLineAttention é a otimização dos parâmetros do modelo, implementada no método updateInputWeights. É aqui que são atualizados os pesos de todos os componentes internos que possuem parâmetros treináveis.

bool CNeuronGateLineAttention::updateInputWeights(CNeuronBaseOCL *NeuronOCL)
  {
   if(!cQKV.UpdateInputWeights(NeuronOCL))
      return false;
   if(!cGate.UpdateInputWeights(NeuronOCL))
      return false;
   if(!CNeuronSpikeConv::updateInputWeights(cMHAttention.AsObject()))
      return false;
//---
   return true;
  }

O método transfere sequencialmente o controle para os objetos cQKV e cGate, atualizando seus pesos com base nos gradientes acumulados. Em seguida, o controle é transferido para a classe pai, responsável por atualizar os parâmetros do bloco que combina os resultados da atenção multi-head.

Vale observar que CNeuronGateLineAttention possui poucos parâmetros treináveis internos, por isso o método se limita a uma transferência sequencial de controle entre os componentes. Ainda assim, essa etapa é fundamental: é nela que o modelo se adapta às séries temporais financeiras, ajustando os pesos para prever tendências com maior precisão e identificar com eficiência os sinais relevantes.

Com isso, concluímos a implementação da classe CNeuronGateLineAttention. Seu código completo, juntamente com todos os métodos, está disponível no anexo do artigo.

Hoje avançamos de forma extensa e minuciosa na análise e implementação, analisando e implementando passo a passo os principais componentes da atenção linear recorrente com gates. Agora é o momento de assimilar o que foi desenvolvido. No próximo artigo, continuaremos esse desenvolvimento e levaremos a implementação à sua conclusão lógica. Treinaremos o modelo e avaliaremos sua eficiência com dados históricos reais dos mercados financeiros.


Conclusão

Neste artigo, analisamos em detalhes e implementamos o bloco de atenção linear recorrente com gates no objeto CNeuronGateLineAttention. Passo a passo, percorremos desde a inicialização e a geração dos tensores até a propagação para frente, a retropropagação do erro e a otimização dos pesos, mostrando como cada componente influencia o processamento das séries temporais financeiras.

Dedicamos atenção especial às decisões arquiteturais: regulação por gates, acúmulo recorrente do estado, combinação da atenção multi-head e um único caminho residual compartilhado por todos os módulos dentro da camada. Em conjunto, esses elementos formam um mecanismo capaz de identificar sinais relevantes, filtrar ruído e considerar tanto oscilações de curto prazo do mercado quanto tendências de longo prazo.

Hoje estabelecemos uma base sólida para o funcionamento prático do modelo. No próximo artigo, continuaremos esse desenvolvimento, levando a implementação até as etapas de treinamento e teste com dados históricos reais. Isso permitirá avaliar a eficácia das abordagens propostas em condições reais de mercado e como elas contribuem para a geração de previsões precisas.


Referências


Programas usados no artigo

# Nome Tipo Descrição
1 Study.mq5 Expert Advisor EA de treinamento offline de modelos
2 StudyOnline.mq5 Expert Advisor EA de treinamento online de modelos
3 Test.mq5 Expert Advisor EA para teste do modelo
4 Trajectory.mqh Biblioteca de classes Estrutura de descrição do estado do sistema e da arquitetura dos modelos
5 NeuroNet.mqh Biblioteca de classes Biblioteca de classes para criação de rede neural
6 NeuroNet.cl Biblioteca Biblioteca de código do programa OpenCL

Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/19762

Arquivos anexados |
MQL5.zip (3135.77 KB)
Últimos Comentários | Ir para discussão (1)
TahianaBE
TahianaBE | 30 set. 2025 em 16:54
Olá. Gostaria de dizer que, ao tentar compilar o arquivo Neuronet.mqh, encontrei erros relacionados ao número incorreto de parâmetros no arquivo Math.mqh. Em um artigo anterior, o senhor respondeu que precisávamos alterar MathPow para ::MathPow; é nesse ponto que estou confuso. Como não sou programador, não sei se devo alterar as funções MathPow encontradas no arquivo Neuronet.mqh ou no Math.mqh. Se o senhor puder me esclarecer, ficaria muito grato.
Atenciosamente.
Negociando opções sem opções (Parte 3): Estratégias complexas com opções Negociando opções sem opções (Parte 3): Estratégias complexas com opções
São analisadas estratégias com opções para mercado lateral (não direcionais) e para mercado em tendência (direcionais), bem como sua implementação em MQL5. O EA desenvolvido no artigo anterior é aprimorado com a exibição dos níveis de opções. Agora é hora de analisar o funcionamento e implementar as estratégias que os traders de opções utilizam na prática.
Desenvolvendo um EA multimoeda (Parte 29): Aprimoramento do pipeline Desenvolvendo um EA multimoeda (Parte 29): Aprimoramento do pipeline
Vamos tornar mais prático o uso do pipeline de otimização automática, percorrendo todo o caminho desde a criação do projeto de otimização até o teste do EA final. Para facilitar a compreensão, reproduziremos passo a passo todo o processo de criação do EA final, fazendo pausas para aplicar os ajustes desejados.
Está chegando o novo MetaTrader 5 e MQL5 Está chegando o novo MetaTrader 5 e MQL5
Esta é apenas uma breve resenha do MetaTrader 5. Eu não posso descrever todos os novos recursos do sistema por um período tão curto de tempo - os testes começaram em 09.09.2009. Esta é uma data simbólica, e tenho certeza que será um número de sorte. Alguns dias passaram-se desde que eu obtive a versão beta do terminal MetaTrader 5 e MQL5. Eu ainda não consegui testar todos os seus recursos, mas já estou impressionado.
Previsão da distribuição condicional com uma MLP Previsão da distribuição condicional com uma MLP
Neste artigo, analisaremos um modelo de regressão baseado em MLP que prevê não apenas a esperança matemática condicional, mas também a variância condicional. Em outras palavras, treinaremos nossa rede para prever toda a distribuição dos preços futuros com base em um vetor de atributos de entrada. Para isso, porém, precisaremos implementar nossa própria função de perda.