You are on page 1of 22

Redes Neurais em Portugus Simples

Artigo original: http://www.ai-junkie.com/ann/evolved/nnt1.html


Autor: Mat Buckland
Traduo: Jerson Seling
Nota: Os captulos 7, 8, 9, 10 e 11 do livro AI Techniques For Game Programming (Premier Press) do
mesmo autor, apresentam este artigo de forma amplamente mais aprofundada. Recomendo que voc
adquira esse livro se ficou interessado pelo tema.

1. Introduo
Eu tenho interesse em inteligncia artificial e vida artificial faz tempo e j li a maioria dos livros
populares impressos sobre o assunto. Eu desenvolvi uma compreenso da maioria dos tpicos, mas ainda
assim, as redes neurais sempre pareciam ser incompreensveis para mim. Certo, eu podia explicar suas
arquiteturas, mas como elas realmente trabalhavam e como eram implementadas... Bem, isso era um
completo mistrio para mim, tanto quanto a mgica para a cincia. Eu comprei vrios livros sobre o
assunto, mas a maioria abordava o assunto de ponto um ponto de vista muito matemtico e acadmico e
muitos poucos mostravam usos prticos ou exemplos. Por muito tempo eu arranhei minha cabea e
esperei pelo dia em que teria compreenso o bastante para experiment-las eu mesmo.
Esse dia chegou um tempo mais tarde quando - sentado em uma tenda nos planaltos da Esccia,
lendo um livro - eu tive um insight repentino. Esse foi um daqueles fantsticos momentos de heureca e
embora a Esccia seja um belo lugar, eu no podia esperar para pegar um computador e experimentar o
que eu tinha aprendido. Para minha surpresa a primeira rede do neural que programei funcionou
perfeitamente e eu no tive mais problemas com elas. Eu ainda tenho uma grande vontade de aprender,
redes neurais um assunto enorme, mas eu espero que possa compartilhar conhecimento e entusiasmo o
bastante para voc comear a us-las em seus pequenos projetos. De muitas maneiras os campos da IA e
A-Life so muito excitantes de trabalhar. Eu penso nestes assuntos como os exploradores da antiguidade
deviam olhar todos aqueles vastos espaos vazios nos mapas. H tanto para aprender e descobrir.

Antes de voc comear este assunto, por favor, garanta que voc entende como usar algoritmos
genticos tambm. Isso necessrio, pois sero usados algoritmos genticos para desenvolver os pesos
da rede neural no cdigo do projeto no fim deste tutorial. Leia e entenda o contedo do captulo
anterior para isso.

Eu comearei descrevendo o que realmente uma rede do neural e que a sua arquitetura, ento
eu explicarei um pouco da teoria sobre como ns usamos elas, mas tentarei usar o menos de matemtica
possvel. (Ter que compreender algo de matemtica impossvel de se evitar, alm disso, quanto mais
fundo voc entrar neste tpico, mais matemtica voc vai ter que aprender). Finalmente, ns nos
divertiremos um pouco. Eu mostrarei um pequeno projeto e explicarei sua programao etapa por etapa.
Essa ser a ltima fase deste tutorial, onde eu espero que voc tenha o mesmo sentimento de "heureca"
para redes neurais como eu tive na velha Esccia chuvosa. At ento, somente fique sentado, absorva e
seja paciente.

O fonte C++ para o tutorial e um executvel pr-compilado podem ser encontrados em:
http://www.ai-junkie.com/files/smart_sweepers.zip
Um leitor, Sam Corder, converteu o cdigo para VB NET. Voc pode baixar esse fonte e executvel em:
http://www.ai-junkie.com/files/AntsVBdotNet.zip
Um leitor, Chris Reitzel, converteu o cdigo para DELPHI. Voc pode baixar esse fonte e executvel em:
http://www.ai-junkie.com/files/Smart_Sweepers_Delphi.zip

2. Ento, o que exatamente uma Rede Neural?


