Rede neural na prática: Funções de ativação
Introdução
No artigo anterior Rede neural na prática: Grafico da Rectifier, foi desenvolvido uma aplicação da qual poderíamos visualizar o gráfico da função de ativação, juntamente com a sua respectiva derivada. Contudo, aquilo que foi mostrado ali é apenas e somente uma base para algo um pouco mais elaborado, e que será o foco principal deste artigo.
Sei que muitos podem achar desnecessário compreender o gráfico de uma função de ativação e também a sua derivada. E que poderíamos partir logo para algo mais aprofundado, deixando este tipo de coisa de lado. Contudo, é muito mais simples entender por que a função sigmoide não pode ser utilizada em redes muito profundas, se você conseguir entender o gráfico da mesma.
Uma nova aplicação
Toda aquela explicação dada no artigo anterior, continua sendo válida. Mas além do que foi visto lá. Aqui veremos o novo código. Assim como também, serão dadas uma breve explicação sobre cada uma das funções que foram selecionadas. E antes de começarmos, quero deixar um pequeno lembrete, a você, meu amigo leitor. Existe uma quantidade enorme de funções de ativação. Algumas são mais populares do que outras. Porém cada uma tem a sua finalidade e um tipo de aplicação especifica. Isto a fim de que tenhamos algum tipo de otimização na velocidade de treinamento. No futuro, iremos ver algumas destas funções sendo utilizadas na prática. De forma que você consiga ver como a simples mudança de escolha faz toda a diferença no tipo de resultado que podemos conseguir no treinamento da rede propriamente dita. De qualquer forma, agora iremos focar apenas a parte realmente divertida do sistema. Onde estaremos usando as funções de forma completamente isolada. Isto para que seja mais simples entender qual seria a melhor escolha em certas situações. De qualquer modo não se preocupe com isto agora. Mesmo que a parte matemática seja bastante densa e complicada. Pensamos em tornar a coisa bem mais simples no final. Quero que você seja realmente capaz de entender o funcionamento de rede neural. Sendo que o objetivo não é o de dizer: Faça isto. Não faça aquilo. Pois este tipo de coisa não agrega em nada no seu conhecimento. Apenas cria uma falsa ilusão de que você entende algo que na verdade você não está conseguindo entender.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. #property indicator_chart_window 004. #property indicator_plots 0 005. //+------------------------------------------------------------------+ 006. #include <Canvas\Canvas.mqh> 007. //+------------------------------------------------------------------+ 008. enum eFnActivate { 009. Sigmoid, 010. Tangh, 011. ReLU, 012. eLU, 013. SoftSign, 014. SoftPlus, 015. }; 016. //+------------------------------------------------------------------+ 017. input eFnActivate user_00 = Sigmoid; //Activation function 018. input double user_01 = 10.0; //Limits 019. input double user_02 = 0.1; //Granulation 020. input double user_03 = 9.0; //Zoom Y axis 021. //+------------------------------------------------------------------+ 022. CCanvas *canvas; 023. //+------------------------------------------------------------------+ 024. struct st_00 025. { 026. int x, 027. y, 028. maxX, 029. maxY; 030. double Position, 031. dx, fx; 032. }global; 033. //+------------------------------------------------------------------+ 034. void PlotTextPosition(void) 035. { 036. uint w, h; 037. string sz0; 038. 039. sz0 = StringFormat("%s Position: [%.1f] << fx: %.8f dx: %.8f >>", EnumToString(user_00), global.Position, global.fx, global.dx); 040. TextGetSize(sz0, w, h); 041. (*canvas).TextOut(global.x - (w / 2), global.maxY + 5, sz0, ColorToARGB(clrBlack)); 042. } 043. //+------------------------------------------------------------------+ 044. void Function_Curve(void) 045. { 046. int x[], y[], d[]; 047. uint p; 048. double fx, dx, cx, step, alpha; 049. 050. ArrayResize(x, (int)(((user_01 * 2) / user_02) + 1)); 051. ArrayResize(y, x.Size()); 052. ArrayResize(d, x.Size()); 053. 054. step = MathMin(global.maxY, global.maxX) / (x.Size() * 1.0) / user_02; 055. 056. cx = -user_01; 057. for (uint c = 0, m = x.Size(); c < m; c++, cx += user_02) 058. { 059. x[c] = global.x + (int)(step * cx); 060. switch (user_00) 061. { 062. case Sigmoid: 063. fx = 1 / (1 + MathExp(-cx)); 064. dx = MathExp(-cx) / MathPow(1 + MathExp(-cx), 2); 065. break; 066. case Tangh: 067. fx = (2 / (1 + MathExp(-2 * cx))) - 1; 068. dx = (4 / MathPow(MathExp(-cx) + MathExp(cx), 2)); 069. break; 070. case ReLU: 071. fx = MathMax(0, cx); 072. dx = (cx <= 0 ? 0 : 1); 073. break; 074. case eLU: 075. alpha = 0.75; 076. fx = (cx <= 0 ? (alpha * (MathExp(cx) - 1)) : cx); 077. dx = (cx <= 0 ? (alpha * MathExp(cx)) : 1); 078. break; 079. case SoftSign: 080. fx = cx / (1 + MathAbs(cx)); 081. dx = cx / MathPow(1 + MathAbs(cx), 2); 082. break; 083. case SoftPlus: 084. fx = MathLog(1 + MathExp(cx)); 085. dx = 1 / (1 + MathExp(-cx)); 086. break; 087. default: 088. fx = dx = 0; 089. break; 090. } 091. if (cx <= global.Position) 092. { 093. p = c; 094. global.fx = fx; 095. global.dx = dx; 096. } 097. y[c] = global.y - (int)(step * fx * user_03); 098. d[c] = global.y - (int)(step * dx * user_03); 099. } 100. 101. (*canvas).PolylineThick(x, y, ColorToARGB(clrIndigo, 255), 3, STYLE_SOLID, LINE_END_ROUND); 102. (*canvas).PolylineThick(x, d, ColorToARGB(clrDarkOrange, 255), 3, STYLE_SOLID, LINE_END_ROUND); 103. 104. (*canvas).FillCircle(x[p], y[p], 5, ColorToARGB(clrForestGreen, 255)); 105. (*canvas).FillCircle(x[p], d[p], 5, ColorToARGB(clrFireBrick, 255)); 106. 107. ArrayFree(d); 108. ArrayFree(y); 109. ArrayFree(x); 110. } 111. //+------------------------------------------------------------------+ 112. void UpdateGraphics(int direct) 113. { 114. double value = global.Position + user_02 * direct; 115. 116. global.Position = MathAbs(value) <= user_01 ? value : global.Position; 117. 118. (*canvas).Erase(ColorToARGB(clrWhite, 255)); 119. 120. (*canvas).LineVertical(global.x, 0, global.maxY, ColorToARGB(clrRoyalBlue, 255)); 121. (*canvas).LineHorizontal(0, global.maxX, global.y, ColorToARGB(clrRoyalBlue, 255)); 122. 123. Function_Curve(); 124. PlotTextPosition(); 125. 126. (*canvas).Update(true); 127. } 128. //+------------------------------------------------------------------+ 129. void Resize(void) 130. { 131. uint w, h; 132. 133. global.maxX = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0); 134. global.maxY = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0); 135. 136. if (canvas != NULL) 137. { 138. (*canvas).Destroy(); 139. delete canvas; 140. } 141. canvas = new CCanvas; 142. (*canvas).CreateBitmapLabel("BL", 0, 0, global.maxX, global.maxY, COLOR_FORMAT_ARGB_NORMALIZE); 143. global.x = global.maxX / 2; 144. global.y = global.maxY / 2; 145. TextGetSize("M", w, h); 146. global.maxY -= (int)(h * 2); 147. } 148. //+------------------------------------------------------------------+ 149. int OnInit() 150. { 151. ChartSetInteger(0, CHART_SHOW_DATE_SCALE, false); 152. ChartSetInteger(0, CHART_SHOW_PRICE_SCALE, false); 153. ZeroMemory(global); 154. canvas = NULL; 155. 156. return INIT_SUCCEEDED; 157. } 158. //+------------------------------------------------------------------+ 159. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 160. { 161. return rates_total; 162. } 163. //+------------------------------------------------------------------+ 164. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 165. { 166. int direct = 0; 167. 168. switch (id) 169. { 170. case CHARTEVENT_CHART_CHANGE: 171. Resize(); 172. break; 173. case CHARTEVENT_KEYDOWN: 174. if (TerminalInfoInteger(TERMINAL_KEYSTATE_LEFT)) direct = -1; 175. if (TerminalInfoInteger(TERMINAL_KEYSTATE_RIGHT)) direct = 1; 176. break; 177. } 178. UpdateGraphics(direct); 179. } 180. //+------------------------------------------------------------------+ 181. void OnDeinit(const int reason) 182. { 183. (*canvas).Destroy(); 184. delete canvas; 185. ChartSetInteger(0, CHART_SHOW_DATE_SCALE, true); 186. ChartSetInteger(0, CHART_SHOW_PRICE_SCALE, true); 187. } 188. //+------------------------------------------------------------------+
Caramba. Este código parece ser bem mais complicado, isto sim. Calma. Vou explicar as coisas para que você consiga entender. Na verdade ele é mais simples do que o código anterior. Primeiramente, foi adicionado uma enumeração, na linha oito. Esta tem como objetivo, permitir que selecionemos a equação desejada. Cada um nos nomes dentro desta enumeração, é na verdade o nome de uma equação.
Todas estas seis equações mostradas aqui, são de fato equações usadas em funções de ativação. Como foi mencionando antes. Existem outras. Muitas delas são pequenas variações das que estão listadas aqui. Se você desejar, pode adicionar elas, sem nenhum problema. Só existe um único cuidado a ser tomado. E este cuidado é em colocar a expressão matemática, no local correto e da forma adequada. Se isto for feito, você poderá plotar qualquer gráfico, de qualquer equação matemática que desejar. E tudo isto de maneira bastante simples e sem grandes complicações.
Beleza, já que agora temos os nomes das equações. A próxima coisa que foi modificada é a forma como o usuário, escolhe a equação a ser plotada. Antes tínhamos que compilar o código a cada nova mudança. Fosse na equação, fosse nos parâmetros a serem utilizados. Mas como mencionei no inicio. A ideia aqui é promover uma melhor interatividade com o usuário. Assim você não precisará ficar compilando o código a todo momento. Para se fazer isto, foram adicionados quatro inputs. Antes tínhamos duas definições sendo feitas. Uma para indicar o limite de valores, e a outra para indicar a granulação da curva a ser gerada. Agora além de manter o acesso aqueles mesmos ajustes, adicionei também um input para que você diga qual a equação a ser plotada. E um input para dizer qual será o fator de zoom a ser dado no gráfico. Isto por que em alguns casos, os valores no eixo Y ficam muito próximos de 1.0. Quando isto acontece, é adequado permitir que o usuário, mude a escala no eixo Y. Desta forma o gráfico sofrerá uma mudança, que permitirá que a curva plotada seja, mais facilmente analisada.

