Discussão do artigo "Redes neurais de retropropagação em matrizes MQL5" - página 2

 
Lorentzos Roussos #:

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.

Acho que você está errado. A derivação é calculada de acordo com a fórmula que forneci acima: y'(x) = y(x)*(1-y(x)), em que x é o estado do neurônio antes da ativação e y(x) é o resultado da aplicação da função de ativação. Você não usa o valor pré-ativação para calcular a derivação, mas sim o resultado da ativação (y). Aqui está um teste simplificado:

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

void OnStart()
{
  vector x = {{0.68}};
  vector y;
  x.Activation(y, AF_SIGMOID);                            // obteve o resultado da ativação/sigmoide como "y(x)" em y[0]
  vector d;
  x.Derivative(d, AF_SIGMOID);                            // obteve a derivada do sigmoide em x
  Print(derivative(x[0]), " ", derivative(y[0]), " ", d); // 0.2176 0.2231896389723258 [0.2231896389723258]
}
 
Stanislav Korotky #:

Acho que você está errado. A derivação é calculada de acordo com a fórmula que forneci acima: y'(x) = y(x)*(1-y(x)), em que x é o estado do neurônio antes da ativação e y(x) é o resultado da aplicação da função de ativação. Você não usa o valor pré-ativação para calcular a derivação, mas sim o resultado da ativação (y). Aqui está um teste simplificado:

Sim, é isso que estou dizendo, a derivada correta corresponde à derivada chamada a partir dos valores x.

Na função back prop, você está chamando o equivalente y.Derivative(d,AF_SIGMOID)

A matriz de saídas é y no backprop do artigo. Não acho que você esteja armazenando o equivalente a x em uma matriz para chamar a derivada a partir dela.

(novamente, de acordo com a função mq)

--

Mesmo no seu exemplo, você está chamando a derivada de x, aposto que você digitou y no início e depois "errou".

Diga a eles no fórum russo. Isso poupará muito tempo de muitas pessoas se eles puderem adicionar isso aos documentos.

Agradecimentos

 
Stanislav Korotky #:

Acho que você está errado. A derivação é calculada de acordo com a fórmula que forneci acima: y'(x) = y(x)*(1-y(x)), em que x é o estado do neurônio antes da ativação e y(x) é o resultado da aplicação da função de ativação. Você não usa o valor pré-ativação para calcular a derivação, mas sim o resultado da ativação (y). Aqui está um teste simplificado:

Deixe-me simplificar isso.

Este é o seu exemplo

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

void OnStart()
{
  vector x = {{0.68}};
  vector y;
  x.Activation(y, AF_SIGMOID);                            // obteve o resultado da ativação/sigmoide como "y(x)" em y[0]
  vector d;
  x.Derivative(d, AF_SIGMOID);                            // obteve a derivada do sigmoide em x
  Print(derivative(x[0]), " ", derivative(y[0]), " ", d); // 0.2176 0.2231896389723258 [0.2231896389723258]
}

No seu exemplo, você está chamando x.Derivative para preencher o vetor de derivadas d.

Você não está chamando y.Derivative para preencher as derivadas, por quê? Porque ele retorna os valores errados. (e você provavelmente percebeu isso, por isso usou x.Derivative).

O que é y? Os valores de ativação de x.

Então, quando você faz isso:

x.Activation(y, AF_SIGMOID);  

Você preenche y com os valores de ativação de x, mas chama a derivada em x e não em y(o que está correto de acordo com a função mql5).

Em seu artigo, no feed forward, a temperatura é x

matrix temp = outputs[i].MatMul(weights[i]);

E y seria os valores de ativação de x. Que matriz é essa?

temp.Activation(outputs[i + 1], i < n - 1 ? af : of)

Osoutputs. No artigo, a matriz y ( da qual você não chama a derivada no exemplo) é a matriz de saídas .

(o que estamos vendo no código acima é o equivalente x.Activation(y,AF) do exemplo que preenche y com os valores de ativação)

No seu código de backprop, você não está chamando x.Derivative porque x(matriz temp = outputs[i].MatMul(weights[i]);)