Uma rede neural uma maneira primitiva do homem tentar simular o crebro eletronicamente.
Assim, para entender como uma rede neural funciona, primeiro devemos dar uma olhada em como a
velha massa cinzenta faz o que tem que fazer...
Nossos crebros so feitos de aproximadamente 100 bilhes de pequeninas unidades chamadas
neurnios. Cada neurnio conectado a milhares de outros neurnios e se comunica com eles via sinais
eletroqumicos. Os sinais que entram nos neurnios so recebidos via junes chamadas sinapses, essas
por sua vez so localizadas no fim de ramos da clula do neurnio chamados dendritos. O neurnio
continuamente recebe sinais destas entradas e ento desempenha uma pequena mgica. O que o neurnio
faz (isso o mais simplificado que eu posso dizer) somar as entradas de alguma maneira e ento, se o
resultado final maior que algum valor de baliza, o neurnio dispara um sinal. Ele gera uma voltagem e
emite um sinal ao longo de uma coisa chamada axnio. No se preocupe demais em lembras todas essas
palavras novas, pois no as usaremos muitas delas deste momento em diante, apenas d uma boa olhada
na ilustrao e tente imaginar o que acontece dentro desta simples e pequena clula.

Obrigado a quem desenhou esta imagem. Espero no ofend-lo ao us-la.

Redes neurais so feitas de muitos neurnios artificiais. Um neurnio artificial simplesmente um


modelo eletrnico do neurnio biolgico. Quantos neurnios so utilizados depende da tarefa para qual a
rede servir. Esses podem ser to pouco quanto trs ou muitos como vrias dezenas de milhares. Um
pesquisador otimista conectou mais de 2 milhes de neurnios na esperana dessa rede poder a vir a se
tornar alguma coisa to inteligente quanto um gato, embora a maioria das pessoas da comunidade de IA
duvide que ele tenha sucesso. (Atualizao: ele no teve!). H muitas maneiras diferentes de se conectar
os neurnios artificiais para se criar uma rede neural, mas eu me concentrarei na mais comum que uma
rede chamada de feedforward. Assim, eu aposto que voc est se perguntando, como a aparncia de um
neurnio artificial? Bem, aqui vai para voc:

Cada entrada no neurnio possui um peso associado a ela como ilustrado pelos crculos vermelhos
com um w (de weight, peso). Um peso simplesmente um nmero de ponto flutuante que ajustado
quando a rede treinada. Os pesos na maioria das redes neurais podem ser tanto negativos quanto
positivos, portanto eles influenciam para excitar ou inibir cada entrada. Quando cada entrada entra no
ncleo (crculo azul) ela multiplicada por seu peso. O ncleo ento soma esses novos valores de entrada
para que eles faam a ativao (outra vez um nmero de ponto flutuante que pode ser negativo ou
positivo). Se a ativao maior que um valor baliza vamos usar o nmero 1 como exemplo o
neurnio emite um sinal de sada. Se a ativao menor que 1, o neurnio emite zero. Isso tipicamente
chamado de funo de step (degrau) (d uma espiada no seguinte diagrama e adivinhe o porqu).

3. Agora alguma matemtica


Eu agora vou explicar a voc algumas equaes. Tentarei manter a matemtica no mnimo
possvel, mas ser til para voc aprender algumas notaes. Eu introduzirei a matemtica pouco a pouco
e mostrarei os conceitos novos nas sees relevantes. Dessa maneira eu espero que sua mente possa
absorver todas as idias um pouco mais confortavelmente e voc estar apto a ver como a matemtica
entra em cada estgio do desenvolvimento de uma rede neural.
Um neurnio pode ter qualquer nmero de entradas de um at n,, onde n o nmero total de
entradas. As entradas podem ser representadas como x1, x2, x3 xn. E os pesos correspondentes dessas
entradas como w1, w2, w3 wn. Agora, a somatria
soma
dos pesos multiplicados pelas entradas que falamos
acima pode ser escrita como x1w1 + x2w2 + x3w3 + xnwn, a qual eu espero que voc lembre que o valor
de ativao. Assim...