Observe que foram mudadas algumas coisas na apresentação. Na verdade, a mudança foi feita apenas na parte referente ao texto mostrado. Antes apenas mostrávamos a posição referente ao eixo X. Agora além daquele valor, também imprimimos o nome da equação. O valor dela no eixo Y, dependendo do valor no eixo X. Assim como o valor da derivada, na mesma posição no eixo X. E tudo isto foi conseguido, fazendo quase nenhuma mudança no código. Basicamente os valores de fx e dx, que você pode ver na imagem, são colocados ali, por conta da linha trinta e nove no código. Observe que nesta linha estamos acessando a enumeração, a fim de saber o nome da função. Assim como também estamos acessando os valores definidos na linha trinta e um. Estas duas variáveis da linha trinta e um, não existiam antes.
Bem, como a maior parte do código permaneceu intacta. Sendo que as únicas mudanças, foram o surgimento das linhas 151, 152, 185 e 186, cujo objetivo é tornar a janela do gráfico mais limpa quanto for possível. Como isto é algo simples de entender, podemos voltar a nossa atenção ao ponto principal, que é justamente o procedimento presente na linha quarenta e quatro.
043. //+------------------------------------------------------------------+ 044. void Function_Curve(void) 045. { 046. int x[], y[], d[]; 047. uint p; 048. double fx, dx, cx, step, alpha; 049. 050. ArrayResize(x, (int)(((user_01 * 2) / user_02) + 1)); 051. ArrayResize(y, x.Size()); 052. ArrayResize(d, x.Size()); 053. 054. step = MathMin(global.maxY, global.maxX) / (x.Size() * 1.0) / user_02; 055. 056. cx = -user_01; 057. for (uint c = 0, m = x.Size(); c < m; c++, cx += user_02) 058. { 059. x[c] = global.x + (int)(step * cx); 060. switch (user_00) 061. { 062. case Sigmoid: 063. fx = 1 / (1 + MathExp(-cx)); 064. dx = MathExp(-cx) / MathPow(1 + MathExp(-cx), 2); 065. break; 066. case Tangh: 067. fx = (2 / (1 + MathExp(-2 * cx))) - 1; 068. dx = (4 / MathPow(MathExp(-cx) + MathExp(cx), 2)); 069. break; 070. case ReLU: 071. fx = MathMax(0, cx); 072. dx = (cx <= 0 ? 0 : 1); 073. break; 074. case eLU: 075. alpha = 0.75; 076. fx = (cx <= 0 ? (alpha * (MathExp(cx) - 1)) : cx); 077. dx = (cx <= 0 ? (alpha * MathExp(cx)) : 1); 078. break; 079. case SoftSign: 080. fx = cx / (1 + MathAbs(cx)); 081. dx = cx / MathPow(1 + MathAbs(cx), 2); 082. break; 083. case SoftPlus: 084. fx = MathLog(1 + MathExp(cx)); 085. dx = 1 / (1 + MathExp(-cx)); 086. break; 087. default: 088. fx = dx = 0; 089. break; 090. } 091. if (cx <= global.Position) 092. { 093. p = c; 094. global.fx = fx; 095. global.dx = dx; 096. } 097. y[c] = global.y - (int)(step * fx * user_03); 098. d[c] = global.y - (int)(step * dx * user_03); 099. } 100. 101. (*canvas).PolylineThick(x, y, ColorToARGB(clrIndigo, 255), 3, STYLE_SOLID, LINE_END_ROUND); 102. (*canvas).PolylineThick(x, d, ColorToARGB(clrDarkOrange, 255), 3, STYLE_SOLID, LINE_END_ROUND); 103. 104. (*canvas).FillCircle(x[p], y[p], 5, ColorToARGB(clrForestGreen, 255)); 105. (*canvas).FillCircle(x[p], d[p], 5, ColorToARGB(clrFireBrick, 255)); 106. 107. ArrayFree(d); 108. ArrayFree(y); 109. ArrayFree(x); 110. } 111. //+------------------------------------------------------------------+
Se você comparar este código com o visto no artigo anterior. Irá claramente notar que surgiu duas novas variáveis aqui. Estas estão na linha quarenta e oito. E mesmo as variáveis fx e dx, mudaram o seu propósito inicial. Agora estas duas variáveis irão ser usadas para armazenar temporariamente os valores calculados.
Parece complicado, mas se você nunca programou algo antes, preste atenção, pois agora você vai aprender como criar as coisas em poucos passos.
Vamos supor que você, queira adicionar uma nova função a esta aplicação. Isto a fim de conseguir plotar, tanto o resultado da equação, quanto o resultado da derivada. Como você deveria fazer isto ? Bem, a primeira coisa a ser feita, é ir na linha oito, onde temos os nomes das funções, a adicionar o nome da sua nova função. Lembre-se de que você NÃO PODERÁ usar espaço no meio do nome. Se for fazer isto, utilize o caráter Underline ( _ ).
Feito isto, você já poderá selecionar a equação no momento em que for usar o aplicativo compilado dentro do MetaTrader 5. Bem, mas não irá ver nenhum gráfico sendo plotado. Para agora poder plotar o gráfico, você simplesmente deverá vir a este procedimento mostrado no fragmento acima e fazer o seguinte: Na linha sessenta temos um comando switch. Este irá selecionar qual código será executado, com base no que será o valor presente no nome da equação selecionada pelo usuário.
Assim você na linha sessenta e um, depois do abre chave, deverá pressionar ENTER e digitar, case, dar um espaço e logo depois o nome da sua equação, seguido por dois pontos ( : ). Novamente dar ENTER e digitar break seguido de ponto-virgula ( ; ). Com isto você criará uma estrutura muito parecida com a vista no código. Agora vem a parte divertida. Entre os dois pontos ( : ) e a palavra break. Você deverá digitar algo muito parecido com o que é visto em cada uma das estruturas que se encontra entre as linhas sessenta e dois e oitenta e seis. Note que temos fx = a alguma coisa. E dx = a alguma outra coisa. E o que isto significa ? Bem, em fx você deve colocar a equação normal, em dx você deve colocar a derivada da equação usada em fx. E isto é tudo que você precisa fazer. Todo o restante é feito pelo próprio código, ou durante o momento em que você estiver usando-o no MetaTrader 5. Se você ficar não entendeu como lidar como o comando switch, veja este meu outro artigo Do básico ao intermediário: Comando SWITCH. Neste outro artigo, explico em detalhes como trabalhar como o comando switch.
Função de ativação Sigmoide
Esta função foi vista em um outro artigo. Onde mostrei a necessidade de usar uma função de ativação, para que pudéssemos sair de um ponto de estagnação. Veja os artigos anteriores para entender mais a este respeito. Pois bem, quando colocamos no gráfico o indicador, e selecionamos a sigmoide. Poderemos interagir com o gráfico conforme mostrado na animação abaixo.



