implementação de rede neural “do zero” - lps.usp.br · implementação de rede neural “do...

25
Implementação de rede neural “do zero” Nesta apostila, vamos implementar uma rede neural desde o início, sem usar uma implementação pronta. Os exemplos utilizando as funções de rede neural de OpenCV estão nas apostilas "aprendi- zagem" e "mle_avancada". Vamos utilizar o material de dois sites: 1) Introdutória: http://mattmazur.com/2015/03/17/a-step-by-step-backpropagation-example/ 2) Avançada: http://neuralnetworksanddeeplearning.com/ Para acompanhar esta apostila, devem olhar ao mesmo tempo o material correspondente desses dois sites. 1

Upload: lamduong

Post on 11-Nov-2018

219 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Implementação de rede neural “do zero” - lps.usp.br · Implementação de rede neural “do zero” Nesta apostila, vamos implementar uma rede neural desde o início, sem usar

Implementação de rede neural “do zero” Nesta apostila, vamos implementar uma rede neural desde o início, sem usar uma implementaçãopronta. Os exemplos utilizando as funções de rede neural de OpenCV estão nas apostilas "aprendi-zagem" e "mle_avancada".

Vamos utilizar o material de dois sites:

1) Introdutória: http://mattmazur.com/2015/03/17/a-step-by-step-backpropagation-example/

2) Avançada: http://neuralnetworksanddeeplearning.com/

Para acompanhar esta apostila, devem olhar ao mesmo tempo o material correspondente desses doissites.

1

Page 2: Implementação de rede neural “do zero” - lps.usp.br · Implementação de rede neural “do zero” Nesta apostila, vamos implementar uma rede neural desde o início, sem usar

O site:http://mattmazur.com/2015/03/17/a-step-by-step-backpropagation-example/[haewin/GoodReader/Algoritmos/deep/stepbystep.pdf]

apresenta a simulação numérica de uma rede neural muito simples. Vamos compreender como fun-ciona, fazendo a implementação em C++.

Pesos iniciais da rede [mattmazur.com]

Numa rede neural "normal", cada neurônio tem um bias. No exemplo do site, há um único "bias"para todos os neurônios de uma camada de rede.

Os nós i1 e i2 (input) não fazem nada. Só pegam as entradas e passam para os nós da camada es-condida (h1 e h2).

Os nós h1, h2 (hidden), o1 e o2 (output) calculam a média ponderada das entradas pelos pesos wi,mais o bias. Depois, aplicam a função de ativação sigmóide. Considere o nó h1:

neth1 = w1*i1 + w2*i2 + b1*1outh1 = sigmoid(neth1)

Funções "logistic" e "sigmoid" são a mesma coisa:f(x)= 1 / ( 1 + exp(-x) )

Logistic sigmoid function [extraído da Wikipedia].

2

Page 3: Implementação de rede neural “do zero” - lps.usp.br · Implementação de rede neural “do zero” Nesta apostila, vamos implementar uma rede neural desde o início, sem usar

Outra função de ativação bastante usada é tangente hiperbólico:

A função custo ou função erro é calculada:

C=Etotal=∑ 12(target−output )2

Vamos calcular a saída de rede neural para entrada (0.05, 0.10). A saída desejada para essa entradaé (0.01, 0.99). Vamos também calcular os erros.

net_h1 = w1*i1+w2*i2+b1*1;net_h1 = 0.15*0.05+0.2*0.1+0.35*1 = 0.3775

out_h1 = logistic(net_h1);out_h1 = 0.59327;

out_h2 = 0.596884;

net_o1 = w5*out_h1+w6*out_h2+b2*1;net_o1 = 0.4*0.593+0.45*0.597+0.6*1 = 1.10591;

out_o1 = logistic(net_o1);out_o1 = 0.751365

out_o2 = 0.772928

err_o1 = 0.5 * elev2(target_o1-out_o1);err_o1 = 0.5 * elev2(0.01-0.7513) = 0.274811;

err_o2 = 0.02356

err_total = err_o1+err_o2;err_total = 0.298371

3

Page 4: Implementação de rede neural “do zero” - lps.usp.br · Implementação de rede neural “do zero” Nesta apostila, vamos implementar uma rede neural desde o início, sem usar

//step01.cpp#include <cekeikon.h>

double i1=0.05, i2=0.10;double w1=0.15, w2=0.20, w3=0.25, w4=0.30, b1=0.35;double net_h1, net_h2, out_h1, out_h2;double w5=0.40, w6=0.45, w7=0.50, w8=0.55, b2=0.60;double net_o1, net_o2, out_o1, out_o2;double target_o1=0.01, target_o2=0.99; // saida desejadadouble err_o1,err_o2,err_total;

double logistic(double x) { return 1 / ( 1 + exp(-x) );}

void forward() { net_h1=w1*i1+w2*i2+b1*1; xprint(net_h1); out_h1=logistic(net_h1); xprint(out_h1);

net_h2=w3*i1+w4*i2+b1*1; xprint(net_h2); out_h2=logistic(net_h2); xprint(out_h2);

net_o1=w5*out_h1+w6*out_h2+b2*1; xprint(net_o1); out_o1=logistic(net_o1); xprint(out_o1);

net_o2=w7*out_h1+w8*out_h2+b2*1; xprint(net_o2); out_o2=logistic(net_o2); xprint(out_o2);

err_o1 = 0.5 * elev2(target_o1-out_o1); xprint(err_o1); err_o2 = 0.5 * elev2(target_o2-out_o2); xprint(err_o2); err_total = err_o1+err_o2; xprint(err_total);}

int main() { forward();}

~/haepi/deep/redeneural/stepbystep $ step01net_h1 = 0.3775out_h1 = 0.59327net_h2 = 0.3925out_h2 = 0.596884net_o1 = 1.10591out_o1 = 0.751365net_o2 = 1.22492out_o2 = 0.772928err_o1 = 0.274811err_o2 = 0.02356err_total = 0.298371

Como era de se esperar, a saída bate perfeitamente com a simulação do site.

4

Page 5: Implementação de rede neural “do zero” - lps.usp.br · Implementação de rede neural “do zero” Nesta apostila, vamos implementar uma rede neural desde o início, sem usar

backpropagation [mattmazur.com]

No backpropagation, queremos diminuir o erro ou o custo (diferença entre a saída da rede e a saídadesejada). Para isso, devemos calcular as derivadas parciais da função custo C (ou erro E) em rela-ção a cada peso e cada bias. Depois, movemos "um pouco" cada peso e cada bias no sentido contrá-rio à sua derivada parcial para diminuir o erro. Para especificar "um pouco", utiliza-se a taxa deaprendizagem (learning rate) η. Learning rate é denotada por α no tiny_dnn.

De [http://neuralnetworksanddeeplearning.com/]

Backpropagation:

w k→w' k=wk−η∂C∂w k

bl→b ' l=b l−η∂C∂ bl

O próximo programa faz backpropagation dos neurônios da camada de saída (output).

5

Page 6: Implementação de rede neural “do zero” - lps.usp.br · Implementação de rede neural “do zero” Nesta apostila, vamos implementar uma rede neural desde o início, sem usar

//step02.cpp#include <cekeikon.h>

double i1=0.05, i2=0.10;double w1=0.15, w2=0.20, w3=0.25, w4=0.30, b1=0.35;double net_h1, net_h2, out_h1, out_h2;double w5=0.40, w6=0.45, w7=0.50, w8=0.55, b2=0.60;double net_o1, net_o2, out_o1, out_o2;double target_o1=0.01, target_o2=0.99; // saida desejadadouble err_o1, err_o2, err_total;double w1p, w2p, w3p, w4p, w5p, w6p, w7p, w8p;

double logistic(double x) { return 1 / ( 1 + exp(-x) );}

void forward() { net_h1=w1*i1+w2*i2+b1*1; xprint(net_h1); out_h1=logistic(net_h1); xprint(out_h1);

net_h2=w3*i1+w4*i2+b1*1; xprint(net_h2); out_h2=logistic(net_h2); xprint(out_h2);

net_o1=w5*out_h1+w6*out_h2+b2*1; xprint(net_o1); out_o1=logistic(net_o1); xprint(out_o1);

net_o2=w7*out_h1+w8*out_h2+b2*1; xprint(net_o2); out_o2=logistic(net_o2); xprint(out_o2);

err_o1 = 0.5 * elev2(target_o1-out_o1); xprint(err_o1); err_o2 = 0.5 * elev2(target_o2-out_o2); xprint(err_o2); err_total = err_o1+err_o2; xprint(err_total);}

void backward() { double eta=0.5;

double derr_dw5=-(target_o1-out_o1)*out_o1*(1-out_o1)*out_h1; xprint(derr_dw5); w5p = w5 - eta*derr_dw5; xprint(w5p);

double derr_dw6=-(target_o1-out_o1)*out_o1*(1-out_o1)*out_h2; xprint(derr_dw6); w6p = w6 - eta*derr_dw6; xprint(w6p);

double derr_dw7=-(target_o2-out_o2)*out_o2*(1-out_o2)*out_h1; xprint(derr_dw7); w7p = w7 - eta*derr_dw7; xprint(w7p);

double derr_dw8=-(target_o2-out_o2)*out_o2*(1-out_o2)*out_h2; xprint(derr_dw8); w8p = w8 - eta*derr_dw8; xprint(w8p);}

int main() { forward(); backward();}

Saída:

~/haepi/deep/redeneural/stepbystep $ step02net_h1 = 0.3775out_h1 = 0.59327net_h2 = 0.3925out_h2 = 0.596884net_o1 = 1.10591out_o1 = 0.751365net_o2 = 1.22492out_o2 = 0.772928err_o1 = 0.274811err_o2 = 0.02356err_total = 0.298371

derr_dw5 = 0.082167w5p = 0.358916derr_dw6 = 0.0826676w6p = 0.408666derr_dw7 = -0.0226025w7p = 0.511301derr_dw8 = -0.0227402w8p = 0.56137

Não vamos fazer simulação para a camada escondida, mas os cálculos são semelhantes.

6

Page 7: Implementação de rede neural “do zero” - lps.usp.br · Implementação de rede neural “do zero” Nesta apostila, vamos implementar uma rede neural desde o início, sem usar

Livro online sobre rede neural e deep learning:http://neuralnetworksanddeeplearning.com/[haewin/GoodReader/Algoritmos/deep/neural_network_and_deep_learning.pdf]

traz uma descrição mais detalhada de como funciona uma rede neural. Esse livro traz os exemplosem Python. Vamos traduzir esses programas para C++ e verificar que os resultados são praticamen-te iguais aos das implementações em Python. Isto certificará que aprendemos corretamente os con-ceitos apresentados no livro. Os exemplos abaixo seguirão esse livro.

O programa rede1 abaixo treina rede neural de 3 camadas com número de neurônios (2,2,2) paraque a saída P=(P1, P2) para a entrada X=(0.05, 0.1) fique cada vez mais semelhante à saída idealY=(0.01, 0.99) - é o mesmo exemplo que estávamos tentando implementar antes.

Inicializa pesos w e biases b com função gaussiana de média zero e desvio-padrão um.Repete 40 vezes:

1) Feed-forward.2) Calcula erro.3) Feed-backward.4) Update weights and biases (gradient descent)

Diferentemente do exemplo anterior, aqui cada neurônio tem um bias.Cada aresta tem um weight.Cada neurônio que não seja de entrada possui função de ativação sigmoide na saída.

Quando tem somente uma amostra de treinamento, função custo ou função erro é:C(w ,b)=‖y (x)−a‖2

x = amostra de entrada y = saída ideala = saída gerada pelo algoritmo

∇C(w1, ... ,wK , b1, ... , bL)=( ∂C∂w1

, ... ,∂C∂wK

,∂C∂ b1

... ,∂C∂ bL

,)T

w k→w' k=wk−η∂C∂w k

bl→b ' l=b l−η∂C∂ bl

7

Page 8: Implementação de rede neural “do zero” - lps.usp.br · Implementação de rede neural “do zero” Nesta apostila, vamos implementar uma rede neural desde o início, sem usar

//rede1.cpp//Treina rede para que quando a entrada for X=(0.05, 0.1) //a saida seja Y=(0.01, 0.99)#include <cekeikon.h>

