Uma Nova Abordagem para Critérios Personalizados em Otimizações (Parte 1): Exemplos de Funções de Ativação
Introdução
A busca pela otimização ideal para encontrar a combinação correta de parâmetros continua. O fórum está repleto de métodos sugeridos para persuadir o Otimizador do MetaTrader a retornar e classificar as várias execuções, permitindo que um desenvolvedor escolha uma combinação (ou combinações) de parâmetros que sejam estáveis. A introdução das prop firms na equação, com seus limites rigorosos — compreensivelmente muito mais rigorosos do que os exigidos por um trader individual — tornou isso ainda mais desafiador.
A capacidade de definir um Critério Personalizado, e até mesmo utilizar o Critério Complexo com sua metodologia opaca, introduziu a possibilidade de reduzir a análise — ou pelo menos a inspeção — dos resultados em Excel, Python, R ou em software proprietário, para obter a melhor permutação de parâmetros.
O problema é que ainda não é incomum ver o uso de return(0) em Critérios Personalizados publicados. Isso está repleto de perigos reais ou potenciais, incluindo a possibilidade de descartar resultados (ligeiramente) indesejados ou, pior ainda, desviar o processo de otimização genética de caminhos potencialmente produtivos.
Em uma tentativa de retornar a alguns primeiros princípios, após conduzir alguns experimentos bastante empíricos, procurei encontrar algumas equações de curvas. Para isso, analisei “Funções de Ativação em Redes Neurais” e adotei e modifiquei algumas para uso aqui. Além disso, tendo esclarecido essas funções, sugeri alguns métodos para colocá-las em uso prático.
O plano para esta série de artigos é o seguinte:
- Introdução e funções de ativação padrão com código MQL5
- Modificações, escalonamento e ponderação e exemplos reais
- Uma ferramenta para explorar diferentes curvas, escalonamento e ponderação
- Quaisquer outros pontos que surgirem...
Estado atual do uso de Critérios Personalizados
Dois colaboradores, pelos quais tenho o maior respeito, fizeram excelentes observações para ilustrar o problema, com preocupações sobre o uso de return(0) e os resultados retornados pelo Critério Complexo. Alain Verleyen, moderador do fórum, relata uma execução retornando uma perda de 6.000 pontuando mais alto do que outra retornando um lucro de 1.736 com mais operações (e pontuando consideravelmente melhor em Fator de Lucro, RF, índice de Sharpe E drawdown). Alain diz: ‘o enigmático "critério complexo" produz alguns resultados estranhos.’ Eu iria além e diria que isso certamente levanta dúvidas significativas sobre a metodologia por trás do Critério Complexo. Se de fato, como Muhammad Fahad comenta mais adiante na mesma discussão — e tenho certeza de que ele está correto — ‘resultados com saldo negativo não são particularmente ruins para otimização, eles desempenham um papel crucial no julgamento da geração futura do ruim versus o pior enquanto os genes estão sendo cruzados’, o retorno de uma pontuação tão positiva pode possivelmente direcionar erroneamente o otimizador genético.
Já faz muito, muito tempo desde que utilizei o MT4, mas lembro vagamente da capacidade de inserir restrições para certas estatísticas de resultado. No MQL5, à primeira vista, não parece haver como fazer isso além de usar o instrumento bastante rudimentar de return(0) em um Critério Personalizado, cujas desvantagens potenciais já foram mencionadas. Ocorre-me que, se pudermos obter um valor de retorno muito próximo de 1 quando a razão ou métrica estiver no ou acima do mínimo desejado, no ou abaixo do máximo desejado, ou até mesmo no ou próximo de um alvo específico, e então multiplicando os valores para cada métrica/razão e depois multiplicando o resultado por 100, podemos estar diante de algo interessante...
Por que buscar inspiração em Redes Neurais?
i) Otimização genética como Rede Neural... Independentemente de o algoritmo genético possuir ou não uma rede neural por trás, o processo lógico parece ser o mesmo — testar uma combinação paramétrica, pontuá-la (de acordo com o critério de otimização selecionado), reter seleções potencialmente lucrativas para aprimoramento adicional e descartar o restante. Isso é repetido até que a melhoria na pontuação selecionada atinja um platô.
ii) Vales, gradientes explosivos, ponderação, normalização e seleção manualO problema da superfície de erro não convexa é bem conhecido em Machine Learning. Da mesma forma, parece que, na otimização genética, podemos acabar em mínimos locais ou pontos de sela, desviados por instruções return(0) que causam o descarte de ‘conhecimento’ pelo algoritmo genético.
O processamento de várias métricas para formar um critério personalizado também exige bastante reflexão... minhas primeiras tentativas foram muito rudimentares, utilizando diversos fatores para divisão e multiplicação, sem mencionar fatores exponenciais, levando a pontuações beirando o absurdamente alto (gradiente explosivo)... O algoritmo de pontuação do critério complexo claramente usa uma função para limitar a pontuação entre 0 e 100, sugerindo o uso de uma função do tipo Sigmoide ou logística.
A necessidade de aplicar pesos aos componentes individuais de um critério personalizado é clara: precisamos usar pesos para determinar a prioridade dos vários componentes, e normalização tanto das entradas, para ajustar a sensibilidade do nosso modelo, quanto da saída, para permitir comparação entre modelos. Abordaremos ponderação e normalização em um artigo futuro.
Quando observamos a saída de uma execução de otimização, seja manualmente ou em uma planilha, podemos ordenar por várias medidas de saída, filtrar resultados e, rapidamente, quase subconscientemente, selecionar conjuntos de parâmetros pela cor das métricas principais. Aqui estamos simplesmente reaplicando os processos descritos, que por si só são uma tentativa de imitar nosso próprio processamento intelectual do resultado completo.
Funções de Ativação em Redes Neurais
Parece-me que as funções que eu estava procurando eram semelhantes às que encontrei há um ou dois anos ao estudar Funções de Ativação em Redes Neurais.
As Funções de Ativação em Redes Neurais podem ser amplamente classificadas em três tipos: binárias, lineares e não lineares:
- Funções binárias classificam valores em 1 de 2 classes retornando 1 ou 0. Elas não são particularmente úteis no nosso caso de uso;
- Funções lineares retornam transformações por simples adição, multiplicação, subtração ou divisão, ou uma combinação dessas operações. Elas também são de pouca ajuda no nosso caso de uso.
- Funções não lineares retornam curvas de resultados que são limitadas nos extremos de x, mas permitem discriminação dos valores de x em uma faixa centrada em 0. Essa faixa e centro podem ser modificados por meio do uso de offsets.
Deve-se dizer que o uso de
return(0);
bem como o retorno irrestrito do produto de inúmeras medidas de resultado, ambos dos quais fui culpado, e ambos os quais ainda vejo nos fóruns, são métodos que replicam os problemas associados a essas duas primeiras classes; por outro lado, funções não lineares tendem a ser limitadas para evitar gradientes explosivos, enquanto mantêm um gradiente de -∞ a ∞.
Como forma de explicação, recapitularei alguns dos exemplos padrão. Vamos analisar ReLU, Softplus, Sigmoid e Tanh, juntamente com, no próximo artigo, minhas próprias modificações: Flipped ReLU, Point ReLU, Flipped Sigmoid, Point Sigmoid, Flipped Tanh e Point Tanh.
a) Função de Ativação ReLU
O objetivo desta função é retornar zero quando x é <= 0 e x quando x > 0, ou, em notação matemática, f(x) = max(0,x). A linha obtida é ilustrada abaixo, e a função MQL5 pode ser definida assim:
double ReLU(double x) { return(MathMax(0, x)); }