Agora se você apenas olhar tais expressões matemáticas, talvez não conseguisse notar como esta derivada da sigmoide faz uma grande redução nos valores. Isto de certa forma é um tanto quanto desagradável. Visto que isto traz implicações bastante severas para que possamos usar esta função em uma rede mais profunda. Devemos tomar alguns cuidados ao dizer em que ponto da rede iremos usar a sigmoide. Pois se você a usar de forma indiscriminada. Irá acabar em algum momento não permitindo que sua rede continue a propagar dados. Mas isto será melhor entendido depois, tem quem entende um pouco de matemática e derivadas. Já deve estar de olho do seguinte tópico: REGRA DA CADEIA. Este é o nome do problema. Mas ela só nos afeta de fato, quando estamos usando as derivadas e apenas e somente durante a retro propagação. Mas como foi mencionado, isto será visto em detalhes futuramente.
Mas mesmo tendo conhecimento sobre este problema. A sigmoide é uma função extremamente útil devido ao fato de que ela normaliza os valores de saída do perceptron para que ele fique entre zero e um. Além de ser muito usada quando queremos usar uma modelagem, onde pretendemos prever a probabilidade de algo, baseando-se no valor de saída da rede neural. Visto que o valor de saída fica sempre entre zero e um. O que torna perfeito, converter os dados em porcentagem. Existe uma outra grande vantagem em se usar a sigmoide. Desde que se faça isto com cuidado. Que é o de evitar saltos abruptos no gradiente descendente. Isto se deve ao fato de ela ser uma função diferenciável, que significa que podemos encontrar a inclinação da curva, usando para isto dois pontos quaisquer.
Função de ativação Tangente Hiperbólica
Esta função de ativação, procura resolver um problema da sigmoide. Que é justamente o fato de ela não permitir valores negativos. Além deste problema, em alguns casos o fato da sigmoide se limitar a valores entre zero e um, tem um agravante. Tal fato é notado quando os valores da sigmoide, se aproximam de zero. Isto tem um efeito que muitas das vezes é devastador em uma rede perceptron. Já que praticamente "mata" alguns dos pesos que estão sendo usados. Fazendo com que eles venham a ser completamente ignorados. Quando na verdade, eles poderiam ajudar na classificação de novos dados que estão entrando na rede.
Bem, mas a função tangente hiperbólica, quando selecionada na aplicação, terá a seguinte animação sendo mostrada.