float logistic(float x) { // Funcao logistic return 1 / ( 1 + exp(-x) );}

Mat_<float> logistic(Mat_<float> x) { // Calcula funcao logistic de cada elemento da matriz Mat_<float> y(x.size()); for (unsigned i=0; i<x.total(); i++) y(i)=logistic(x(i)); return y;}

float Dlogistic(float x) { // Derivada da funcao logistic float z=logistic(x); return z*(1-z);}

Mat_<float> Dlogistic(Mat_<float> x) { // Calcula derivada da funcao logistic para cada elemento da matriz Mat_<float> y(x.size()); for (unsigned i=0; i<x.total(); i++) y(i)=Dlogistic(x(i)); return y;}

class RedeNeural { Mat_<int> nNeuronio; vector< Mat_<float> > w; // pesos - um para cada aresta vector< Mat_<float> > b; // bias - um para cada neurônio exceto entrada public: RedeNeural(Mat_<int> _nNeuronio); void train(Mat_<float> x, Mat_<float> y);};

RedeNeural::RedeNeural(Mat_<int> _nNeuronio) { // Cria redeneural com o numero de neuronios em cada camada // especificada pelo vetor _nNeuronio // _nNeuronio deve ter uma linha // e numero de colunas igual a numero de camadas da rede RNG r(7); r.next(); nNeuronio=_nNeuronio.clone(); w.resize(nNeuronio.total()); // nao existe w[0] for (unsigned i=1; i<w.size(); i++) { w[i].create(nNeuronio(i),nNeuronio(i-1)); r.fill(w[i],RNG::NORMAL,0.0,1.0); } b.resize(nNeuronio.total()); // nao existe b[0] for (unsigned i=1; i<b.size(); i++) { b[i].create(nNeuronio(i),1); r.fill(b[i],RNG::NORMAL,0.0,1.0); }}

void RedeNeural::train(Mat_<float> x, Mat_<float> y) { // Treina rede neural para que a saida de x concorde com y vector< Mat_<float> > a(nNeuronio.total()); // valores dos neurônios antes de sigmoide // a[0] = x vector< Mat_<float> > z(nNeuronio.total()); // valores dos neurônios depois de sigmoide // Nao existe z[0] vector< Mat_<float> > delta(nNeuronio.total()); for (unsigned passo=0; passo<40; passo++) { //======== feed forward ================= a[0]=x.clone(); for (unsigned i=1; i<a.size(); i++) { z[i]=w[i]*a[i-1]+b[i]; a[i]=logistic(z[i]); }

//========= calcula erro ======================== int L=a.size()-1; printf("passo=%-5d erro=%9.6f\n",passo,norm(a[L]-y)); //cout << "Erro: " << norm(a[L]-y) << endl; //========== feed backward (cap 2, BP1 e BP2) ======================

8

Page 9: Implementação de rede neural “do zero” - lps.usp.br · Implementação de rede neural “do zero” Nesta apostila, vamos implementar uma rede neural desde o início, sem usar

multiply( a[L]-y, Dlogistic(z[L]), delta[L]); // (BP1) for (unsigned i=L-1; i>0; i--) multiply( w[i+1].t()*delta[i+1], Dlogistic(z[i]), delta[i]); // (BP2) //============== update weights and biases (cap 2, BP3 e BP4) ================== float eta=3.0; for (unsigned i=L; i>0; i--) { w[i] = w[i] - eta * delta[i]*a[i-1].t(); // (BP4) b[i] = b[i] - eta * delta[i]; // (BP3) } }}

int main() { Mat_<float> x = ( Mat_<float>(2,1) << 0.05, 0.1 ); Mat_<float> y = ( Mat_<float>(2,1) << 0.01, 0.99 ); Mat_<int> v = (Mat_<int>(1,3) << 2,2,2); RedeNeural ind(v); ind.train(x,y);}

O vetor de matrizes w armazena os pesos das arestas. O vetor de vetores b armazena os bias dos neurônios.Os elementos w[0] e b[0] não existem (começam a ser usados a partir do índice 1).

O vetor de vetores z armazena os valores de saída dos neurônios, antes de calcular a função de ati-vação sigmóide.O vetor de vetores a armazena os valores de saída dos neurônios, após calcular a função de ativaçãosigmóide. a[0] contém os valores de entrada da rede neural.A função multiply efetua multiplicação de Hadamard (elemento a elemento).

9

Page 10: Implementação de rede neural “do zero” - lps.usp.br · Implementação de rede neural “do zero” Nesta apostila, vamos implementar uma rede neural desde o início, sem usar

Os erros L2 obtidos são:passo=0 erro= 0.686175passo=1 erro= 0.523090passo=2 erro= 0.390845passo=3 erro= 0.311366passo=4 erro= 0.262869...passo=32 erro= 0.075270passo=33 erro= 0.073844passo=34 erro= 0.072485passo=35 erro= 0.071187passo=36 erro= 0.069946passo=37 erro= 0.068759passo=38 erro= 0.067620passo=39 erro= 0.066528

Note que o erro diminui com o número de iterações.

Backpropagation:

Equações do livro:

De [neuralnetworksanddeeplearning.com].

Equações adaptadas para o programa:

δL=(aL− y )⊙σ ' (zL) (BP1)L é a última camada.Multiplicação de Hadamard (elemento a elemento).σ’ é a derivada do sigmoide.

δl=((wl−1)T δl+1)⊙σ ' (zl) (BP2)l é camada interior de rede neural (exceto a última).

wl=w l−ηδ l(al−1)T (BP4)η é a taxa de aprendizagem

bl=bl−ηδl (BP3)

10

Page 11: Implementação de rede neural “do zero” - lps.usp.br · Implementação de rede neural “do zero” Nesta apostila, vamos implementar uma rede neural desde o início, sem usar

Cada amostra de treinamento gera um erro. É possível calcular as derivadas parciais dessa funçãoerro em relação à cada peso e bias. Backpropagation minimiza o erro fazendo pesos e bias andar nosentido contrário às derivadas parciais.

Quando tem duas ou mais amostras de treinamento, é necessário calcular média da função custopara todas as amostras. E calcular as derivadas parciais (em relação à w e b) dessa média da funçãocusto.

C(w ,b)=1

2n∑x

‖y (x )−a‖2

x = amostra de entrada y = saída ideala = saída gerada pelo algoritmon = número de amostras

Dw[l] é a somatória para todas as amostras de treinamento:(Dw)

l=∑(x, y)δ l(al−1)T (BP5)

Db[l] é a somatória para todas as amostras de treinamento:(Db)

l=∑(x , y)δl (BP6)

O programa rede3 abaixo treina rede neural de 3 camadas com número de neurônios (2,2,2) paraque a saída P=(P1, P2) para a entrada X=(0.9, 0.1) seja Y=(0.1, 0.9) e a saída para entrada X=(0.1,0.9) seja Y=(0.9, 0.1).

Repete 200 vezes:Repete para cada uma das duas amostras:1) Feed-forward.2) Calcula erro.3) Feed-backward.4) Update weights and biases (gradient descent), tirando a média dos dois gradientes (para as duasamostras de treinamento).

Para a entrada: 0.9, 0.1, 0.1, 0.9, 0.8, 0.0, 0.2, 0.9

A saída obtida é: [0.096133143, 0.8931331; 0.90120441, 0.10131229; 0.12444679, 0.8717249; 0.85848975, 0.1471826]

11

Page 12: Implementação de rede neural “do zero” - lps.usp.br · Implementação de rede neural “do zero” Nesta apostila, vamos implementar uma rede neural desde o início, sem usar

//rede3.cpp//Treina rede neural de 3 camadas com número de neurônios (2,2,2) //para que a saída para a entrada X=(0.9, 0.1) seja Y=(0.1, 0.9) //e a saída para entrada X=(0.1, 0.9) seja Y=(0.9, 0.1). #include <cekeikon.h>

float logistic(float x) { return 1 / ( 1 + exp(-x) );}

Mat_<float> logistic(Mat_<float> x) { Mat_<float> y(x.size()); for (unsigned i=0; i<x.total(); i++) y(i)=logistic(x(i)); return y;}

float Dlogistic(float x) { float z=logistic(x); return z*(1-z);}

Mat_<float> Dlogistic(Mat_<float> x) { Mat_<float> y(x.size()); for (unsigned i=0; i<x.total(); i++) y(i)=Dlogistic(x(i)); return y;}

class RedeNeural { Mat_<int> nNeuronio; vector< Mat_<float> > w; vector< Mat_<float> > b; public: RedeNeural(Mat_<int> _nNeuronio); void train(Mat_<float> ax, Mat_<float> ay); Mat_<float> predict(Mat_<float> qx);};

RedeNeural::RedeNeural(Mat_<int> _nNeuronio) { RNG r(18); r.next(); nNeuronio=_nNeuronio.clone(); w.resize(nNeuronio.total()); // nao existe w[0] for (unsigned i=1; i<w.size(); i++) { w[i].create(nNeuronio(i),nNeuronio(i-1)); r.fill(w[i],RNG::NORMAL,0.0,1.0); } b.resize(nNeuronio.total()); // nao existe b[0] for (unsigned i=1; i<b.size(); i++) { b[i].create(nNeuronio(i),1); r.fill(b[i],RNG::NORMAL,0.0,1.0); }}

void RedeNeural::train(Mat_<float> ax, Mat_<float> ay) { int L=nNeuronio.total()-1; // Indice da ultima camada da rede if (ax.cols!=nNeuronio(0)) erro("Erro: Dimensao entrada"); if (ay.cols!=nNeuronio(L)) erro("Erro: Dimensao saida"); if (ax.rows!=ay.rows) erro("Erro: Amostras ax != amostras ay"); int N=ax.rows; // Numero de amostras

vector< Mat_<float> > a(nNeuronio.total()); vector< Mat_<float> > z(nNeuronio.total()); vector< Mat_<float> > delta(nNeuronio.total()); vector< Mat_<float> > Dw(nNeuronio.total()); vector< Mat_<float> > Db(nNeuronio.total()); for (unsigned passo=0; passo<200; passo++) { for (unsigned s=0; s<N; s++) { //======== feed forward ================= a[0]=ax.row(s).t(); for (unsigned i=1; i<a.size(); i++) { z[i]=w[i]*a[i-1]+b[i]; a[i]=logistic(z[i]); } //========== feed backward ====================== multiply( a[L]-ay.row(s).t(), Dlogistic(z[L]), delta[L]); // (BP1) for (unsigned i=L-1; i>0; i--) { multiply( Mat_<float>(w[i+1].t()*delta[i+1]), Dlogistic(z[i]), delta[i]); // (BP2) }

12

Page 13: Implementação de rede neural “do zero” - lps.usp.br · Implementação de rede neural “do zero” Nesta apostila, vamos implementar uma rede neural desde o início, sem usar

for (unsigned i=L; i>0; i--) { // (BP5) (BP6) if (s==0) { Dw[i] = delta[i]*a[i-1].t(); Db[i] = delta[i]; } else { Dw[i] += delta[i]*a[i-1].t(); Db[i] += delta[i]; } } }

//============== update weights and biases ================== float eta=3.0; for (unsigned i=L; i>0; i--) { w[i] = w[i] - (eta/N) * Dw[i]; // (BP4) b[i] = b[i] - (eta/N) * Db[i]; // (BP3) } }}

Mat_<float> RedeNeural::predict(Mat_<float> qx) { if (qx.cols!=nNeuronio(0)) erro("Erro: Dimensao entrada"); int L=nNeuronio.total()-1; // Indice da ultima camada da rede int N=qx.rows; // Numero de amostras Mat_<float> qp(N,nNeuronio(L));

vector< Mat_<float> > a(nNeuronio.total()); vector< Mat_<float> > z(nNeuronio.total()); for (unsigned s=0; s<N; s++) { a[0]=qx.row(s).t(); for (unsigned i=1; i<a.size(); i++) { z[i]=w[i]*a[i-1]+b[i]; a[i]=logistic(z[i]); } qp.row(s)=a[L].t(); } return qp;}

int main() { Mat_<float> ax = ( Mat_<float>(2,2) << 0.9, 0.1, 0.1, 0.9 ); Mat_<float> ay = ( Mat_<float>(2,2) << 0.1, 0.9, 0.9, 0.1 );

Mat_<int> v = (Mat_<int>(1,3) << 2,2,2); RedeNeural ind(v); ind.train(ax,ay);

Mat_<float> qx = ( Mat_<float>(4,2) << 0.9, 0.1, 0.1, 0.9, 0.8, 0.0, 0.2, 0.9 ); Mat_<float> qp=ind.predict(qx); cout << qp << endl;}

13

Page 14: Implementação de rede neural “do zero” - lps.usp.br · Implementação de rede neural “do zero” Nesta apostila, vamos implementar uma rede neural desde o início, sem usar

Stochastic gradient descent (SGD):

Algoritmo de backpropagation faz:

w k→w' k=wk−η∂C∂w k

bl→b ' l=b l−η∂C∂ bl

onde o custo C é o erro médio L2 para todas as amostras de treinamento.

Para calcular gradiente ∇C , é necessário calcular o gradiente ∇C x com respeito a cada

amostra de treinamento x e tirar média delas: ∇C=1n(∑x

∇C x ) . Quando a quantidade de

amostras de treinamento é grande, demora muito tempo para calcular e fazer uma única descida degradiente.

No stochastic gradient descent, estima-se o gradiente ∇C calculando ∇Cx para umpequeno número de amostras de treinamento x escolhidos aleatoriamente. Estas amostras detreinamento escolhidos aleatoriamente são chamados de mini-batch.

Um epoch de treinamento é quando usamos todas as amostras de treinamento (divididos em mini-batches) para fazer descida de gradiente.

Exemplo: Suponha que queiramos treinar uma rede neural para reconhecer dígitos manuscritosusando o banco de imagens MNIST que possui 60000 amostras de treinamento. Treinar 30 epochscom mini-batch m=10 significa:1) Dividir aleatoriamente 60000 amostras em 6000 mini-batches com 10 amostras cada.2) Aplicar descida de gradiente estocástico usando cada mini-batch de 10 amostras, até usar todosos 6000 mini-batches.3) Repetir 30 vezes (o número de epochs) os passos 1 e 2.

No site: https://www.quora.com/What-are-the-meanings-of-batch-size-mini-batch-iterations-and-epoch-in-neural-networks há boa explicação dada por Pankaj Malhotra sobre estes conceitos. Copioabaixo:

"Gradient descent is an iterative algorithm which computes the gradient of a function and uses it toupdate the parameters of the function in order to find a maximum or minimum value of thefunction. In case of Neural Networks, the function to be optimized (minimized) is the loss function,and the parameters are the weights and biases in the network.

Number of iterations (n): The number of times the gradient is estimated and the parameters of theneural network are updated using a batch of training instances. The batch size B is the number oftraining instances used in one iteration.

When the total number of training instances (N) is large, a small number of training instances(B<<N) which constitute a mini-batch can be used in one iteration to estimate the gradient of theloss function and update the parameters of the neural network.

It takes n (=N/B) iterations to use the entire training data once. This constitutes an epoch. So, thetotal number of times the parameters get updated is (N/B)*E, where E is the number of epochs.

Three modes of gradient descent:

14

Page 15: Implementação de rede neural “do zero” - lps.usp.br · Implementação de rede neural “do zero” Nesta apostila, vamos implementar uma rede neural desde o início, sem usar

Batch mode: N=B, one epoch is same as one iteration.

Mini-batch mode: 1<B<N, one epoch consists of N/B iterations.

Stochastic mode: B=1, one epoch takes N iterations."

AdaGrad (adaptive gradient algorithm) é um stochastic gradient descent modificado com taxa deaprendizagem η adaptativo.

Também há Adam (adaptive moment estimation) [Kingma 2015].

15

Page 16: Implementação de rede neural “do zero” - lps.usp.br · Implementação de rede neural “do zero” Nesta apostila, vamos implementar uma rede neural desde o início, sem usar

O programa rede7 abaixo treina rede neural de 3 camadas com número de neurônios (nlado*nlado,30 , 10) para reconhecer os dígitos do banco de dados MNIST.

Usa mini-batches de tamanho m=10, P=30 epochs e taxa de aprendizagem η=3. Obtém taxa de erro4.04% usando nlado=12 e erro 4.58% usando nlado=28.

Não inverte as cores do MNIST (fundo=preto letras=branco). Usa bounding-box: MNIST mnist(nlado,false,true);

//rede7.cpp//Classifica MNIST usando mini-batch m=10 e P=30 epochs //Obtem taxa de erro 4.04% usando nlado=12

#include <cekeikon.h>

class RedeNeural { float logistic(float x); // Funcao logistic Mat_<float> logistic(Mat_<float> x); // Calcula funcao logistic de cada elemento da matriz float Dlogistic(float x); // Derivada da funcao logistic Mat_<float> Dlogistic(Mat_<float> x); // Calcula derivada da logistic para cada elemento da matriz void trocaLinhas(Mat_<float>& a, int i, int j); void embaralhaLinhas(Mat_<float>& ax, Mat_<float>& ay, int semente);

Mat_<int> nNeuronio; // Numero de neuronios em cada camada vector< Mat_<float> > w; // weight vector< Mat_<float> > b; // bias public: RedeNeural(Mat_<int> _nNeuronio); // construtor void train(Mat_<float> ax, Mat_<float> ay); // treina Mat_<float> predict(Mat_<float> qx); // faz previsao};

float RedeNeural::logistic(float x) { return 1 / ( 1 + exp(-x) );}

Mat_<float> RedeNeural::logistic(Mat_<float> x) { Mat_<float> y(x.size()); for (unsigned i=0; i<x.total(); i++) y(i)=logistic(x(i)); return y;}

float RedeNeural::Dlogistic(float x) { float z=logistic(x); return z*(1-z);}

Mat_<float> RedeNeural::Dlogistic(Mat_<float> x) { Mat_<float> y(x.size()); for (unsigned i=0; i<x.total(); i++) y(i)=Dlogistic(x(i)); return y;}

void RedeNeural::trocaLinhas(Mat_<float>& a, int i, int j) { for (unsigned c=0; c<a.cols; c++) { swap(a(i,c),a(j,c)); } }

void RedeNeural::embaralhaLinhas(Mat_<float>& ax, Mat_<float>& ay, int semente) { if (ax.rows!=ay.rows) erro("Erro: Amostras ax != amostras ay"); RNG rng(semente); rng.next(); for (unsigned i=0; i<teto(ax.rows,2); i++) { unsigned j=rng.uniform(i+1,ax.rows); trocaLinhas(ax,i,j); trocaLinhas(ay,i,j); } }

RedeNeural::RedeNeural(Mat_<int> _nNeuronio) { RNG r(18); r.next();

16

Page 17: Implementação de rede neural “do zero” - lps.usp.br · Implementação de rede neural “do zero” Nesta apostila, vamos implementar uma rede neural desde o início, sem usar

nNeuronio=_nNeuronio.clone(); w.resize(nNeuronio.total()); // nao existe w[0] for (unsigned i=1; i<w.size(); i++) { w[i].create(nNeuronio(i),nNeuronio(i-1)); r.fill(w[i],RNG::NORMAL,0.0,1.0); } b.resize(nNeuronio.total()); // nao existe b[0] for (unsigned i=1; i<b.size(); i++) { b[i].create(nNeuronio(i),1); r.fill(b[i],RNG::NORMAL,0.0,1.0); }}

void RedeNeural::train(Mat_<float> ax, Mat_<float> ay) { int L=nNeuronio.total(); // Numero de camadas da rede if (ax.cols!=nNeuronio(0)) erro("Erro: Dimensao entrada"); if (ay.cols!=nNeuronio(L-1)) erro("Erro: Dimensao saida"); if (ax.rows!=ay.rows) erro("Erro: Amostras ax != amostras ay"); int N=ax.rows; // Numero de amostras int M=10; // tamanho dos mini-batches int K=N/M; // numero de mini-batches 60000/10=6000 int P=30; // numero de epochs

vector< Mat_<float> > a(L); vector< Mat_<float> > z(L); vector< Mat_<float> > delta(L); vector< Mat_<float> > Dw(L); vector< Mat_<float> > Db(L); for (unsigned p=0; p<P; p++) { // para cada um dos 30 epochs printf("Epoch %d\n",p); embaralhaLinhas(ax,ay,p+7); for (unsigned k=0; k<K; k++) { // para cada um dos 6000 mini-batches for (unsigned m=0; m<M; m++) { // para cada uma das 10 amostras do mini-batch int s=k*M+m; //======== feed forward ================= a[0]=ax.row(s).t(); for (unsigned i=1; i<a.size(); i++) { z[i]=w[i]*a[i-1]+b[i]; a[i]=logistic(z[i]); } //========== feed backward ====================== multiply( a[L-1]-ay.row(s).t(), Dlogistic(z[L-1]), delta[L-1]); for (unsigned i=L-2; i>0; i--) { multiply( Mat_<float>(w[i+1].t()*delta[i+1]), Dlogistic(z[i]), delta[i]); } for (unsigned i=L-1; i>0; i--) { if (m==0) { //epoch 0, sample 0 Dw[i] = delta[i]*a[i-1].t(); Db[i] = delta[i]; } else { Dw[i] += delta[i]*a[i-1].t(); Db[i] += delta[i]; } } } // cada sample //============== update weights and biases (para cada mini-batch) ================== float eta=3.0; for (unsigned i=L-1; i>0; i--) { w[i] = w[i] - (eta/M) * Dw[i]; b[i] = b[i] - (eta/M) * Db[i]; } } // cada um dos 6000 mini-batches } // cada um dos 10 epochs}

Mat_<float> RedeNeural::predict(Mat_<float> qx) { if (qx.cols!=nNeuronio(0)) erro("Erro: Dimensao entrada"); int L=nNeuronio.total(); // Numero de camadas da rede int N=qx.rows; // Numero de amostras Mat_<float> qp(N,nNeuronio(L-1));

vector< Mat_<float> > a(L); vector< Mat_<float> > z(L);

17

Page 18: Implementação de rede neural “do zero” - lps.usp.br · Implementação de rede neural “do zero” Nesta apostila, vamos implementar uma rede neural desde o início, sem usar

for (unsigned s=0; s<N; s++) { a[0]=qx.row(s).t(); for (unsigned i=1; i<a.size(); i++) { z[i]=w[i]*a[i-1]+b[i]; a[i]=logistic(z[i]); } qp.row(s)=a[L-1].t(); } return qp;}

//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<int main(int argc, char** argv) { if (argc!=2) erro("ann nlado");

int nlado; convArg(nlado,argv[1]); if (nlado<2) erro("Erro: nlado fora do intervalo valido");

MNIST mnist(nlado,false,true); mnist.le("c:/haebase/mnist");

// Converte ay com rotulos 0..9 para ay2 com 10 saidas Mat_<float> ay2(mnist.na,10); ay2.setTo(0.0); for (unsigned i=0; i<mnist.ay.rows; i++) { int j=int(mnist.ay(i)); assert(0<=j && j<10); ay2(i,j)=1.0; }

Mat_<int> v = (Mat_<int>(1,3) << nlado*nlado, 30, 10); RedeNeural ind(v); ind.train(mnist.ax,ay2);

Mat_<float> saida=ind.predict(mnist.qx); for (int l=0; l<mnist.nq; l++) { mnist.qp(l)=argMax(saida.row(l)); } printf("Erros=%10.2f\%\n",100.0*mnist.contaErros()/mnist.nq);}

18

Page 19: Implementação de rede neural “do zero” - lps.usp.br · Implementação de rede neural “do zero” Nesta apostila, vamos implementar uma rede neural desde o início, sem usar

O programa rede-ocv abaixo treina a classe rede neural do OpenCV 2.X de 3 camadas com númerode neurônios (nlado*nlado, 30 , 10) para reconhecer os dígitos do banco de dados MNIST.

Obtém taxa de erro 8.65% usando nlado=12. Note que a nossa implementação obtém taxa de errobem menor (4.04%) nas mesmas condições. Podemos concluir que a implementação de rede neuraldo OpenCV não é muito boa.

Não inverte as cores do MNIST (fundo=preto letras=branco). Usa bounding-box.MNIST mnist(nlado,false,true);

//rede-ocv.cpp//Classifica MNIST usando rede neural do opencv 2.X//Obtem taxa de erro 8.65% usando nlado=12 e leva 25s

#include <cekeikon.h>

//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<int main(int argc, char** argv) { if (argc!=2) erro("ann nlado");

int nlado; convArg(nlado,argv[1]); if (nlado<2) erro("Erro: nlado fora do intervalo valido");

MNIST mnist(nlado,false,true); mnist.le("c:/haebase/mnist");

// Converte ay com rotulos 0..9 para ay2 com 10 saidas Mat_<float> ay2(mnist.na,10); ay2.setTo(0.0); for (unsigned i=0; i<mnist.ay.rows; i++) { int j=int(mnist.ay(i)); assert(0<=j && j<10); ay2(i,j)=1.0; }

Mat_<int> v = (Mat_<int>(1,3) << nlado*nlado, 30, 10); CvANN_MLP ind(v); ind.train(mnist.ax,ay2,Mat());

Mat_<float> saida; ind.predict(mnist.qx,saida); for (int l=0; l<mnist.nq; l++) { mnist.qp(l)=argMax(saida.row(l)); } printf("Erros=%10.2f\%\n",100.0*mnist.contaErros()/mnist.nq);}

19

Page 20: Implementação de rede neural “do zero” - lps.usp.br · Implementação de rede neural “do zero” Nesta apostila, vamos implementar uma rede neural desde o início, sem usar

Taxas de erros obtidos. Os dois constantes booleanos indicam inverte_cores / bounding-box.

Erro true/false true/true false/true false/falserede7

nlado=12epochs=10

11.37% 5.91%4.42%

26s4.49%

rede7nlado=10

epochs=304.12%

rede7nlado=12

epochs=30

4.62%68s

4.04%68s

rede7nlado=16

epochs=304.21%

rede7nlado=20

epochs=30

4.13%120s

rede-ocvnlado=12

8.26%22.86s

8.65%25s

rede-ocvnlado=20

8.71%65s

Rede7 é a minha implementação. Usa mini-batches m=10.Rede-ocv usa a implementação de rede neural do opencv 2.X.A minha implementação comete bem menos erros que a implementação do opencv e tem velocida-de semelhante (usando 10 epochs).

Meu programa usando 30 neurônios na camada escondida (rede7) e 30 epochs:nlado 4 6 8 10 12 14 16 20 28erro 6.47% 4.49% 4.17% 4.12% 4.04% 4.50% 4.21% 4.13% 4.58%

tempo(treino+teste)

41s 48s 53s 61s 68s 79s 90s 119s 200s

Meu programa usando 100 neurônios na camada escondida e 30 epochs:nlado 4 6 8 10 12 14 16 20 28erro 2.71% 12.16%

tempo(treino+teste)

?? 700

Observação: Aumentando o número de neurônios da camada escondida de 30 para 100 neurônios, oerro cai para nlado=12 mas aumenta para nlado=28. Provavelmente, temos um "overfitting". Resol-veremos este problema com regularização.

20

Page 21: Implementação de rede neural “do zero” - lps.usp.br · Implementação de rede neural “do zero” Nesta apostila, vamos implementar uma rede neural desde o início, sem usar

Regularização L2 cria penalidade para pesos grandes. A função custo torna-se:

C=Co+λ

2n∑ w2

onde Co é a função custo original (função de erro), λ é o parâmtro de regularização, n é o núme-ro de amostras de treinamento e w são os pesos da redes. Intuitivamente, o efeito de regularização éfazer com que rede com pesos pequenos tenha menor custo. Backpropagation vai tentar minimizaro erro e ao mesmo tempo a soma dos pesos.

Para fazer regularização, o livro faz a seguinte modificação em Python:

network.py (sem regularização): self.weights = [w-(eta/len(mini_batch))*nw for w, nw in zip(self.weights, nabla_w)]

network2.py (com regularização): self.weights = [(1-eta*(lmbda/n))*w-(eta/len(mini_batch))*nw for w, nw in zip(self.weights, nabla_w)]

Faremos a mesma modificação em C++:

rede7.cpp (sem regularização): M é tamanho de mini-batch w[i] = w[i] - (eta/M) * Dw[i];

rede8.cpp (com regularização): M é tamanho de mini-batch w[i] = (1-(eta*lmbda/N))*w[i] - (eta/M) * Dw[i];

lmbda (λ) é parâmetro de regularização.

Meu programa (rede8) usando 100 neurônios na camada escondida e 30 epochs com regularização:Lmbda=5, eta=0.5.

nlado 4 6 8 10 12 14 16 20 28erro 3.53% 2.55%

tempo(treino+teste)

189s 747s

Repare que o erro diminuiu substancialmente para nlado=28 (de 12% para 2,5%). A minha imple-mentação C++ comete erro semelhante ao relatado no livro para implementação em Python.

21

Page 22: Implementação de rede neural “do zero” - lps.usp.br · Implementação de rede neural “do zero” Nesta apostila, vamos implementar uma rede neural desde o início, sem usar

Erros obtidos para diferentes parâmetros com regularização:

nlado=28 eta=0.5 hidden neurons=100 inicialização=G(0,1) rede8lmbda 5 4 2erro 2.55% 2.26% 2.34%

tempo 747s 734s ??

nlado=12 eta=0.5 hidden neurons=100 inicialização=G(0,1) rede8lmbda 5 4 2 1 0.5erro 3.53% 3.13% 2.49% 2.30%

tempo 189s 191s ??

nlado=12 hidden neurons=100 inicialização=G(0,1) lmbda=1 rede8

eta 0.1 0.4 0.5 1.0 1.3 2.0erro 13.89% 2.35% 2.30% 2.04% 2.24% 2.41%

nlado=12 eta=0.5 hidden neurons=100 inicialização de pesos=G(0,1/n) rede9

lmbda 5 4 2 1erro 3.38% 2.82%

tempoEsta inicialização de pesos não diminui o erro.

Com stochastic gradient descent e regularização, chegamos ao erro da ordem de 2%, usando redeneural com 3 camadas (uma camada escondida).

22

Page 23: Implementação de rede neural “do zero” - lps.usp.br · Implementação de rede neural “do zero” Nesta apostila, vamos implementar uma rede neural desde o início, sem usar

//rede8.cpp//Classifica MNIST usando mini-batch m=10 e P=30 epochs //Usa rede neural com 100 neuronios na camada escondida.//Usa regularizacao com lmbda=1//Usando eta=1, chega-se a 2.04% de erro.

#include <cekeikon.h>

class RedeNeural { float logistic(float x); // Funcao logistic Mat_<float> logistic(Mat_<float> x); // Calcula funcao logistic de cada elemento da matriz float Dlogistic(float x); // Derivada da funcao logistic Mat_<float> Dlogistic(Mat_<float> x); // Calcula derivada da funcao logistic para cada elemento damatriz void trocaLinhas(Mat_<float>& a, int i, int j); void embaralhaLinhas(Mat_<float>& ax, Mat_<float>& ay, int semente);

Mat_<int> nNeuronio; // Numero de neuronios em cada camada vector< Mat_<float> > w; // weight vector< Mat_<float> > b; // bias public: RedeNeural(Mat_<int> _nNeuronio); // construtor void train(Mat_<float> ax, Mat_<float> ay); // treina Mat_<float> predict(Mat_<float> qx); // faz previsao};

float RedeNeural::logistic(float x) { return 1 / ( 1 + exp(-x) );}

Mat_<float> RedeNeural::logistic(Mat_<float> x) { Mat_<float> y(x.size()); for (unsigned i=0; i<x.total(); i++) y(i)=logistic(x(i)); return y;}

float RedeNeural::Dlogistic(float x) { float z=logistic(x); return z*(1-z);}

Mat_<float> RedeNeural::Dlogistic(Mat_<float> x) { Mat_<float> y(x.size()); for (unsigned i=0; i<x.total(); i++) y(i)=Dlogistic(x(i)); return y;}

void RedeNeural::trocaLinhas(Mat_<float>& a, int i, int j) { for (unsigned c=0; c<a.cols; c++) { swap(a(i,c),a(j,c)); } }

void RedeNeural::embaralhaLinhas(Mat_<float>& ax, Mat_<float>& ay, int semente) { if (ax.rows!=ay.rows) erro("Erro: Amostras ax != amostras ay"); RNG rng(semente); rng.next(); for (unsigned i=0; i<teto(ax.rows,2); i++) { unsigned j=rng.uniform(i+1,ax.rows); trocaLinhas(ax,i,j); trocaLinhas(ay,i,j); } }

RedeNeural::RedeNeural(Mat_<int> _nNeuronio) { RNG r(18); r.next(); nNeuronio=_nNeuronio.clone(); w.resize(nNeuronio.total()); // nao existe w[0] for (unsigned i=1; i<w.size(); i++) { w[i].create(nNeuronio(i),nNeuronio(i-1)); r.fill(w[i],RNG::NORMAL,0.0,1.0); } b.resize(nNeuronio.total()); // nao existe b[0] for (unsigned i=1; i<b.size(); i++) { b[i].create(nNeuronio(i),1);

23

Page 24: Implementação de rede neural “do zero” - lps.usp.br · Implementação de rede neural “do zero” Nesta apostila, vamos implementar uma rede neural desde o início, sem usar

r.fill(b[i],RNG::NORMAL,0.0,1.0); }}

void RedeNeural::train(Mat_<float> ax, Mat_<float> ay) { int L=nNeuronio.total(); // Numero de camadas da rede if (ax.cols!=nNeuronio(0)) erro("Erro: Dimensao entrada"); if (ay.cols!=nNeuronio(L-1)) erro("Erro: Dimensao saida"); if (ax.rows!=ay.rows) erro("Erro: Amostras ax != amostras ay"); int N=ax.rows; // Numero de amostras int M=10; // tamanho dos mini-batches int K=N/M; // numero de mini-batches 60000/10=6000 int P=30; // numero de epochs //int P=10; // numero de epochs

vector< Mat_<float> > a(L); vector< Mat_<float> > z(L); vector< Mat_<float> > delta(L); vector< Mat_<float> > Dw(L); vector< Mat_<float> > Db(L); for (unsigned p=0; p<P; p++) { // para cada um dos 10 ou 30 epochs printf("Epoch %d\n",p); embaralhaLinhas(ax,ay,p+7); for (unsigned k=0; k<K; k++) { // para cada um dos 6000 mini-batches for (unsigned m=0; m<M; m++) { // para cada uma das 10 amostras do mini-batch int s=k*M+m; //======== feed forward ================= a[0]=ax.row(s).t(); for (unsigned i=1; i<a.size(); i++) { z[i]=w[i]*a[i-1]+b[i]; a[i]=logistic(z[i]); } //========= calcula erro ======================== // cout << "Sample " << s << " erro: " << norm(a[L-1]-ay.row(s).t()) << endl; //========== feed backward ====================== multiply( a[L-1]-ay.row(s).t(), Dlogistic(z[L-1]), delta[L-1]); for (unsigned i=L-2; i>0; i--) { multiply( Mat_<float>(w[i+1].t()*delta[i+1]), Dlogistic(z[i]), delta[i]); } for (unsigned i=L-1; i>0; i--) { if (m==0) { //epoch 0, sample 0 Dw[i] = delta[i]*a[i-1].t(); Db[i] = delta[i]; } else { Dw[i] += delta[i]*a[i-1].t(); Db[i] += delta[i]; } } } // cada sample //============== update weights and biases ================== //float eta=3.0; float eta=1.0; float lmbda=1.0; for (unsigned i=L-1; i>0; i--) { //w[i] = w[i] - (eta/M) * Dw[i]; w[i] = (1-(eta*lmbda/N))*w[i] - (eta/M)*Dw[i]; b[i] = b[i] - (eta/M) * Db[i]; } } // cada um dos 6000 mini-batches } // cada um dos 10 epochs}

Mat_<float> RedeNeural::predict(Mat_<float> qx) { if (qx.cols!=nNeuronio(0)) erro("Erro: Dimensao entrada"); int L=nNeuronio.total(); // Numero de camadas da rede int N=qx.rows; // Numero de amostras Mat_<float> qp(N,nNeuronio(L-1));

vector< Mat_<float> > a(L); vector< Mat_<float> > z(L); for (unsigned s=0; s<N; s++) { a[0]=qx.row(s).t();

24

Page 25: Implementação de rede neural “do zero” - lps.usp.br · Implementação de rede neural “do zero” Nesta apostila, vamos implementar uma rede neural desde o início, sem usar

for (unsigned i=1; i<a.size(); i++) { z[i]=w[i]*a[i-1]+b[i]; a[i]=logistic(z[i]); } qp.row(s)=a[L-1].t(); } return qp;}

//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<int main(int argc, char** argv) { if (argc!=2) erro("ann nlado");

int nlado; convArg(nlado,argv[1]); if (nlado<2) erro("Erro: nlado fora do intervalo valido");

MNIST mnist(nlado,false,true); mnist.le("c:/haebase/mnist");

// Converte ay com rotulos 0..9 para ay2 com 10 saidas Mat_<float> ay2(mnist.na,10); ay2.setTo(0.0); for (unsigned i=0; i<mnist.ay.rows; i++) { int j=int(mnist.ay(i)); assert(0<=j && j<10); ay2(i,j)=1.0; }

Mat_<int> v = (Mat_<int>(1,3) << nlado*nlado, 100, 10); RedeNeural ind(v); ind.train(mnist.ax,ay2);

Mat_<float> saida=ind.predict(mnist.qx); for (int l=0; l<mnist.nq; l++) { mnist.qp(l)=argMax(saida.row(l)); } printf("Erros=%10.2f\%\n",100.0*mnist.contaErros()/mnist.nq);}

25