Redes neurais em trading: abordagem semântica baseada em spikes para identificação espaço-temporal (Componentes principais)
Introdução
No universo do trading algorítmico e da análise financeira, a ideia de enxergar não apenas números, mas eventos sempre despertou o interesse dos pesquisadores. Fluxos de cotações e ordens, picos de atividade, quedas bruscas e períodos de calmaria: tudo isso lembra um organismo vivo, pulsando ao ritmo da liquidez global. No entanto, a maioria dos modelos tradicionais enxerga apenas estatísticas, um conjunto de valores sequenciais. Eles analisam o preço, mas não percebem sua dinâmica. A capacidade de reconhecer significado nas mudanças tornou-se um dos pilares das abordagens modernas de processamento de dados de mercado.
No artigo anterior da série, conhecemos em detalhes o framework S3CE-Net, que combina princípios de percepção orientada a eventos com aprendizado profundo. Esse framework funciona como uma espécie de ponte entre dois universos: o biológico e o computacional. De um lado, herda a rapidez e a eficiência dos sistemas baseados em spikes, nos quais a informação é transmitida por impulsos esparsos, porém significativos. De outro, preserva a profundidade estrutural das redes neurais clássicas, capazes de realizar generalizações e análises complexas.
A base da arquitetura é o framework SEW-ResNet, que permite extrair atributos de forma estável e eficiente mesmo em redes profundas, sem perda de informação. Sobre essa base são incorporados mecanismos mais sofisticados: a atenção espaço-temporal SSAM e o módulo STFS. O primeiro estabelece relações entre eventos ao longo da sequência, estabelecendo dependências coerentes com a causalidade, enquanto o segundo realiza a amostragem adaptativa de atributos no tempo e no espaço. Com isso, o S3CE-Net se transforma em um sistema de percepção dinâmico, no qual cada impulso carrega significado.
A essência da arquitetura pode ser resumida da seguinte forma: ela aprende a reconhecer estrutura onde outros modelos enxergam ruído. Graças à sua natureza orientada a eventos, o modelo não opera em intervalos fixos, mas diretamente por impulsos, o que o aproxima do ritmo real do mercado. O SSAM concentra a atenção nos trechos dos dados analisados em que realmente ocorrem mudanças, enquanto o STFS acrescenta flexibilidade e robustez, permitindo que o modelo aprenda sem depender excessivamente de cenários específicos. Isso transforma o S3CE-Net em um sistema integrado de análise semântica do fluxo de eventos.
O valor prático do S3CE-Net no trading está justamente em sua natureza orientada a eventos. Um modelo construído com base nesses princípios não reage a cada oscilação, mas apenas aos microeventos que realmente alteram o equilíbrio do mercado. Um pico de volume, o surgimento de uma ordem de grande porte ou uma mudança na estrutura da liquidez são interpretados como sinais significativos, e não como ruído aleatório. As decisões do modelo se baseiam no contexto e na experiência acumulada, isto é, em como o mercado vinha se comportando até aquele momento.
Essa abordagem se torna especialmente valiosa em situações nas quais os métodos tradicionais perdem estabilidade. Quando os indicadores se confundem diante de dados contraditórios e os modelos clássicos começam a sofrer sobreajuste, o S3CE-Net mantém a clareza da percepção. Sua atenção permanece concentrada no que realmente importa. Com isso, ele é capaz de analisar o passado e captar o estado atual do mercado com latência mínima, praticamente acompanhando o ritmo dos próprios movimentos do mercado.
Mas não podemos esquecer que, por trás de toda ideia elegante, existe uma engenharia rigorosa. Para que o modelo permaneça rápido, robusto e adaptativo, é necessário planejar cuidadosamente a execução dos cálculos, uso racional da memória e preservação rigorosa da causalidade no processamento dos eventos. Por isso, a implementação prática deixa de ser apenas uma forma de validar a teoria e se torna uma etapa essencial da evolução do framework. É ela que permite avaliar até que ponto a ideia permanece viável fora das condições de laboratório e se ela pode, de fato, transformar-se em uma ferramenta analítica para o trader.
Assim, a teoria começa gradualmente a ganhar vida. E talvez seja justamente na capacidade do modelo de captar o ritmo e os impulsos do fluxo financeiro que esteja o aspecto que distingue a inteligência artificial de um simples algoritmo. Seguimos desenvolvendo o S3CE-Net como uma ferramenta capaz de perceber o mercado.
A visualização do framework S3CE-Net criada pelo autor é apresentada abaixo.