Com relação a equação, existem pequenas variações na formulação. Existe a que é vista no código, na linha sessenta e sete, que é a mesma mostrada abaixo.

Mas também temos uma outra forma de expressar a mesma equação. Esta pode ser vista logo abaixo.

Nesta equação mostrada acima, você logo percebe que podemos usar equações trigonométricas em uma rede perceptron. Diferente do que muitos pensam. Algumas vezes, usar equações trigonométricas, trazem muito mais benefícios do que você consegue imaginar. Já que elas podem ser desenvolvidas usando mecanismos bem conhecidos presentes na trigonometria. Mas este exemplo acima, foi somente uma curiosidade. Uma outra forma de também representar esta mesma expressão é vista logo abaixo.

Apesar destas variações na forma de escrever a equação. A parte responsável pela sua derivada não muda tanta assim. Mas mesmo assim temos algumas alternativas na forma de escrever a derivada. No código, você pode ver a derivada sendo mostrada na linha sessenta e oito. Esta está usando a equação mostrada abaixo.

Mas também podemos ter uma equação como mostrado abaixo, que também permite obter a derivada.


Claro, que todas estas expressões, são apenas um pequeno apanhado de formas de expressar a mesma coisa. Ou seja, você não deve se apegar a formulas ou expressões, que possam ser vistas em algum lugar. Só por que alguém disse que esta seria a melhor alternativa. Como já mencionei: A humanidade não sabe exatamente como construir uma rede perceptron genérica. Tudo que conseguimos entender, é como criar uma rede que se adapta de uma forma mais ou menos adequada para um dado tipo de problema.
Bem, vamos continuar, por que ainda falta coisas a serem mostradas neste artigo.
Função de ativação ReLU
Esta função já foi vista no artigo anterior. Então vamos passar rapidamente por ela aqui. Ao selecionarmos ela terá o seguinte gráfico mostrado, onde podemos interagir com ele.



