Discussão do artigo "Redes neurais de retropropagação em matrizes MQL5"

 

Novo artigo Redes neurais de retropropagação em matrizes MQL5 foi publicado:

Este artigo trata da teoria e prática do uso do algoritmo de retropropagação de erros no MQL5 através de matrizes. Oferecemos classes prontas e exemplos de scripts, indicadores e EAs.

Veremos a seguir que o MQL5 oferece um amplo conjunto de funções de ativação incorporadas. A escolha de uma função específica deve ser baseada nas especificidades do problema a ser resolvido (seja ele de regressão, classificação). Geralmente, é possível selecionar várias funções para qualquer problema e, posteriormente, encontrar a mais adequada por meio de experimentação.

Funções de ativação populares

Funções de ativação populares

As funções de ativação podem possuir diferentes intervalos de valores - sendo limitadas ou ilimitadas. Por exemplo, a função sigmoide (3) mapeia os dados para o intervalo [0,+1] (sendo mais adequada para tarefas de classificação), enquanto a tangente hiperbólica direciona os dados para o intervalo [-1,+1] (sendo mais apropriada para tarefas de regressão e previsão).

Autor: Stanislav Korotky

 
Agradecemos ao autor pelo artigo detalhado e pelas conclusões honestas.
O criador de redes neurais anterior, em seus 35 artigos, colocou fotos do Graal, mas na verdade foi o lançamento de uma moeda.

Li um artigo que dizia que, no Ocidente, alguém conseguiu criar um networker neural consistentemente lucrativo, com base na aprendizagem por reforço.
 

Mais agradecimentos ao Stanislav! Finalmente um artigo sobre redes neurais usando matrizes nativas foi escrito....

A propósito. Hoje mesmo descobri que há um novo bloco na documentação referente ao ML.

Документация по MQL5: ONNX модели
Документация по MQL5: ONNX модели
  • www.mql5.com
ONNX модели - Справочник MQL5 - Справочник по языку алгоритмического/автоматического трейдинга для MetaTrader 5
 
Ivan Butko #:
Agradecemos ao autor pelo artigo detalhado e pelas conclusões honestas.
O criador de redes neurais anterior, em seus 35 artigos, colocou imagens do Graal, mas, na realidade, foi um lance de moeda.

"Aquelas coisas que eu esqueci, você nem sabe ainda" - é assim que o autor poderia responder a essas críticas.

 
Rashid Umarov #:

"Essas coisas que eu esqueci você ainda nem sabe" é como um autor pode responder a essas críticas.

Há dois aspectos e isso é importante:

1. Execução, parte técnica, universalidade, elogiada até mesmo por programadores maduros. E aqui, de fato, não posso, não quero e não tentei criticar o código. Estou falando de outra coisa:

2. Testes. O autor destacou que todos os seus modelos funcionam. Na verdade, quando você pressiona o botão "Start" (Iniciar) no testador, aparece uma imagem aleatória das próximas 2 semanas, de 10 pressionamentos 5 - ameixa, 5 - lucro. Naturalmente, se você colocar uma coisa dessas na realidade, a viabilidade de seu depósito será de 50/50. Esse não é o caso. Em minhas perguntas sobre o que é isso e como consertá-lo, o autor disse que deveria ser assim, pois esse é o princípio do modelo. Bem, aqui eu caí simplesmente, sem palavras.
Dois meses eu tentei iniciar essas redes, eles não queriam trabalhar na NVIDIA, graças a Aleksey Vyazmikin, reescreveu o código do autor e tudo funcionou em 3080. E no final - modelos inoperáveis. Isso é duplamente ofensivo.

Portanto, é inapropriado apelar para a autoridade, não critiquei seu profissionalismo.

Mas se eu o ofendi/ofendi, peço desculpas, não tinha esse objetivo.
 
matrix temp;
if(!outputs[n].Derivative(temp, of))

Na retropropagação, as funções derivadas esperam receber x e não a ativação de x (a menos que isso tenha sido alterado recentemente para as funções de ativação às quais se aplica)

Aqui está um exemplo:

#property version   "1.00"

int OnInit()
  {
//---
  double values[];
  matrix vValues;
  int total=20;
  double start=-1.0;
  double step=0.1;
  ArrayResize(values,total,0);
  vValues.Init(20,1);
  for(int i=0;i<total;i++){
  values[i]=sigmoid(start);
  vValues[i][0]=start;
  start+=step;
  }
  matrix activations;
  vValues.Activation(activations,AF_SIGMOID);
  //imprimir sigmoide
  for(int i=0;i<total;i++){
  Print("sigmoidDV("+values[i]+")sigmoidVV("+activations[i][0]+")");
  }
  //derivativos
  matrix derivatives;
  activations.Derivative(derivatives,AF_SIGMOID);
  for(int i=0;i<total;i++){
  values[i]=sigmoid_derivative(values[i]);
  Print("dDV("+values[i]+")dVV("+derivatives[i][0]+")");
  }
//---
   return(INIT_SUCCEEDED);
  }