Módulo SSAM
Na parte prática do artigo anterior, chegamos a uma etapa fundamental: a implementação dos algoritmos de propagação para frente e propagação reversa do módulo SSAM no programa OpenCL. É justamente nos kernels computacionais que se estabelece a base do funcionamento do mecanismo de atenção, capaz de operar com um fluxo de eventos baseados em spikes respeitando rigorosamente a causalidade temporal. Essa foi uma etapa necessária, pois é no nível do OpenCL que se concentra a maior parte do processamento computacional: cálculo das matrizes de atenção, aplicação das máscaras e propagação dos gradientes.
Agora avançamos um nível acima, para a estrutura do programa principal, onde é definida a lógica de interação entre todos os módulos do framework. Aqui criamos a classe CNeuronSSAM, que passa a representar a camada de atenção baseada em spikes dentro da arquitetura. Enquanto no OpenCL lidávamos diretamente com a matemática, agora tratamos da composição arquitetural que integra cálculos de baixo nível à lógica neural de alto nível.
class CNeuronSSAM : public CNeuronSpikeActivation { protected: uint iUnits; uint iHeads; uint iDimension; bool bMaskFuture; CNeuronSpikeConv cQKV; CParams cDiaganalBias; int ciScore; CNeuronBaseOCL cMHAttentionOut; CNeuronSpikeConv cW0; CNeuronBaseOCL cResidual; CNeuronBatchNormOCL cNorm; //--- virtual bool SpikeMHAttention(void); virtual bool SpikeMHAttentionGrad(void); //--- virtual bool feedForward(CNeuronBaseOCL *NeuronOCL) override; virtual bool updateInputWeights(CNeuronBaseOCL *NeuronOCL) override; virtual bool calcInputGradients(CNeuronBaseOCL *NeuronOCL) override; public: CNeuronSSAM(void) {}; ~CNeuronSSAM(void) {}; //--- virtual bool Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint units, uint window, uint heads, uint dimension_k, bool mask_future, ENUM_OPTIMIZATION optimization_type, uint batch); //--- virtual int Type(void) override const { return defNeuronSSAM; } //--- methods for working with files virtual bool Save(const int file_handle) override; virtual bool Load(const int file_handle) override; virtual void SetOpenCL(COpenCLMy *obj) override; //--- virtual bool WeightsUpdate(CNeuronBaseOCL *source, float tau) override; virtual void TrainMode(bool flag) override; virtual bool Clear(void) override; };
A classe herda a funcionalidade básica de CNeuronSpikeActivation, preservando a mecânica geral de ativação dos neurônios baseados em spikes. Ao mesmo tempo, introduz sua própria hierarquia de componentes, cada um responsável por um aspecto específico da atenção espaço-temporal. Na seção protegida são declarados os principais parâmetros:
- iUnits — dimensão da sequência analisada,
- iHeads — número de cabeças de atenção,
- iDimension — comprimento do vetor de um elemento das entidades internas,
- bMaskFuture — flag de mascaramento que determina se o modelo pode considerar informações futuras ao construir o mapa de atenção.
O núcleo da classe é formado por um conjunto de objetos internos pelos quais passa todo o fluxo computacional. O objeto cQKV é responsável pela geração dos três tensores internos Q, K e V, a camada neural base cMHAttentionOut acumula o resultado da atenção multi-head, enquanto cResidual implementa os mecanismos clássicos de conexão residual, além da normalização.
No entanto, cDiaganalBias ocupa uma posição especial: esse componente é responsável por ajustar a atenção na projeção espacial. Ele implementa a ideia de Intra-Frame Relative Position Bias, proposta pelos autores do framework S3CE-Net, um mecanismo de viés posicional relativo dentro do frame que permite ao modelo compreender melhor as relações locais entre os tokens.
Na prática, esse bias desempenha várias funções ao mesmo tempo. Primeiro, estabiliza os cálculos, evitando que a atenção se disperse quando as dimensões espaciais são grandes. Segundo, permite ao modelo representar melhor o contexto das estruturas locais: ele aprende a identificar padrões característicos dos elementos mais próximos sem perder a perspectiva global. Por fim, no contexto de dados de mercado, em que os tokens podem representar níveis de preço, clusters de volume ou segmentos do fluxo de ordens, cDiaganalBias ajuda o algoritmo a concentrar a atenção nas regiões de maior atividade. Afinal, eventos de mercado próximos em preço ou no tempo raramente são independentes. E é justamente essa dependência que ajuda a orientar os pesos aprendidos pelo mecanismo de atenção.
A principal vantagem da estrutura da classe CNeuronSSAM está na declaração estática de todos os seus componentes internos. À primeira vista, essa decisão parece puramente técnica, mas na prática traz vantagens arquiteturais importantes.
Primeiro, a estrutura estática elimina a necessidade de alocação e liberação dinâmica de memória. O construtor e o destrutor da classe podem permanecer vazios. Não há riscos de vazamentos de memória, fragmentação ou acesso incorreto a ponteiros não inicializados. Cada objeto interno existe durante todo o ciclo de vida da instância da classe, formando uma estrutura extremamente estável e inteiramente definida em tempo de compilação.
Segundo, a abordagem estática aumenta significativamente o determinismo dos cálculos, uma propriedade essencial ao trabalhar com OpenCL e modelos baseados em spikes. Nesse contexto, cada ciclo de execução e cada operação são importantes. A ausência de alocação dinâmica de memória garante que os tempos de inicialização e execução permaneçam previsíveis, enquanto a arquitetura do modelo não depende do estado da memória no momento da execução. Isso é particularmente importante na criação de algoritmos de trading, em que a estabilidade e a precisão dos cálculos influenciam diretamente a confiabilidade das decisões tomadas.
Além disso, a declaração estática se encaixa perfeitamente no conceito de otimização de baixo nível adotado no OpenCL. Ao contrário dos objetos criados dinamicamente, as instâncias estáticas podem ser associadas previamente a buffers específicos da GPU, reduzindo a sobrecarga a cada acesso ao dispositivo. Isso não apenas acelera a execução da camada, como também evita atrasos típicos causados pela criação e destruição frequentes de contextos computacionais.
Assim, a estrutura estática dos componentes internos de CNeuronSSAM não é apenas uma questão de conveniência. Trata-se de uma decisão arquitetural cuidadosamente planejada, que garante estabilidade, previsibilidade e máxima eficiência computacional.
Essa base sólida e estaticamente definida sustenta toda a inicialização subsequente, implementada no método Init, que transforma a classe em um componente completo do framework S3CE-Net, pronto para operar em tempo real.
bool CNeuronSSAM::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint units, uint window, uint heads, uint dimension_k, bool mask_future, ENUM_OPTIMIZATION optimization_type, uint batch) { if(!CNeuronSpikeActivation::Init(numOutputs, myIndex, open_cl, units * window, optimization_type, batch)) return false;
O método Init é responsável por preparar uma instância funcional do bloco neural e interligar todos os seus componentes internos. Sua execução começa com a chamada do método de mesmo nome da classe pai, que define os principais parâmetros da camada.
Em seguida, é configurada a arquitetura interna da camada. Os parâmetros iHeads e iDimension determinam a configuração da atenção multi-head.
iHeads = (uint)MathMax(heads, 1); iDimension = (uint)MathMax(dimension_k, 1); iUnits = units; bMaskFuture = mask_future;
Os valores são processados pela função MathMax para evitar dimensões nulas. Isso é especialmente importante durante a criação dos buffers no OpenCL.
Na etapa seguinte, são inicializados os componentes internos, os blocos que formam a lógica da camada. Primeiro, é criado o objeto cQKV, responsável pela formação dos três tensores básicos da atenção: Query, Key e Value. Seu tamanho é definido pelo produto do número de cabeças, da dimensão dos vetores e do fator três, garantindo a distribuição correta dos parâmetros entre todos os subespaços de atenção.
uint index = 0; if(!cQKV.Init(0, index, OpenCL, window, window, 3 * iHeads * iDimension, iUnits, 1, optimization, iBatch)) return false;
Depois, é inicializado cDiaganalBias, que cria a estrutura destinada ao armazenamento e ao cálculo da matriz de vieses posicionais, o Intra-Frame Relative Position Bias mencionado anteriormente. Esse mecanismo introduz um ajuste baseado na posição relativa dos elementos dentro do frame. Esse bloco pode ser visto como um refinamento do mecanismo de atenção: ele permite que o modelo considere a proximidade espacial entre os tokens e ajuste o foco de acordo com a posição relativa entre eles.
index++; if(!cDiaganalBias.Init(0, index, OpenCL, iUnits, optimization, iBatch)) return false; cDiaganalBias.SetActivationFunction(None); CBufferFloat* temp=cDiaganalBias.getWeightsParams(); if(!temp || !temp.Fill(0)) return false;
Na etapa inicial, o objeto cDiaganalBias é inicializado com valores nulos, o que é perfeitamente coerente durante a inicialização inicial do modelo. Nesse momento, a rede neural ainda não dispõe de conhecimento sobre a posição relativa dos elementos dentro do frame e, portanto, ainda não conhece a geometria interna do espaço de entrada. A inicialização com zeros fornece um ponto de partida neutro, no qual todos os vieses relativos têm o mesmo valor e o mecanismo de atenção atua exclusivamente com base nos tensores Q, K e V.
Também merece atenção especial a criação do buffer ciScore, que reserva uma região de memória exclusivamente no contexto OpenCL para armazenar valores intermediários de similaridade entre queries e keys. Esse buffer funciona como armazenamento temporário durante o cálculo dos pesos de atenção. A verificação do resultado garante que a memória tenha sido efetivamente alocada; caso contrário, a inicialização é interrompida. Essa validação torna o procedimento mais confiável e controlado.
ciScore = OpenCL.AddBuffer(sizeof(float) * iUnits * iUnits * iHeads, CL_MEM_READ_WRITE); if(ciScore == INVALID_HANDLE) return false;
Em seguida, os demais módulos da camada são conectados sequencialmente. O objeto cMHAttentionOut é responsável por agregar os resultados da atenção multi-head e é inicializado com a dimensão total iUnits * iDimension * iHeads. Sua função de ativação é desativada para preservar o valor resultante das operações de atenção sem transformações adicionais.
index++; if(!cMHAttentionOut.Init(0, index, OpenCL, iUnits * iDimension * iHeads, optimization, iBatch)) return false; cMHAttentionOut.SetActivationFunction(None);
Na sequência vem cW0, um transformador linear aplicado à saída da atenção. Seus parâmetros são treinados para adaptar de forma flexível a dimensão do resultado às operações subsequentes.
index++; if(!cW0.Init(0, index, OpenCL, iHeads * iDimension, iHeads * iDimension, window, iUnits, 1, optimization, iBatch)) return false;
O componente cResidual implementa a conexão residual, reincorporando parte do sinal original ao resultado da camada e ajudando o modelo a evitar a degradação dos gradientes durante o treinamento profundo. Em cResidual, a função de ativação também é explicitamente desativada e, além disso, é definido um gradiente compartilhado com cW0. Isso permite sincronizar o treinamento entre as camadas e manter a estabilidade durante a retropropagação do erro.
index++; if(!cResidual.Init(0, index, OpenCL, Neurons(), optimization, iBatch)) return false; cResidual.SetActivationFunction(None); if(!cResidual.SetGradient(cW0.getGradient(), true)) return false;
Por fim, é inicializado cNorm, que estabiliza a distribuição dos valores na saída da camada, reduzindo possíveis distorções provocadas por alterações dinâmicas no mecanismo de atenção e preparando os dados para a ativação baseada em spikes realizada pela classe pai.
index++; if(!cNorm.Init(0, index, OpenCL, Neurons(), iBatch, optimization)) return false; //--- return true; }
Cada um desses componentes recebe seu próprio índice, compartilha o mesmo objeto OpenCL e utiliza os parâmetros de otimização correspondentes, formando uma hierarquia bem definida coordenada pela classe principal. Após a execução bem-sucedida de todas as etapas do algoritmo, o método é concluído e a instância de CNeuronSSAM fica pronta para uso: todos os buffers, conexões e cadeias computacionais estão configurados, e a camada encontra-se totalmente integrada à estrutura do modelo.
O algoritmo de propagação para frente é implementado no método feedForward. Seu funcionamento mostra como as informações dos eventos baseados em spikes percorrem todos os componentes do módulo de atenção, incluindo os vieses posicionais e as conexões residuais.
bool CNeuronSSAM::feedForward(CNeuronBaseOCL *NeuronOCL) { if(!cQKV.FeedForward(NeuronOCL)) return false;
Na primeira etapa, são formados os tensores Q, K e V, que constituem a base para o cálculo da atenção multi-head. Essa é uma etapa fundamental: sem queries, keys e values corretamente formados, não é possível realizar a agregação posterior das informações.
Em seguida, são gerados os parâmetros de viés otimizados. Em outras palavras, são produzidos os valores efetivamente utilizados para ajustar os pesos de atenção.
if(bTrain) if(!cDiaganalBias.FeedForward()) return false;
É importante observar que essa operação é executada apenas no modo de treinamento (bTrain == true). No modo de produção, quando o modelo já está treinado, os parâmetros de viés permanecem estáticos e já estão armazenados no buffer de resultados do objeto. Essa abordagem reduz a sobrecarga durante a operação em tempo real.
Na etapa seguinte, é chamado SpikeMHAttention, que funciona como um wrapper do kernel de mesmo nome no programa OpenCL. Sua função é preparar o kernel e colocá-lo na fila de execução. Nesse ponto, são configurados todos os buffers necessários, os parâmetros são definidos e as referências para Q, K e V são passadas ao kernel, bem como para os vieses posicionais de cDiaganalBias.
if(!SpikeMHAttention()) return false;
Toda a carga computacional, incluindo a atenção multi-head, a normalização dos pesos e a incorporação do Intra-Frame Relative Position Bias ao cálculo, é processada exclusivamente na GPU, aproveitando os recursos de execução paralela. Essa abordagem permite processar grandes tensores e operações complexas de atenção com alto desempenho, enquanto a CPU permanece disponível para gerenciar o fluxo de dados e monitorar o estado do modelo.
Depois disso, é realizada uma transformação linear que adapta a saída da atenção multi-head à dimensão dos dados originais e prepara os valores para a conexão residual.
if(!cW0.FeedForward(cMHAttentionOut.AsObject())) return false;
Em seguida, os sinais dos dois fluxos de informação são somados.
if(!SumAndNormilize(NeuronOCL.getOutput(), cW0.getOutput(), cResidual.getOutput(), Neurons() / iUnits, false, 0, 0, 0, 1)) return false;
Depois, é necessário preparar os valores obtidos para a ativação baseada em spikes. Mantemos a abordagem Element-Wise: cada célula do tensor de saída é processada de forma independente, permitindo controlar com precisão a dinâmica dos spikes.
Para manter os valores em uma faixa comparável e evitar a perda de definição dos sinais, é aplicada a camada de normalização em lote cNorm. Ela estabiliza a distribuição dos dados após a soma dos valores dos dois fluxos de informação, mantendo-os em uma faixa adequada para a ativação subsequente.
if(!cNorm.FeedForward(cResidual.AsObject())) return false;
Após a normalização, o sinal normalizado é passado para o método de mesmo nome da classe pai, onde são executadas as operações de ativação adaptativa baseada em spikes.
return CNeuronSpikeActivation::feedForward(cNorm.AsObject());
}
Essa abordagem permite integrar corretamente o módulo SSAM à rede como um todo, encaminhando os eventos baseados em spikes para as etapas seguintes e preservando a alta precisão e a estabilidade do mecanismo de atenção.
Após a propagação para frente e a obtenção dos valores previstos, inicia-se a etapa de retropropagação do erro, na qual é necessário comparar a previsão com os dados reais e distribuir os gradientes entre todos os componentes da camada. Essa tarefa é executada pelo método calcInputGradients.
bool CNeuronSSAM::calcInputGradients(CNeuronBaseOCL *NeuronOCL) { if(!NeuronOCL) return false;
A execução do método começa verificando se o ponteiro recebido para o objeto de dados de origem NeuronOCL é válido. Em seguida, é chamado o método de mesmo nome da classe pai. Nessa etapa, os gradientes da ativação baseada em spikes são propagados até a camada de normalização em lote.
if(!CNeuronSpikeActivation::calcInputGradients(cNorm.AsObject())) return false;
Depois, o erro é passado ao objeto responsável pelas conexões residuais. Graças à substituição dos ponteiros para os buffers dos gradientes de erro, os valores obtidos são encaminhados automaticamente ao objeto que agrega os resultados da atenção multi-head.
if(!cResidual.CalcHiddenGradients(cNorm.AsObject())) return false;
A etapa seguinte consiste na chamada de SpikeMHAttentionGrad, onde é realizada a retropropagação através da atenção multi-head baseada em spikes.
if(!SpikeMHAttentionGrad()) return false;
Nesse ponto, são considerados todos os pesos de atenção, os tensores QKV e os vieses posicionais cDiaganalBias. Essa etapa é fundamental, pois permite ajustar os parâmetros de atenção de acordo com a estrutura local e global do sinal analisado.
Depois disso, os gradientes são propagados até os dados de origem. Primeiro, distribuímos o erro proveniente dos componentes Q, K e V.
if(!NeuronOCL.CalcHiddenGradients(cQKV.AsObject())) return false;
Em seguida, é necessário adicionar os gradientes provenientes do caminho residual. Aqui existem duas possibilidades, dependendo da presença de uma função de ativação no objeto de dados de origem. Quando não há função de ativação, os gradientes provenientes do caminho residual são propagados diretamente e somados aos valores acumulados anteriormente.
if(NeuronOCL.Activation() == None) { if(!SumAndNormilize(NeuronOCL.getGradient(), cResidual.getGradient(), NeuronOCL.getGradient(), Neurons() / iUnits, false, 0, 0, 0, 1)) return false; } else { if(!DeActivation(NeuronOCL.getOutput(), cResidual.getPrevOutput(), cResidual.getGradient(), NeuronOCL.Activation())) return false; if(!SumAndNormilize(NeuronOCL.getGradient(), cResidual.getPrevOutput(), NeuronOCL.getGradient(), Neurons() / iUnits, false, 0, 0, 0, 1)) return false; } //--- return true; }
Quando há uma função de ativação, os gradientes de erro provenientes do caminho residual são primeiro multiplicados pela derivada da função de ativação utilizada. Somente depois disso os gradientes são somados.
Como resultado, o método calcInputGradients distribui o erro entre todos os componentes envolvidos: da ativação baseada em spikes, passando pela normalização, conexões residuais, transformações lineares e atenção multi-head, até a sequência analisada e o bias posicional. Esse mecanismo garante a atualização correta de todos os parâmetros da camada durante o treinamento e permite que o modelo melhore gradualmente a precisão das previsões, preservando a estabilidade e a consistência dos cálculos.
A otimização propriamente dita dos parâmetros é realizada no método updateInputWeights. Aqui não há lógica complexa de redistribuição nem cálculo manual de novos valores: todos os parâmetros treináveis já estão contidos nos componentes internos da classe. A otimização se resume à chamada sequencial dos métodos de mesmo nome desses componentes, cada um responsável por atualizar seus próprios pesos e buffers com base nos gradientes recebidos.
bool CNeuronSSAM::updateInputWeights(CNeuronBaseOCL *NeuronOCL) { if(!cQKV.UpdateInputWeights(NeuronOCL)) return false; if(!cDiaganalBias.UpdateInputWeights()) return false; if(!cW0.UpdateInputWeights(cMHAttentionOut.AsObject())) return false; if(!cNorm.UpdateInputWeights(cResidual.AsObject())) return false; //--- return CNeuronSpikeActivation::updateInputWeights(cNorm.AsObject()); }
Essa arquitetura garante modularidade e simplifica a manutenção do código. Cada bloco é responsável apenas por seus próprios parâmetros, enquanto a classe controla a ordem de atualização e garante a consistência dos dados.
Assim, o método updateInputWeights funciona como um núcleo coordenador que aciona sequencialmente a otimização de todas as estruturas internas, garantindo uma atualização consistente e estável do modelo. Essa abordagem segue integralmente o princípio de encapsulamento: os detalhes do treinamento permanecem internos aos componentes, enquanto o nível superior gerencia apenas a sincronização dos componentes e a ordem de execução.
O código completo da classe CNeuronSSAM, juntamente com todos os seus métodos, está disponível no anexo do artigo. Isso permite ao leitor não apenas compreender a lógica de funcionamento da camada em nível conceitual, mas também utilizar o código pronto em experimentos ou adaptá-lo às suas próprias tarefas.
Integração com a versão baseada em spikes do ResNeXt
A próxima etapa do nosso trabalho será integrar o módulo SSAM ao algoritmo SEW-ResNeXt implementado anteriormente. Na descrição original do framework S3CE-Net, os autores destacam que vários módulos de atenção podem ser integrados entre os blocos SEW-ResNet em diferentes níveis. Essa abordagem se encaixa naturalmente na estrutura de modelos sequenciais, sem a necessidade de criar módulos ou estruturas auxiliares adicionais.
No entanto, vale lembrar que, em nossa implementação do bloco SEW-ResNeXt, são utilizados dois blocos Bottleneck paralelos, que analisam os dados de origem em representações distintas: temporal e espacial. Cada um desses fluxos carrega informações próprias e complementares. A análise temporal permite captar a dinâmica dos sinais e identificar padrões na sequência de eventos, enquanto a análise espacial registra relações locais entre os elementos dos dados na fatia atual.
Considerando essa arquitetura, seria natural incorporar um módulo SSAM a cada uma das ramificações de processamento, permitindo tratar a atenção de forma independente nas representações espacial e temporal para as representações espacial e temporal. Essa abordagem permitirá aproveitar o potencial da atenção multi-head baseada em spikes em diferentes contextos sem comprometer a estrutura geral do modelo e poderá aumentar sua precisão graças à adaptação independente dos pesos de atenção para cada tipo de informação.
Consequentemente, a integração do SSAM nas duas ramificações do bloco SEW-ResNeXt torna-se uma continuação natural do conceito arquitetural do S3CE-Net, proporcionando flexibilidade, robustez e capacidade de capturar com precisão as dependências espaço-temporais dos dados analisados.
É dentro desse conceito que criamos o novo módulo CNeuronSSAMResNeXtBlock, que herda a funcionalidade base de CNeuronSpikeResNeXtBlock.
class CNeuronSSAMResNeXtBlock : public CNeuronSpikeResNeXtBlock { protected: CNeuronSSAM caSSAM[2]; //--- virtual bool feedForward(CNeuronBaseOCL *NeuronOCL) override; virtual bool updateInputWeights(CNeuronBaseOCL *NeuronOCL) override; virtual bool calcInputGradients(CNeuronBaseOCL *NeuronOCL) override; public: CNeuronSSAMResNeXtBlock(void) {}; ~CNeuronSSAMResNeXtBlock(void) {}; //--- virtual bool Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint chanels_in, uint chanels_out, uint units_in, uint units_out, uint group_size, uint groups, uint heads, uint dimension_k, ENUM_OPTIMIZATION optimization_type, uint batch); //--- virtual int Type(void) override const { return defNeuronSSAMResNeXtBlock; } //--- 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 WeightsUpdate(CNeuronBaseOCL *source, float tau) override; virtual void TrainMode(bool flag) override; virtual bool Clear(void) override; };
Na seção protegida da classe é declarado apenas o array caSSAM, que contém duas instâncias do módulo SSAM, uma para cada ramificação de análise dos dados de origem. Todos os demais componentes necessários são herdados da classe pai.
O construtor e o destrutor da classe permanecem vazios, pois todos os componentes internos são estáticos. Sua inicialização e configuração são realizadas no método Init, responsável pela inicialização completa do bloco, pela criação dos objetos internos e por sua configuração.
bool CNeuronSSAMResNeXtBlock::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint chanels_in, uint chanels_out, uint units_in, uint units_out, uint group_size, uint groups, uint heads, uint dimension_k, ENUM_OPTIMIZATION optimization_type, uint batch) { if(!CNeuronSpikeResNeXtBlock::Init(numOutputs, myIndex, open_cl, chanels_in, chanels_out, units_in, units_out, group_size, groups, optimization_type, batch)) return false;
O método começa pela inicialização do bloco base SEW-ResNeXt, na qual são definidos os principais parâmetros do módulo: número de canais de entrada e saída, quantidade de elementos da sequência, tamanho dos grupos e estratégia de otimização. Isso garante que a arquitetura original do Bottleneck esteja corretamente configurada antes da incorporação do mecanismo de atenção.
Em seguida, são inicializados dois módulos SSAM, um para cada fluxo de informação do bloco. A primeira instância trabalha com as saídas do Bottleneck na dimensão temporal, aplicando o mascaramento de etapas futuras (mask_future = true).
if(!caSSAM[0].Init(0, 0, OpenCL, units_out, chanels_out, heads, dimension_k, true, optimization, iBatch)) return false;
A segunda instância processa os resultados do Bottleneck espacial. Nesse caso, não utilizamos mascaramento (mask_future = false).
if(!caSSAM[1].Init(0, 0, OpenCL, chanels_out, units_out, heads, dimension_k, false, optimization, iBatch)) return false;
Também vale lembrar que, para reduzir as operações de cópia de dados entre buffers durante a inicialização da classe pai, sincronizamos os ponteiros para os buffers de gradientes do primeiro Bottleneck e do objeto responsável pelas conexões residuais. Agora precisamos inserir entre eles o módulo SSAM correspondente. Como os tensores de entrada e saída do módulo possuem as mesmas dimensões, simplesmente trocamos entre os módulos os ponteiros para os buffers de gradientes de erro.
CBufferFloat* temp = caBottleneck[0].getGradient(); if(!caBottleneck[0].SetGradient(caSSAM[0].getGradient(), false)) return false; if(!caSSAM[0].SetGradient(temp, false)) return false; //--- return true; }
Essa troca garante a propagação correta do erro entre os blocos e permite que ambos os componentes adaptem seus parâmetros.
Como resultado, o método Init cria um módulo totalmente pronto para uso, no qual SEW-ResNeXt e SSAM são integrados em uma única cadeia computacional, enquanto os gradientes e buffers são configurados de forma a preservar a estabilidade do treinamento e maximizar a eficiência.
O algoritmo de propagação para frente é implementado do zero, pois a integração de dois módulos SSAM no interior do bloco inviabiliza o uso do método de mesmo nome da classe pai. Os dados percorrem toda a cadeia de forma sequencial, e cada ramificação é processada de acordo com suas particularidades e sua posição na arquitetura.
bool CNeuronSSAMResNeXtBlock::feedForward(CNeuronBaseOCL *NeuronOCL) { if(!caTranspose[0].FeedForward(NeuronOCL)) return false;
Primeiro, o sinal de entrada passa pela camada de transposição caTranspose[0], que prepara os dados para o processamento paralelo e os alinha na dimensão espacial. Em seguida, os blocos Bottleneck do SEW-ResNeXt processam o fluxo de entrada em sequência: o primeiro analisa diretamente os dados de origem na forma de uma sequência temporal.
if(!caBottleneck[0].FeedForward(NeuronOCL)) return false;
O segundo recebe o sinal transposto, permitindo que as informações sejam processadas separadamente em representações distintas.
if(!caBottleneck[1].FeedForward(caTranspose[0].AsObject())) return false;
Nessa etapa, entram em ação os módulos SSAM: cada uma das duas instâncias processa seu próprio fluxo, temporal ou espacial. O primeiro SSAM aplica o mascaramento das etapas futuras, ajustando a dinâmica temporal, enquanto o segundo constrói o mapa de atenção para a representação espacial.
for(int i = 0; i < 2; i++) if(!caSSAM[i].FeedForward(caBottleneck[i].AsObject())) return false;
O resultado do segundo módulo também passa por uma camada de transposição, garantindo o alinhamento correto antes da combinação com o sinal residual de cResidual.
if(!caTranspose[1].FeedForward(caSSAM[1].AsObject())) return false;
Em seguida, são somadas as saídas dos módulos SSAM e do caminho residual. O resultado é armazenado no buffer da primeira camada do módulo Element-Wise da função cG. Isso garante a combinação consistente de todos os fluxos de informação e estabiliza os valores antes de encaminhá-los aos componentes seguintes do bloco.
if(!cResidual.FeedForward(NeuronOCL)) return false; //--- CNeuronBaseOCL* current = cG[0]; if(!current) return false; if(!SumAndNormilize(caSSAM[0].getOutput(), caTranspose[1].getOutput(), current.getOutput(), caTranspose[1].GetCount(), false, 0, 0, 0, 1) || !SumAndNormilize(current.getOutput(), cResidual.getOutput(), current.getOutput(), caTranspose[1].GetCount(), false, 0, 0, 0, 1)) return false;
Depois, os dados percorrem sequencialmente todos os elementos restantes da função cG, em que cada objeto recebe o sinal da etapa anterior e executa sua própria propagação para frente.
for(int i = 1; i < cG.Total(); i++) { current = cG[i]; if(!current || !current.FeedForward(cG[i - 1])) return false; }
Ao final, o sinal normalizado e agregado é passado ao método de mesmo nome da classe pai para aplicação da ativação adaptativa baseada em spikes.
return CNeuronSpikeActivation::feedforward(cG[-1]); }
Essa abordagem permite preservar a modularidade e a estrutura do bloco, ao mesmo tempo que garante o processamento e a integração corretos dos fluxos de dados temporais e espaciais, além de aproveitar de forma eficiente o potencial da atenção multi-head baseada em spikes.
Alterações semelhantes também foram introduzidas nos métodos de propagação reversa, levando em conta a integração dos dois módulos SSAM e sua interação com as ramificações do SEW-ResNeXt. Para facilitar a visualização e a compreensão da estrutura de funcionamento do bloco, sugerimos que esses métodos sejam analisados diretamente no código.
Vale destacar que o princípio permanece o mesmo: os gradientes são propagados sequencialmente por cada componente, incluindo os blocos Bottleneck, os módulos SSAM e as camadas de transposição e residuais. A atualização dos parâmetros, por sua vez, é realizada em sequência pelos métodos correspondentes dos objetos internos.
De modo geral, CNeuronSSAMResNeXtBlock funciona como uma camada de integração que combina de forma harmoniosa SEW-ResNeXt e SSAM, permitindo considerar simultaneamente dependências espaço-temporais locais e globais dos dados sem comprometer a estrutura modular do modelo. O código completo da classe e de todos os seus métodos é apresentado no anexo.
Hoje avançamos bastante no desenvolvimento. É um bom momento para fazer uma pequena pausa, organizar as ideias e assimilar o conteúdo apresentado. No próximo artigo, daremos continuidade ao trabalho até concluir o desenvolvimento iniciado, e validaremos necessariamente as abordagens implementadas com dados históricos reais. Isso permitirá avaliar a eficácia do modelo em condições tão próximas quanto possível das condições reais de uso e confirmar se todos os componentes foram integrados corretamente.
Conclusão
Neste artigo, demos continuidade à implementação das abordagens propostas pelos autores do framework S3CE-Net, com foco especial na integração do módulo SSAM ao bloco SEW-ResNeXt. Essa integração permite criar um bloco combinado de alto desempenho, no qual a atenção multi-head baseada em spikes atua em conjunto com os blocos residuais SEW-ResNeXt, proporcionando uma análise profunda e precisa das características temporais e espaciais dos dados de origem.
A flexibilidade arquitetural e a modularidade do framework permitem adaptar o bloco a diferentes cenários de processamento de sinais. Cada componente preserva sua autonomia. Os blocos Bottleneck analisam os dados de origem em suas respectivas representações. Os módulos SSAM ajustam o mapa de atenção em cada fluxo, enquanto as camadas de transposição e residuais garantem a combinação consistente das informações. Essa abordagem permite que o modelo considere simultaneamente dependências locais e padrões globais, proporcionando alta precisão das previsões e robustez ao ruído presente nos dados.
Referências
- S3CE-Net: Spike-guided Spatiotemporal Semantic Coupling and Expansion Network for Long Sequence Event Re-Identification
- Outros artigos da série
Programas usados no artigo
| # | Nome | Tipo | Descrição |
|---|---|---|---|
| 1 | Study.mq5 | Expert Advisor | EA para treinamento offline de modelos |
| 2 | StudyOnline.mq5 | Expert Advisor | EA para 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 redes neurais |
| 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/19956
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.
Análise da influência dos ciclos solares e lunares sobre os preços das moedas
Algoritmo de sistema artificial de circulação coronariana (Artificial Coronary Circulation System, ACCS)
Está chegando o novo MetaTrader 5 e MQL5
Mapas auto-organizáveis de Kohonen em um EA MQL5
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso