preview
Rede neural na prática: Funções de ativação

Rede neural na prática: Funções de ativação

MetaTrader 5Aprendizado de máquina |
19 0
Daniel Jose
Daniel Jose

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.

Então aqui o foco será justamente este. Iremos selecionar algumas das funções de ativação, e ver como poderíamos modificar o código visto no artigo anterior para conseguir visualizar graficamente tanto a função de ativação como também a sua derivada.


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.

Muito bem, antes de vermos cada uma das funções que foram selecionadas. Vamos dar uma rápida olhada no código. Como ele passou por algumas pequenas mudanças. E algumas, no meu entender, um tanto quanto interessantes de serem explicadas. Irei colocar ele na íntegra logo abaixo. Assim será bem mais simples de explicar os novos detalhes adicionados. Então não se assustem logo de cara. Este código é muito mais simples do que parece a primeira vista.
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.

Muito bem, então por padrão, o gráfico será plotado usando a equação da sigmoide. Com valores que vão de menos dez até dez positivo. Com uma granulação de um décimo. O que garante que a curva será bem suave. E com um fator de escala no eixo Y igual a nove. Com este parametros sendo aplicados, você poderá ver o seguinte gráfico no MetaTrader 5.

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.

Este procedimento sofreu pequenas mudanças em sua estrutura. Mas todas elas visando tornar a coisa toda, bem mais simples de ser programada e modificada, caso seja necessário mudanças no código no futuro. Mesmo por quem esteja apenas começando na programação. Pois este novo código, foi pensado de forma que você, consiga colocar qualquer equação que desejar sem muitas dificuldades. Para tornar as coisas bem mais agradáveis. Vamos recortar o código e o analisar com mais calma, olhando apenas o fragmento logo abaixo.

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.

Ok, uma vez entendido esta parte mais simples. Podemos passar para os próximos tópicos. Onde veremos cada uma das equações que estarão sendo implementadas por padrão.


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.

Aqui estamos seguindo as mesmas regras explicadas no artigo anterior. Então se você olhar o código na linha sessenta e três, verá qual é a equação usada. Esta mesma equação, é mostrada em sua notação matemática logo abaixo.

Já a sua derivada, que está sendo definida na linha sessenta e quatro, tem sua equação mostrada 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.

No entanto, apesar de ter dito tais coisas neste momento. Por hora, você deve apenas observar e entender o que acontece, a cada valor colocado na equação e seu resultado quando usado na derivada. Muito bem, com isto já temos alguma coisa para pensar sobre a sigmoide. Podemos passar para a próxima função na lista.


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.

Apesar de muitas redes perceptron, serem construídas para apenas promover determinados conhecimentos. Algumas poucas, vão recebendo novos dados ao longo do tempo. E quando isto acontece, pode ocorrer de uma sigmoide, acabar prejudicando algum peso específico dentro da rede. E se o peso afetado, tiver algum nível de relevância para os novos dados que estão entrando. Parte do aprendizado pode se tornar um tanto quanto mais difícil de ser conseguido.

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.

E como não poderíamos deixar de mostrar. Também existe a forma trigonométrica que é vista abaixo.

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.

A equação que se encontra presente na linha setenta e dois, pode ser vista na expressão abaixo.

Assim como a derivada que é vista na linha setenta e três. Cuja expressão é vista logo abaixo.

Porém apesar desta função ter sido mostrada no artigo anterior. Ela está sendo mostrada aqui novamente por um único e simples detalhe: Está com toda a certeza é a função de ativação que contém o maior número de variações. Pessoalmente imagino que ela deva de fato ser a queridinha de todos. Superando até mesmo a sigmoide. Já que a sigmoide não contém tantas variações na sua expressão original. Dando assim origem a diversas outras funções. Que de alguma maneira ou de outra, tem algo da ReLU dentro de sua base de cálculo. Entre toda, que você poderá encontrar, existe uma da qual também é usada em cálculos de engenharia. Por conta disto, ela foi selecionada e será a que veremos no próximo tópico.


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.

Quando você selecionar a eLU, você verá algo parecido com a animação abaixo.

Observe que o gráfico dela é diferente do gráfico da ReLU original. Mas isto se deve a uma pequena variação na forma de calcular os valores. Abaixo, você pode ver a expressão que está na linha setenta e seis do código.

Caramba, que expressão mais feia. Você tem certeza de que é assim que se escreve ? Sim, meu caro leitor. É desta forma que se escreve matematicamente o código visto na linha setenta e seis. Sei que parece algo medonho e assustador. Mas isto é devido a própria forma de se escrever algumas coisas de maneira matemática. Porém antes de qualquer outra coisa, vamos ver a expressão da derivada. Esta se encontra na linha setenta e sete, e tem sua forma matemática sendo representada logo abaixo.

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.

Muito bem, estamos quase terminando. Faltam outras duas equações, que foram selecionadas para aparecer neste artigo. E também fazer parte do código de demonstração. Então vamos para a penúltima equação, que é vista no próximo tópico.


Função de ativação SoftSign

Esta é uma função muito interessante. E é amplamente aplicada em alguns tipos de redes perceptrons. Basicamente você a poderá ver sendo usada em redes de reconhecimento de voz, escrita e até outras onde precisamos de uma rede onde o gradiente descendente não venha a perder funcionalidade de maneira muito rápida. Abaixo você tem a animação desta função sendo mostrada.

O código da linha oitenta, executa exatamente o que é visto na expressão abaixo.

Já a derivada que é vista na linha oitenta e um, tem sua expressão mostrada logo abaixo.

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.

Para finalizar, vamos a última função que foi selecionada. Esta é vista no tópico seguinte.


Função de ativação SoftPlus

Esta função se baseia na ReLU. Sendo também uma variação na forma como a ReLU é calculada e utilizada. Ao selecionar ela na aplicação de demonstração, você verá a animação vista abaixo.

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.

Esta função tem como código o que pode ser visualizado na linha oitenta e quatro, e expressão o que é visto logo abaixo.

Já a derivada, tem como código a linha oitenta e cinco, e a expressão é a que podemos ver abaixo.

Como basicamente esta SoftPlus tem os mesmos objetivos que a ReLU. Você em alguns casos pode experimentar usar ela em uma rede neural profunda. Visto que a variação do gradiente descendente dela será bem mais suave, do que se fizéssemos uso da ReLU na mesma situação. Mas como é sabido: Não existe uma regra para se usar nenhuma função específica. Tudo depende de cada caso específico.


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.

Espero que este artigo tenha lhe despertado a curiosidade e interessa na matemática por debaixo das redes perceptrons. Isto por que apesar de parecer ser algo muito complicado. Uma rede perceptron, ou algum outro tipo de rede neural, nada mais é do que isto: Expressões matemáticas sendo utilizadas de maneira bastante inteligênte por parte dos programadores e desenvolvedores. Não sendo de forma alguma algum tipo de aplicação mágica que consegue entender algo que nenhum outro humano jamais conseguiu entender ou pensar. Então estude com calma o que foi visto aqui, e nos vemos no próximo artigo.









Arquivos anexados |
Anexo.mq5 (6.26 KB)
Caminhe em novos trilhos: Personalize indicadores no MQL5 Caminhe em novos trilhos: Personalize indicadores no MQL5
Vou agora listar todas as possibilidades novas e recursos do novo terminal e linguagem. Elas são várias, e algumas novidades valem a discussão em um artigo separado. Além disso, não há códigos aqui escritos com programação orientada ao objeto, é um tópico muito importante para ser simplesmente mencionado em um contexto como vantagens adicionais para os desenvolvedores. Neste artigo vamos considerar os indicadores, sua estrutura, desenho, tipos e seus detalhes de programação em comparação com o MQL4. Espero que este artigo seja útil tanto para desenvolvedores iniciantes quanto para experientes, talvez alguns deles encontrem algo novo.
Análise de lacunas tempo&lt;rais de preço em MQL5 (Parte II): Criamos um mapa de calor da distribuição de liquidez no tempo Análise de lacunas tempo&lt;rais de preço em MQL5 (Parte II): Criamos um mapa de calor da distribuição de liquidez no tempo
Guia detalhado para criar um indicador de mapa de calor para MetaTrader 5 que visualiza a distribuição temporal do preço na forma de um mapa de calor. O artigo revela a base matemática da análise da densidade temporal, na qual cada nível de preço é colorido do vermelho (tempo mínimo de permanência) ao azul (tempo máximo de permanência).
Está chegando o novo MetaTrader 5 e MQL5 Está chegando o novo MetaTrader 5 e MQL5
Esta é apenas uma breve resenha do MetaTrader 5. Eu não posso descrever todos os novos recursos do sistema por um período tão curto de tempo - os testes começaram em 09.09.2009. Esta é uma data simbólica, e tenho certeza que será um número de sorte. Alguns dias passaram-se desde que eu obtive a versão beta do terminal MetaTrader 5 e MQL5. Eu ainda não consegui testar todos os seus recursos, mas já estou impressionado.
Indicador de sazonalidade por horas, dias da semana e meses Indicador de sazonalidade por horas, dias da semana e meses
O artigo explica como desenvolver uma ferramenta para análise de padrões recorrentes de preços nos mercados financeiros, por dias do mês (1-31), dias da semana (segunda-feira-domingo) ou horas do dia (0-23). O indicador analisa dados históricos, calcula a rentabilidade média para cada período e exibe os resultados na forma de um histograma com previsão. Inclui parâmetros configuráveis: tipo de sazonalidade, quantidade de barras analisadas, exibição em porcentagens ou valores absolutos, cores dos gráficos.