a = x1w1+x2w2+x3w3... +xnwn
Felizmente a uma maneira mais rpida de escrever isso usando a letra maiscula grega sigma , a
qual o smbolo que os matemticos usam para representar a somatria.

Para esclarecer mais o que isso significa eu poderia escrever em cdigo. Assumindo que
q h um
array de entradas e pesos j inicializados como x[n] e w[n], ento:
double activation = 0;
for (int i=0; i<n; i++)
{
activation += x[i] * w[i];
}

Entendeu? Agora lembre que se a ativao > baliza, produzimos uma sada 1 e se a ativao <
baliza, emitimos 0.
Deixe-me
me ilustrar tudo que vimos at aqui com um diagrama.

Por favor, garanta que voc sabe exatamente como se calcula o valor de ativao antes de
continuar.

4. Eu entendi tudo at aqui, mas como se faz para se usar um


neurnio artificial?
Bem, temos que ligar vrios desses neurnios de alguma maneira. Uma das maneiras fazer isso
pela organizao dos neurnios em um padro chamado rede feedforward (a traduo literal
alimentao para frente, mas os autores portugueses a chamam de rede em camadas). Ele tem esse
nome por causa da maneira como as camadas passam suas sadas para diante, para a prxima camada at
chegar a sada final da rede neural. Isto o que uma rede feedforward se parece:

Cada entrada enviada para todos os neurnios da camada oculta (hidden layer) e ento cada
neurnio da camada oculta fica conectado a todos os neurnios da prxima camada. Pode haver inmeras
camadas ocultas dentro de uma rede feedforward, mas uma em geral o bastante para a maioria dos
problemas com que voc ir se deparar. Tambm pode haver qualquer nmero de neurnios em cada
camada, isso depende totalmente do problema. Agora voc pode estar se sentindo um pouco tonto com
toda essa informao, ento e acho que a melhor coisa a fazer neste ponto dar a voc um exemplo do
mundo real no qual uma rede neural pode ser usada, na esperana que eu possa fazer os seus prprios
neurnios dispararem!
Voc pode j saber que um uso popular das redes neurais o reconhecimento de caracteres. Assim
vamos projetar uma rede neural que detectar o nmero 4. Dados um painel feito de uma grade de luzes
que podem ser ligadas ou desligadas, queremos que nossa rede neural deixe-nos saber se ela acha que v
o caractere 4. O painel um quadrado de oito por oito clulas como este:

Ns poderamos projetar uma rede neural que aceitasse o estado do painel como uma entrada e
emitisse uma sada de 1 ou zero. Um 1 indica que ela acha que o caractere 4 est sendo mostrado e 0 se
ela acha que ele no est sendo mostrado. Assim a rede neural ter 64 entradas, cada uma representando
uma clula em particular no painel e uma camada oculta com um nmero de neurnios (mais desta vez)
todos emitindo sua sada para apenas um neurnio na camada de sada. Eu espero que voc possa
imaginar essa imagem na sua cabea, pois no deu para desenhar todos esses crculos e linhas para voc.
Uma vez que a rede neural tenha sido criada ela precisa ser treinada. Uma maneira de se fazer isso
inicializar a rede neural com pesos aleatrios e ento aliment-la com uma srie de entradas que
representaro, neste exemplo, as diferentes configuraes do painel. Para cada configurao fazemos um
teste para ver qual a sada e ajustamos os pesos de acordo a ela, assim se o que se v parece um nmero
4 a sada deve ser 1, para o resto a sada deve ser zero. Este tipo de treinamento chamado de
aprendizado supervisionado (supervised learning) e os dados que alimentamos a rede so chamados de
conjunto de treinamento (training set). H muitas maneiras diferentes de se ajustar os pesos, a mais
comum para este tipo de problema chamada de propagao reversa (backpropagation). Eu no irei me
aprofundar mais neste tutorial, pois mostrarei a voc uma maneira completamente diferente de treinar
redes neurais sem nenhuma superviso (e dificilmente alguma matemtica ufa!).
Se voc pensou isso, voc pode aumentar as sadas desta rede neural para 10. Dessa maneira a
rede pode ser treinada para reconhecer todos os dgitos de 0 at 9. Aumentando mais e mais, ela pode ser
treinada para reconhecer o alfabeto tambm!
Voc est comeando a compreender as redes neurais agora? Eu espero que sim. Mas mesmo que
voc no tenha entendido tudo completamente, j a hora de voc comear a ver algum cdigo.