double sigmoid(double of){
return((1.0/(1.0+MathExp((-1.0*of)))));
}

double sigmoid_derivative(double output){
return(output*(1-output));
}

Também há funções de ativação para as quais você pode enviar mais entradas tanto na ativação quanto na derivação, como o elu, por exemplo

Derivative(output,AF_ELU,alpha);
Activation(output,AF_ELU,alpha);
 
Lorentzos Roussos #:

Na retropropagação, as funções derivadas esperam receber x e não a ativação de x (a menos que isso tenha sido alterado recentemente para as funções de ativação às quais se aplica)

Não tenho certeza do que você quer dizer. Há fórmulas no artigo, que são convertidas exatamente nas linhas do código-fonte. As saídas são obtidas com a chamada de ativação durante o estágio de avanço e, em seguida, pegamos suas derivadas na retropropagação. Provavelmente, você não percebeu que a indexação das matrizes de saída nas classes é feita com +1 em relação à indexação dos pesos das camadas.

 
Stanislav Korotky #:

Não tenho certeza do que você quer dizer. Há fórmulas no artigo, que são convertidas exatamente nas linhas do código-fonte. As saídas são obtidas com a chamada de ativação durante o estágio de feedforward e, em seguida, pegamos seus derivados na retropropagação. Provavelmente, você não percebeu que a indexação das matrizes de saída nas classes é feita com +1 em relação à indexação dos pesos das camadas.

Sim, a matriz temporária é multiplicada pelos pesos e, em seguida, output[] contém os valores de ativação.

No back prop, você está obtendo as derivações desses valores de ativação, enquanto a função derivada MatrixVector espera que você envie os valores temporários

Aqui está a diferença nas derivadas

 

Permita-me simplificá-lo:

#property version   "1.00"

int OnInit()
  {
//---
  //consideremos que x é a saída da camada anterior (*) pesos de um nó 
  //o valor que vai para a ativação . 
    double x=3;
  //obtemos o sigmoide pela fórmula abaixo 
    double activation_of_x=sigmoid(x);
  //e para a derivada, fazemos 
    double derivative_of_activation_of_x=sigmoid_derivative(activation_of_x);
  //fazemos isso com o matrixvector
    vector vX;
    vX.Init(1);
    vX[0]=3;
    //criamos um vetor para ativações
    vector vActivation_of_x;
    vX.Activation(vActivation_of_x,AF_SIGMOID);
    //criamos um vetor para derivações
    vector vDerivative_of_activation_of_x,vDerivative_of_x;
    vActivation_of_x.Derivative(vDerivative_of_activation_of_x,AF_SIGMOID);
    vX.Derivative(vDerivative_of_x,AF_SIGMOID);
    
    Print("NormalActivation("+activation_of_x+")");
    Print("vector Activation("+vActivation_of_x[0]+")");
    Print("NormalDerivative("+derivative_of_activation_of_x+")");
    Print("vector Derivative Of Activation Of X ("+vDerivative_of_activation_of_x[0]+")");
    Print("vector Derivative Of X ("+vDerivative_of_x[0]+")");
    //você está fazendo a derivada vetorial da ativação de x, que retorna o valor errado
    //vectorMatrix espera que você envie x e não a ativação(x)
//---
   return(INIT_SUCCEEDED);
  }

double sigmoid(double of){
return((1.0/(1.0+MathExp((-1.0*of)))));
}

double sigmoid_derivative(double output){
return(output*(1-output));
}


 
Lorentzos Roussos #:

Permita-me simplificar:

Entendo seu ponto de vista. De fato, a derivada sigmoide é formulada por meio de 'y', ou seja, por meio do valor sigmoide no ponto x, que é y(x): y'(x) = y(x)*(1-y(x)). É exatamente assim que os códigos do artigo foram implementados.

Seu script de teste calcula a "derivada" tomando x como entrada, não y, portanto, os valores são diferentes.

 
Stanislav Korotky #:

Entendo seu ponto de vista. De fato, a derivada sigmoide é formulada por meio de 'y', ou seja, por meio do valor sigmoide no ponto x, que é y(x): y'(x) = y(x)*(1-y(x)). É exatamente assim que os códigos do artigo foram implementados.

Seu script de teste calcula a "derivada" tomando x como entrada, não y, portanto, os valores são diferentes.

Sim, mas os valores de ativação são passados para a função de derivação enquanto ela espera os valores de pré-ativação. É isso que estou dizendo.

E você não entendeu o ponto, o valor correto é com o x como entrada (o valor correto de acordo com a própria função do mq).

Você não está armazenando os pesos output_of_previous * em algum lugar (eu acho), que é o que deve ser enviado nas funções de derivação (de acordo com a própria função do mq novamente, vou enfatizar isso)