Função de ativação eLU
Esta é uma de tantas variações que existem na função ReLU. E apareceu no tópico anterior, justamente por conta de que foi desejado mostrar a expressão matemática dela, para que você, que talvez não tenha nunca ouvido falar em variações de uma expressão matemática. Pudesse comparar uma variação, com a expressão original, por assim dizer.



Pelo amor de DEUS, tem que chamar um exorcista para estas duas expressões. Que coisa mais pavorosa. Ok mas, independente do que você possa estar achando, e do quanto você pode estar apavorado olhando tais expressões. Em ambas você pode ver uma coisa em comum, que é uma letra grega. Um ALFA. Este se encontra na linha setenta e cinco do código. Este alfa tem como objetivo mudar a forma da curvatura desta equação. Dependendo do valor colocado nele, você pode levar uma curva mais fechada ou mais aberta. Ou melhor dizendo: O raio do arco que você pode ver no gráfico, pode ficar um pouco mais curto, ou um pouco mais extenso. E isto gera uma ligeira diferença nos resultados de saida do perceptron. Porém, olhando ambas expressões, você pode notar que diferente da ReLU original, esta eLU, não tem o problema com o zero. Já que a própria equação consegue lidar com o valor zero na derivada.
Função de ativação SoftSign



Se você olhar com calma, irá notar que esta SoftSign fica entre o que seria a função da sigmoide e da tangente hiperbólica. Só que neste caso, ela é muito mais adequada do que ambas, em certas situações. Já que dependendo do tipo de coisa que você queira fazer. Ela acaba ajudando o gradiente a procurar um ponto mais baixo de forma um pouco mais eficiente do que a sigmoide ou a tangente hiperbólica. Além disto, por conta da forma como o cálculo é efetuado, ela é em caso de redes mais profunda mais rápida em termos de execução. E isto em alguns casos, é um diferencial durante o treinamento. Redes que usam TensorFlows, fazem uso preferencial desta função, no lugar das sigmoide e da tangente hiperbólica. Justamente por conta deste fator para o treinamento do modelo.
Função de ativação SoftPlus