5. Ento, qual ser o nosso projeto?


Ns iremos desenvolver um varredor de minas virtual para encontrar e coletar minas terrestres
espalhadas sobre um simples mundo 2D. (Na verso original deste tutorial o programa era de formigas
que coletavam comida, mas eu imaginei uma mudana.).
Esta uma tela da aplicao:

Como voc pode ver um mostrador muito simples. Os varredores de minas so as coisas que
parecem tanques e as minas terrestres so representadas pelos pontos verdes. Assim que um varredor de
mina encontra uma mina, ela removida e outra mina posicionada aleatriamente em algum lugar do
mundo, dessa forma garantindo que sempre haver uma quantia constante de minas terrestres no
mostrador. Os varredores de minas desenhados em vermelho so melhores varredores que o programa
desenvolveu at o momento.
Mas como que uma rede neural ir controlar o movimento dos varredores? Bom, da mesma
maneira que em tanque de verdade, os varredores so controlados pelo ajuste da velocidade de uma
esteira esquerda e uma esteira direita. Ao se aplicar vrias foras no lado esquerdo e no direito de um
varredor, podemos dar a ele uma capacidade completa de movimento. Assim a rede neural precisa de
duas sadas, uma para designar a velocidade da esteira esquerda e outra para designar a velocidade da
esteira direita.
O pensamento mais inquietante que voc pode ter agora como que raios poderemos aplicar
foras diversas quando tudo que discutimos at aqui foram redes binrias que produziam sadas de 1s e
0s. O segredo para isso que em vez de usarmos uma simples funo de ativao step (baliza), ns
usaremos uma que suaviza a sada de cada neurnio para produzir uma curva simtrica. H vrias funes
que fazem isso, ns usaremos uma chamada funo sigmoid. (sigmoid ou sigmoidal apenas uma
maneira elegante de dizer que algo tem uma forma de S).

Essa equao pode parecer intimidadora para alguns, mas ela simples na realidade. O e uma
constante matemtica de aproximadamente 2.7183, o a a ativao no neurnio e o p um nmero que
controla a forma da curva. O p em geral ajustado como 1.0.
Essa funo extraordinria e til para toda sorte de diferentes usos, pois ela produz uma sada
como esta:

Quanto mais baixo o valor de p, mais a curva se assemelha a funo step.. Note tambm como essa
curva sempre centrada em 0.5. Os valores de ativao negativos produziro um resultado menor que 0.5
e valores de ativao positivos produziro um resultado maior que 0.5.
Assim, para obter uma sada continuamente nivelada entre 0 e 1 nos
os nossos neurnios,
neurnios temos que
colocar a soma das entradas x pesos na funo sigmoidal e pronto. Agora que as sadas j foram tratadas,
como sero as entradas?
Eu escolhi ter quatro entradas. Duas delas representando um vetor que aponta para a mina mais
prxima e outras duass representando a direo para onde o varredor est apontando. Esse vetor representa
a frente do varredor. Essas quatro entradas do ao crebro do varredor sua rede neural tudo que ele
precisa saber para se orientar na direo das minas.
Esta no a hora e nem o lugar para explicar o que so vetores, assim se voc no os entende, eu
sugiro que pare e os estude antes de continuar. Leia o Captulo 1 para isso.
os nossas entradas e sadas, como ser(o) a(s) camada(s) oculta(s)? Como
Agora de definimos
fazemos para definir quantas camadas teremos e quantos neurnios haver em cada camada? Bom, isso
uma matria de apostas e voc ter que desenvolver um tino

para isso. No h nenhuma