Até aqui, tudo bem, você pode dizer, mas eu quero definir um valor mínimo para o Recovery Factor de 5 ou mais..., bem, simplesmente altere o código para * :
input double MinRF = 5.0; double RF = TesterStatistics(STAT_RECOVERY_FACTOR); double DeviationRF = RF - MinRF; double ReLU(double x) { return(MathMax(0, x)); } double ScoreRF = ReLU(DeviationRF);
assim deslocando a linha para a direita, iniciando em 0 quando RF = 5.0, conforme ilustrado:

Isso, claro, dificilmente é mais avançado do que escrever*
input double MinRF = 5.0; double RF = TesterStatistics(STAT_RECOVERY_FACTOR); double ScoreRF = (RF - MinRF);
mas nos dá um ponto de partida...
* há também outra questão: o valor alvo, 5, na verdade retornará 0 e apenas valores >5 retornarão diferente de zero. Não pretendo dedicar tempo a essa questão, pois os diversos outros problemas desta função impedem sua utilidade adicional em nosso caso de uso.
Problemas
Logo fica claro que ainda temos 2 problemas, ambos decorrentes da simplicidade inerente do nosso código... Eu disse, acima:
"Isso, claro, dificilmente é mais avançado do que escrever
input double MinRF = 5.0; double RF = TesterStatistics(STAT_RECOVERY_FACTOR); double ScoreRF = (RF - MinRF);
mas nos dá um ponto de partida..."
Os problemas são bem conhecidos em Redes Neurais:
-
ReLU Morta: a principal fraqueza da ReLU é que neurônios podem "morrer" permanentemente quando produzem apenas zeros. Isso ocorre quando entradas fortemente negativas criam gradientes zero, tornando esses neurônios inativos e incapazes de continuar aprendendo. Isso também é referido como o problema do gradiente que desaparece.
-
Saída Não Limitada: as saídas da ReLU podem crescer indefinidamente para entradas positivas, ao contrário das funções Sigmoid ou Tanh, que possuem limites superiores incorporados. Essa ausência de limite superior pode, às vezes, causar problemas de explosão de gradiente durante o treinamento de redes neurais profundas. Isso também é referido como o problema do gradiente explosivo.
A segunda questão é tratada em estatística pela normalização de valores ao longo de uma janela de observação, mas esse método não está disponível para nós dentro das limitações do otimizador.
Portanto, precisamos de uma solução que
- gere matematicamente restrição, fornecendo uma normalização automatizada; e
- forneça algum grau de linearidade, em vez de retornar 0, ao longo de uma seção significativa da amostra...
b) Função de Ativação Softplus
Não pretendo entrar em muitos detalhes aqui, mas a menciono de passagem como uma etapa entre ReLU e as funções mais avançadas. Ela é bem descrita em Geeksforgeeks e eu simplesmente apresentarei a seguinte imagem, fórmula e código. Deve ficar claro que, embora resolva o problema da ReLU Morta, o problema de Saída Não Limitada permanece. Além disso, há a transição suave entre a cauda muito rasa e a inclinação do lado direito. Isso introduz a necessidade de considerar um offset de correção — um conceito ao qual retornaremos mais adiante:

double Softplus(double x) { return(MathLog(1 + MathExp(x))); }
Aproveite a leitura se desejar, mas agora chegamos às funções que nos fornecem resultados limitados entre 0 e 1...
c) Função de Ativação Sigmoid
A Função Sigmoid é expressa matematicamente como f(x) = 𝛔(x) = 1 / (1 + e^(-x)). Ela é idêntica à derivada da função Softplus e pode ser codificada em MQL5 assim:
double Sigmoid(double x) { return(1 / (1 + MathExp(-x))); }
Observando a curva (abaixo), é imediatamente evidente que
-
ela é limitada entre 0 e 1, resolvendo assim nosso problema de Saída Não Limitada;
-
embora difícil de perceber nesta escala, um gradiente variável, que nunca se torna zero, é mantido de -∞ a ∞; e
-
a curva não deslocada apresenta valor 0,5 quando x = 0.
Para lidar com 3), de modo a aproximar-se de 1 quando x == nosso valor limite (alvo), é necessário deslocar a curva para a esquerda, não apenas pelo nosso valor limite, mas também por uma correção que é obtida na prática usando um valor de correção >= 5.
A implementação em MQL5 é, portanto
input double MinRF = 5.0; sinput double SigCorrection = 5; double RF = TesterStatistics(STAT_RECOVERY_FACTOR); double DeviationRF = RF - MinRF; double CorrectedSigmoid(double x) { return(1 / (1 + MathExp(-(x - SigCorrection)))); } double ScoreRF = CorrectedSigmoid(DeviationRF);A curva da Função Sigmoid não corrigida é ilustrada abaixo para um alvo de 5, juntamente com a derivada (multiplicada por 4), que será discutida a seguir.