Você claramente pode notar que ela suaviza bastante a transição entre valores negativos e valores positivos. Seguindo a mesma ideia da ReLU original. Porém diferente da ReLU original e dá eLU vista a pouco. Esta SoftPlus, tem um comportamento bem mais suave e gradual. Ou seja, depois de um dado valor ela passa a ter um deslocamento bem suave entre o zero e o um. Diferente da ReLU, que dispara em direção ao zero ou um, pelo simples fato de estarmos com valores negativos ou nos positivos.


Considerações finais
Confesso que de fato o resultado final conseguido neste artigo, acabou me surpreendendo, devido a maneira como ficou bem mais interativo. Não precisando você, precise fazer grandes mudanças no código de forma a conseguir adicionar novas funções a fim de estudar possíveis modelos matemáticos. Já que tudo que será preciso fazer, é simplesmente adicionar o nome da equação, e depois a equação propriamente dita.
Assim neste artigo, além de ver como diferentes funções de ativação tem diferentes implicações na saida do perceptron. Você também aprendeu que pequenas variações na forma de calcular as coisas, pode afetar imensamente a escolha de qual deve ser a função de ativação. Lembrando que nem todo sistema perceptron de fato faz uso de funções como as que foram mostradas aqui. Mas isto é tema para um outro momento.
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
Análise de lacunas tempo<rais de preço em MQL5 (Parte II): Criamos um mapa de calor da distribuição de liquidez no tempo
Está chegando o novo MetaTrader 5 e MQL5
Indicador de sazonalidade por horas, dias da semana e meses
- 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