ne
regra que os
pesquisadores tenham encontrado. Pelo padro umaa simulao usa uma camada oculta que contm seis
neurnios,, embora seja bom gastar um tempinho experimentando diferentes nmeros para ver o efeito. Eu
gosto de enfatizar aqui que quanto mais voc mexer em todos os parmetros, melhor ficar o seu tino e
com isso melhor sero as suas redes neurais.
Agora a hora de ver algum cdigo. Aqui est um resumo das classes mais importantes.

CNeuralNet a classe da rede neural (surpresa, surpresa);


CGenAlg a classe do algoritmo gentico;
CMineSweeper a classe dos dados e controladora de cada varredor;
CController a classe que gerencia todas as outras classes;
CParams uma classe que carrega todos os parmetros para a aplicao. Eles podem ser encontrados no
arquivo params.ini. Eu sugiro fortemente que voc mexa nas configuraes deste arquivo quando
comear a tratar do cdigo.

6. A classe CNeuralNet
Vamos iniciar com a classe da rede neural, CNeuralNet. Queremos que essa classe seja flexvel
para que possa ser usada em outros projetos e seja o mais simples possvel. Precisamos que ela esteja
estar apta a conter uma rede neural com qualquer quantidade de entradas e sadas, bem como qualquer
quantidade de neurnios em qualquer quantia de camadas ocultas. Assim sendo, como fazer isso? Bom,
primeiro precisamos definir as estruturas de um neurnio e de uma camada de neurnios. Vamos dar uma
olhada na definio dessas estruturas... primeiro, o neurnio:
struct SNeuron
{
//o nmbero de entradas em um neurnio
int m_NumInputs;
//os pesos de cada entrada
vector<double> m_vecWeight;
//construtor
SNeuron(int NumInputs);
};

Isso muito simples, apenas precisamos armazenar quantas entradas cada neurnio tem e um
std::vector de doubles que armazenar o peso de todas elas. Lembre, h um peso para cada entrada em
um neurnio. Quando um objeto SNeuron criado, todos os pesos so inicializados com valores
aleatrios.

Nota de programao
O std::vector parte da STL e uma classe feita para manipular arrays dinmicos. Um vetor
criado como visto anteriormente. Os elementos so adicionados pelo uso do mtodo push_back().
Assim:
#include<vector>
std::vector<int> MeuPrimeiroVetor;
for (int i=0; i< 10; i++)
{
MeuPrimeiroVetor.push_back(i);
Cout << endl << MeuPrimeiroVetor [i];
}

Para esvaziar um vetor, apenas use o mtodo: MeuPrimeiroVetor.clear();


Podemos saber o nmero de elementos em um vetor com: MeuPrimeiroVetor.size();
isso a! No necessrio se preocupar com gerenciamento de memria, o std::vector faz
tudo isso para voc. Eu usarei ele no programa quando necessrio.

Este o construtor do SNeuron.


SNeuron::SNeuron(int NumInputs): m_NumInputs(NumInputs+1)
{
//we need an additional weight for the bias hence the +1
for (int i=0; i<NumInputs+1; ++i)
{
//set up the weights with an initial random value
m_vecWeight.push_back(RandomClamped());
}
}

Ele toma o nmero de entradas que entraro no neurnio como argumento e ento cria um vetor
de pesos aleatrios. Um peso para cada entrada.
O que foi que eu ouvi voc dizer? H um peso extra a! Bom, estou feliz que voc tenha percebido
isso, pois esse peso extra muito importante, mas para explic-lo eu terei de usar mais um pouco de
matemtica. Lembre que a ativao era a soma de todas as entradas x pesos e a sada do neurnio
dependia se essa ativao excedia ou no um valor baliza (t)? E isso podia ser representado em equao
na forma de:

x1w1 + x2w2 + x3w3 + xnwn >= t


Durante o treinamento da rede, os pesos sero alterados, mas tambm pode ser necessrio se
alterar o valor da baliza. Para facilitar isso, eu usarei um pequeno truque que far a baliza parecer um
peso. Tudo que voc tem que fazer subtrair o t do outro lado da equao e teremos:

x1w1 + x2w2 + x3w3 + xnwn t >= 0


Ou podemos escrever isso de outra maneira:

x1w1 + x2w2 + x3w3 + xnwn + (-1)t >= 0


Assim voc pode ver (espero eu) que podemos tratar a baliza como um peso que sempre
multiplicado por uma entrada de -1. Isso em geral chamado de vis (bias).
E por isso que cada neurnio inicializado com um peso adicional. Pois agora quando a rede for
ser treinada, no teremos de nos preocupar com o valor de baliza, j que ele est incluso com os pesos e
tomar conta de si mesmo. Bom no ?
Vamos ver o resto do cdigo da rede neural... A prxima estrutura define uma camada (layer) de
neurnios.

struct SNeuronLayer
{
//the number of neurons in this layer
int m_NumNeurons;
//the layer of neurons
vector<SNeuron> m_vecNeurons;
SNeuronLayer(int NumNeurons, int NumInputsPerNeuron);
};

Como voc pode ver, isto apenas agrupa um monte de neurnios em uma camada. A classe
CNeuralNet muito mais interessante, assim vamos seguir e ver sua definio:
class CNeuralNet
{
private:
int m_NumInputs;
int m_NumOutputs;
int m_NumHiddenLayers;
int m_NeuronsPerHiddenLyr;
//storage for each layer of neurons including the output layer
vector<SNeuronLayer> m_vecLayers;
public:
CNeuralNet();
//have a guess... ;0)
void CreateNet();
//gets the weights from the NN
vector<double> GetWeights()const;
//returns the total number of weights in the net
int GetNumberOfWeights()const;
//replaces the weights with new ones
void PutWeights(vector<double> &weights);
//calculates the outputs from a set of inputs
vector<double> Update(vector<double> &inputs);
//sigmoid response curve
inline double Sigmoid(double activation, double response);
};

A maioria disso auto-explicativa. O trabalho principal feito pelo mtodo Update. Nele
passamos nossas entradas para a rede neural como um std::vector de doubles e obtemos a sada como
outro std::vector de doubles. Este na verdade o nico mtodo que usaremos depois da classe
CNeuralNetwork ser inicializada. Apenas podemos trat-la como uma caixa preta, alimentando-a com
dados e obtendo a sada como por mgica. Vamos dar uma olhada mais de perto nesse mtodo:
vector<double> CNeuralNet::Update(vector<double> &inputs)
{
//stores the resultant outputs from each layer
vector<double> outputs;
int cWeight = 0;
//first check that we have the correct amount of inputs
if (inputs.size() != m_NumInputs)
{
//just return an empty vector if incorrect.
return outputs;
}
//For each layer....
for (int i=0; i<m_NumHiddenLayers + 1; ++i)
{
if ( i > 0 )
{
inputs = outputs;
}
outputs.clear();
cWeight = 0;
//for each neuron sum the (inputs * corresponding weights).Throw
//the total at our sigmoid function to get the output.
for (int j=0; j<m_vecLayers[i].m_NumNeurons; ++j)
{
double netinput = 0;
int NumInputs = m_vecLayers[i].m_vecNeurons[j].m_NumInputs;
//for each weight
for (int k=0; k<NumInputs - 1; ++k)
{
//sum the weights x inputs
netinput += m_vecLayers[i].m_vecNeurons[j].m_vecWeight[k] *
inputs[cWeight++];
}

//add in the bias


netinput += m_vecLayers[i].m_vecNeurons[j].m_vecWeight[NumInputs-1] *
CParams::dBias;
//we can store the outputs from each layer as we generate them.
//The combined activation is first filtered through the sigmoid
//function
outputs.push_back(Sigmoid(netinput, CParams::dActivationResponse));
cWeight = 0;
}
}
return outputs;
}

Depois deste mtodo checar a validade do vetor de entrada, ele entra em um loop que examina
uma camada por vez. Para cada camada, ele passa atravs dos neurnios dela e soma todas as entradas
multiplicadas pelos pesos correspondentes. O ltimo peso adicionado em cada neurnio o vis (lembre
que o vis simplesmente um peso que sempre multiplicado por -1.0). Esse valor ento colocado na
funo sigmoidal para resultar na sada desse neurnio e ento esta adicionada a um vetor que ir
alimentar a prxima iterao do loop e assim por diante at se chegue na sada da rede.
Os outros mtodos na CNeuralNet so usados principalmente pela classe do algoritmo gentico
para obter ou trocar os pesos de uma rede.