d) Derivada da Sigmoid (𝛔')
Isso nos fornece uma forma agradável, centrada e maximizada em x = 0... Ela representa a inclinação da Função Sigmoid, e a fórmula é f(x) = 𝛔(x) - 𝛔^2(x) ou 𝛔(x) * (1 - 𝛔(x)).
A função sofre potencialmente (assim como todas as funções que consideraremos) do problema do gradiente que desaparece, um risco que pode ser amenizado pelo simples expediente de multiplicá-la por 4 (uma vez que 𝛔’(x) = 0.25), o que também, de forma útil, restringe novamente o resultado entre 0 e 1. A centralização em um valor de x nos permite definir um alvo para um ponto ou, mais corretamente, um intervalo centrado em um ponto.
Em MQL5 podemos fazer
input double TargetTrades = 100; double Trades = TesterStatistics(STAT_TRADES); double DeviationTrades = Trades - TargetTrades; double Sigmoid(double x) { return(1 / (1 + MathExp(-x))); } double Deriv4Sigmoid(double x) { return(4 * (Sigmoid(x) * (1 - Sigmoid(x)))); } double ScoreTrades = Deriv4Sigmoid(DeviationTrades);
Nota: como nossos resultados para Sigmoid, sua Derivada (multiplicada por 4), e a seguir Tanh e sua Derivada, ambos reescalados entre 0 & 1, estão todos restritos entre 0 e 1, não precisamos fornecer qualquer escalar adicional em produção. Podemos, claro, multiplicar por qualquer fator para normalizá-los em relação a, ou priorizá-los sobre, quaisquer outros Índices de Otimização que possamos incluir em nosso Critério Personalizado final. Veremos isso no próximo artigo.
Temos uma última função padrão para explorar, e essa é a Tanh...
e) Tangente hiperbólica ou Tanh
Tanh é a última das nossas Funções de Ativação que iremos explorar em nossa busca por utilidade em Critérios Personalizados. Seguirei um padrão semelhante com menos explicação do que antes, mas aplicando os mesmos princípios.
O gráfico a seguir exibe a plotagem da Tanh com sua Derivada.

A fórmula da Tanh é f(x) = (e^x - e^(-x))/(e^x + e^(-x)) e em MQL5 ela possui sua própria função, portanto é simples de codificar. Claro, precisamos lidar com valores alvo e fatores de correção, mas ainda assim isso facilita o processo:
input double MinRF = 5.0; sinput double TanhCorrection = 2.5; // Anything between 2 and 3 would be reasonable (see last figure) double RF = TesterStatistics(STAT_RECOVERY_FACTOR); double DeviationRF = RF - MinRF; double CorrectedRSTanh(double x) { return((MathTanh(x + TanhCorrection) + 1) / 2); // rescaled and corrected } double ScoreRF = CorrectedRSTanh(DeviationRF);
O código acima reescala a função subjacente para restringi-la entre 0 e 1 (somando 1 e dividindo por 2). Ele também introduz um fator de correção e um alvo, e a curva resultante é mostrada no próximo gráfico, ilustrando a Derivada da Tanh. Não deve passar despercebido que a Tanh possui uma forma semelhante à Sigmoid, e as Derivadas entre si; exploraremos essas semelhanças no próximo artigo.
f) Derivada da Tanh
A fórmula matemática para a Derivada da Tanh é f(x) = 1 - Tanh^2(x).
Em MQL5 podemos fazer
input double TargetTrades = 100; double Trades = TesterStatistics(STAT_TRADES); double DeviationTrades = Trades - TargetTrades; double DerivTanh(double x) { return(1 - MathPow(x, 2)); }double ScoreTrades = DerivTanh(DeviationTrades);
O próximo gráfico mostra o efeito de reescalar a Tanh, corrigindo-a para aproximar o ponto de partida em 0 com o valor alvo (limiar) de x, e introduzindo um alvo tanto na Tanh quanto na Derivada da Tanh.

Resumo e Próximos Passos
Neste artigo, consideramos brevemente a relação entre Machine Learning, por um lado, e o processo de otimização com ‘pós-processamento’ (seja genético ou completo), além de Excel ou pós-processamento manual, por outro. Começamos a analisar como diferentes tipos de Funções de Ativação podem refinar Critérios Personalizados e como codificá-las.
Os problemas dos gradientes que desaparecem e dos gradientes explosivos em ReLU e Softplus nos levaram a direcionar nossa atenção para funções com curvas como a da função Sigmoid e também a de sua Derivada. Vimos como essas funções nos permitem favorecer, em nossa pontuação, intervalos com limite único (ou seja, x > t) ou intervalos com duplo limite (t1 < x < t2), eliminando o problema dos gradientes explosivos e mitigando o dos gradientes que desaparecem.
Analisamos o uso de offsets de correção e offsets de alvo para ajudar a garantir o foco nas faixas de valores de atributos desejadas.
No próximo artigo, analisaremos algumas modificações desta lista de funções padrão com atributos semelhantes, mas não idênticos. Também exploraremos a ideia de escalonamento e ponderação antes de analisar exemplos do uso de diferentes funções em Critérios Personalizados.
Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/17429
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.
Caminhe em novos trilhos: Personalize indicadores no MQL5
Introdução às curvas ROC (Receiver Operating Characteristic)
Está chegando o novo MetaTrader 5 e MQL5
Ciência de Dados e ML (Parte 34): Decomposição de séries temporais, desmembrando o mercado de ações até o núcleo
- 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