não está armazenado em nenhum lugar e você não pode chamá-lo. Você está chamando o equivalente a y.Derivative, que retorna os valores errados

outputs[n].Derivative(temp, of)
outputs[i].Derivative(temp, af)

porque y tem os valores de ativação.

Novamente, de acordo com a função mql5.

Portanto, no seu exemplo, você está usando a chamada correta e, no seu artigo, está usando a chamada errada.

Parabéns a você

 

Então, você quer isso:

   bool backProp(const matrix &target)
   {
      if(!ready) return false;
   
      if(target.Rows() != outputs[n].Rows() ||
         target.Cols() != outputs[n].Cols())
         return false;
      
      // camada de saída
      matrix temp;
      //*se(!outputs[n].Derivative(temp, of))
      //* return false;
      if(!outputs[n - 1].MatMul(weights[n - 1]).Derivative(temp, of))
         return false;
      matrix loss = (outputs[n] - target) * temp; // registro de dados por linha
     
      for(int i = n - 1; i >= 0; --i) // para cada camada, exceto a de saída
      {
         //*// remove unusable pseudo-errors for neurons, added as constant bias source
         //*// (in all layers except for the last (where it wasn't added))
         //*if(i < n - 1) loss.Resize(loss.Rows(), loss.Cols() - 1);
         #ifdef  BATCH_PROP
         matrix delta = speed[i] * outputs[i].Transpose().MatMul(loss);
         adjustSpeed(speed[i], delta * deltas[i]);
         deltas[i] = delta;
         #else
         matrix delta = speed * outputs[i].Transpose().MatMul(loss);
         #endif
         
         //*se(!outputs[i].Derivative(temp, af))
         //* return false;
         //*perda = loss.MatMul(weights[i].Transpose()) * temp;
         if(i > 0) // retropropagar a perda para as camadas anteriores
         {
            if(!outputs[i - 1].MatMul(weights[i - 1]).Derivative(temp, af))
               return false;
            matrix mul = loss.MatMul(weights[i].Transpose());
            // remover pseudo-erros inutilizáveis para neurônios, adicionados como fonte de polarização constante
            mul.Resize(mul.Rows(), mul.Cols() - 1);
            loss = mul * temp;
         }
         
         weights[i] -= delta;
      }
      return true;
   }

Vou pensar sobre isso.

 
Stanislav Korotky #:

Então, você quer isso:

Vou pensar sobre isso.

À primeira vista, parece bom, sim. O cálculo no local é mais rápido do que o armazenamento, presumo.

👍

 
Lorentzos Roussos #:

À primeira vista, parece bom, sim. O cálculo no local é mais rápido do que o armazenamento, presumo.

Acho que sei o motivo pelo qual ela foi originalmente codificada por meio da saída da função de ativação. Em todas as minhas bibliotecas NN anteriores e em algumas bibliotecas de outras pessoas que usei, as derivadas são calculadas por meio de saídas, porque é mais simples e eficaz (durante a adaptação à API de matriz, não prestei atenção à diferença). Por exemplo:

sigmoid' = sigmoid * (1 - sigmoid)
tanh' = 1 - tanh^2
softsign' = (1 - |softsign|)^2

Dessa forma, não precisamos manter os argumentos de pré-ativação (matrizes) ou recalculá-los novamente durante a fase de retropropagação (como é feito na correção). Não gosto das duas abordagens. O cálculo da "autoderivada", por assim dizer, parece mais elegante. Por isso, prefiro encontrar algumas referências com fórmulas para autoderivadas de todas (ou muitas) funções de ativação suportadas e voltar à minha abordagem original.

É interessante que não é necessário que a fórmula autoderivada seja estritamente derivada da função de ativação - qualquer função com efeito equivalente é suficiente.
 
Stanislav Korotky #:

Acho que sei o motivo pelo qual ela foi originalmente codificada por meio da saída da função de ativação. Em todas as minhas bibliotecas NN anteriores e em algumas outras bibliotecas de pessoas que usei, as derivadas são calculadas por meio das saídas, porque é mais simples e eficaz (durante a adaptação à API de matriz, não prestei atenção à diferença). Por exemplo:

Dessa forma, não precisamos manter os argumentos de pré-ativação (matrizes) ou recalculá-los novamente durante a fase de retropropagação (como é feito na correção). Não gosto das duas abordagens. O cálculo da "autoderivada", por assim dizer, parece mais elegante. Por isso, prefiro encontrar algumas referências com fórmulas para autoderivadas de todas (ou muitas) funções de ativação suportadas e voltar à minha abordagem original.

Sim, mas a mq decidiu fazer isso dessa forma, então se aplica a todas as funções de ativação.

Em palavras simples, em vez de a função derivada se "adaptar" à função de ativação (como as três que você mencionou poderiam receber as saídas), eles decidiram que as funções receberiam os valores de pré-ativação de qualquer maneira. Tudo bem, o problema é que isso não está na documentação.

A suposição padrão de qualquer pessoa é que ele se adapta ao AF.

Isso é ruim para os novatos (como eu, por exemplo), pois eles "resolvem" o problema antes mesmo de começar.

(A comparação entre redes baseadas em objetos e redes baseadas em matrizes também seria um artigo muito interessante e ajudaria muitos programadores que não têm conhecimento de matemática).

De qualquer forma, eu o coloquei no tópico que um moderador tem para relatar problemas de documentação.

(fora do tópico: você pode usar este TanH, é mais rápido e correto, eu acho)

double customTanH(double of){
  double ex=MathExp(2*of);
  return((ex-1.0)/(ex+1.0));
}
É interessante que não é necessário que a fórmula autoderivada seja estritamente derivada da função de ativação - qualquer função com efeito equivalente é suficiente.

Você quer dizer como um "substituto"?

Por exemplo, um nó recebe um erro em sua saída, e você conhece a "flutuação" da saída, portanto, se você "mudá-la" para uma ativação mais simples e derivá-la, ela funcionará?

Então, em teoria, seria como "regularizar" a saída, mas sem fazer isso e apenas multiplicar pela derivada da regularização antes da derivada da ativação?

Por exemplo:

tanh output -1 -> +1 
sigmoid output 0 -> +1 
tanh to sigmoid output = (tanh_out+1)/2.0
and you just multiply by the derivative of that which is 0.5 ? (without touching the tanh outputs at all)
mql5 documentation errors, defaults or inconsistencies.
mql5 documentation errors, defaults or inconsistencies.
  • 2023.04.07
  • www.mql5.com
Page : https://www.mql5.com/en/docs/globals/globalvariabletime This is inexact, GlobalVariableCheck() does NOT modify the last access time. 2023.04...
 
Correção de bug.
Arquivos anexados:
MatrixNet.mqh  23 kb
 

Um administrador respondeu ao tópico dos moderadores sobre isso. Talvez seja de seu interesse

Fórum sobre negociação, sistemas de negociação automatizados e teste de estratégias de negociação

Erros, padrões ou inconsistências na documentação do mql5.

Rashid Umarov, 2023.04.18 11:36

Será aprimorado assim que possível. Por enquanto, você pode usar este arquivo de inclusão como referência.

@Stanislav Korotky
 
Stanislav Korotky # : Correção de bug.

@Stanislav Korotky

Seus esforços para colocar o conceito de rede neural na MQL são muito apreciados. Esse é realmente um excelente trabalho para iniciantes como eu. Parabéns :)

Obrigado por atualizar o arquivo com o bug corrigido. No entanto, eu sugeriria substituir o arquivo na área de download.

Felizmente, eu li a discussão sobre o tópico e descobri que há um erro no arquivo. E agora que estava tentando procurar a correção, encontrei este link de arquivo aqui.

Espero que haja apenas esses lugares para a correção do bug, nas linhas 490-493, 500, 515-527, com a marcação //*. Se estiver em outro lugar, mencione os números das linhas ou marque //*BugFix ...

Atenciosamente