7. A classe CGenAlg
Esta a classe do algoritmo gentico. Se voc seguiu o captulo anterior, voc entender bem
como ela funciona. H, entretanto, uma diferena com a classe CGenAlg desta vez, pois usaremos vetores
de nmeros reais em vez de sequncias de binrios.
A rede neural codificada ao se ler todos os pesos da esquerda para direita e a partir da primeira
camada oculta, debaixo para cima, se armazenando todos esses pesos em um vetor. Ento se um rede se
parece a isto:

O vetor pode ser: 0.3, -0.8, -0.2, 0.6, 0.1, -0.1, 0.4, 0.5. (Note que no estamos levando em conta
o vis, apenas os pesos como mostrado).
Agora podemos usar o cruzamento e a mutao normalmente, apenas com uma diferena: a taxa
de mutao para algoritmos genticos que usam nmeros reais muito maior... um valor entre 0,05 e 0,2
o recomendado.
Antes de eu mostrar a voc a definio da classe CGenAlg, deixe-me rapidamente mostrar a voc a
estrutura do genoma:

struct SGenome
{
vector <double>

vecWeights;

double

dFitness;

SGenome():dFitness(0){}
SGenome( vector <double> w, double f): vecWeights(w), dFitness(f){}
//overload '<' used for sorting
friend bool operator<(const SGenome& lhs, const SGenome& rhs)
{
return (lhs.dFitness < rhs.dFitness);
}
};

E agora a classe CGenAlg:


class CGenAlg
{
private:
//this holds the entire population of chromosomes
vector <SGenome> m_vecPop;
//size of population
int m_iPopSize;
//amount of weights per chromo
int m_iChromoLength;
//total fitness of population
double m_dTotalFitness;
//best fitness this population
double m_dBestFitness;
//average fitness
double m_dAverageFitness;
//worst
double m_dWorstFitness;
//keeps track of the best genome
int m_iFittestGenome;
//probability that a chromosomes bits will mutate.
//Try figures around 0.05 to 0.3 ish
double m_dMutationRate;
//probability of chromosomes crossing over bits
//0.7 is pretty good
double m_dCrossoverRate;

//generation counter
int m_cGeneration;
void Crossover(const vector<double>
const vector<double>
vector<double>
vector<double>

&mum,
&dad,
&baby1,
&baby2);

void Mutate(vector<double> &chromo);


SGenome GetChromoRoulette();
void GrabNBest(int
NBest,
const int
NumCopies,
vector<SGenome> &vecPop);
void CalculateBestWorstAvTot();
void Reset();
public:
CGenAlg(int
double
double
int

popsize,
MutRat,
CrossRat,
numweights);

//this runs the GA for one generation.


vector<SGenome> Epoch(vector<SGenome> &old_pop);

//-------------------accessor methods
vector<SGenome> GetChromos()const{return m_vecPop;}
double AverageFitness()const{return m_dTotalFitness / m_iPopSize;}
double BestFitness()const{return m_dBestFitness;}
};

Quando um objeto CGenAlg criado, o nmero de pesos em cada rede neural dos varredores
passado para ele, junto com o tamanho da populao total. O construtor inicializa a populao inteira com
pesos aleatrios e ento cada cromossomo alocado ao seu respectivo crebro de varredor usando o
mtodo CNeuralNet::PutWeights.
Os varredores esto prontos para a ao!

8. Colocando tudo junto


Eu no entrarei em detalhes sobre a descrio das classes CMineSweeper e CController pois elas
so facilmente compreensveis a partir dos comentrios no cdigo. Eu apenas descreverei o loop
principal, assim voc saber exatamente o que est acontecendo. Algumas linhas que esto do fonte foram
omitidas por clareza. Esse cdigo que falta apenas trata de coisas cosmticas como atualizar o mostrador
de grficos e outras estatsticas.
bool CController::Update()
{
//run the sweepers through CParams::iNumTicks amount of cycles. During
//this loop each sweeper's NN is constantly updated with the appropriate
//information from its surroundings. The output from the NN is obtained
//and the sweeper is moved. If it encounters a mine its fitness is
//updated appropriately,
if (m_iTicks++ < CParams::iNumTicks)
{
for (int i=0; i<m_NumSweepers; ++i)
{
//update the NN and position
if (!m_vecSweepers[i].Update(m_vecMines))
{
//error in processing the neural net
MessageBox(m_hwndMain, "Wrong amount of NN inputs!", "Error", MB_OK);
return false;
}
//see if it's found a mine
int GrabHit = m_vecSweepers[i].CheckForMine(m_vecMines,
CParams::dMineScale);
if (GrabHit >= 0)
{
//we have discovered a mine so increase fitness
m_vecSweepers[i].IncrementFitness();
//mine found so replace the mine with another at a random
//position
m_vecMines[GrabHit] = SVector2D(RandFloat() * cxClient, RandFloat() *
cyClient);
}
//update the fitness score
m_vecThePopulation[i].dFitness = m_vecSweepers[i].Fitness();
}
}

Esta primeira parte do comando if roda todos os varredores atravs de uma gerao (uma gerao
consiste no CParams::iNunTicks em quantia de ciclos do computador), atualizando suas redes neurais e
posies de acordo. Se uma mina-terrestre encontrada, ela removida e a classificao de aptido do
varredor aumentada em 1. A mina ento trocada por outra que posicionada aleatoriamente.
//Another generation has been completed.
//Time to run the GA and update the sweepers with their new NNs
else
{
//increment the generation counter
++m_iGenerations;
//reset cycles
m_iTicks = 0;
//run the GA to create a new population
m_vecThePopulation = m_pGA->Epoch(m_vecThePopulation);
//insert the new (hopefully)improved brains back into the sweepers
//and reset their positions etc
for (int i=0; i<m_NumSweepers; ++i)
{
m_vecSweepers[i].PutWeights(m_vecThePopulation[i].vecWeights);
m_vecSweepers[i].Reset();
}
}
return true;
}

O comando else executado no fim de cada gerao. esse trecho de cdigo que coleta todos os
cromossomos dos varredores e classificaes de aptides e envia essa informao para o algoritmo
gentico. O AG ento faz suas coisas, passa os novos pesos de volta e ento os coloca nos crebros da
nova gerao de varredores. Tudo resetado um novo ciclo comea as ser executado da mesma
maneira.
Essa funo Update itera infinitamente at que voc decida que os varredores desenvolveram um
comportamento interessante o bastante. Isso em geral leva por volta de cinqenta geraes.
Apertando a tecla F quanto o programa est sendo executado, colocar o programa em modo de
tempo acelerado e voc ver um simples grfico do progresso da populao.

8.1 Coisas para se tentar


Desenvolva os varredores para que eles evitem as minas.
Desenvolva os varredores para que eles colham as minas, mas evitem outro tipo de objeto (no
to fcil quanto voc pensa).

Quando voc viu o cdigo, possivelmente a coisa mais gritante que voc notou foi que o operador
de cruzamento simples usado aqui no muito eficiente. Voc sabe por qu? Voc pode projetar um
operador de cruzamento mais eficiente?
possvel se projetar uma rede neural que use menos entradas e neurnios ocultos. O quo
pequena voc pode fazer uma rede que ainda se desenvolva com um comportamento efetivo?

8.2 E isso tudo turma!


Eu achava que nunca ia chegar ao fim disto, mas at que enfim cheguei! Se qualquer a um de
vocs fizer alguma coisa interessante com redes neurais depois que leu este captulo, eu adoraria ser
informado. Sinta-se livre para usar o meu cdigo em seus prprios projetos, mas eu gostaria se voc me
desse algum crdito por ele.
E alm de tudo, divirta-se!

You might also like