Genotip NeuralNetwork

Glavna funkcionalnost neuronke mreže sadržana je u razredu genotipa koja nasljeđuje razred FloatingPoint. Sve težine mreže pohranjene su u polje FloatingPointa, a zbog nasljeđivanja razreda FloatingPoint dostupni su operatori križanja i mutacije definirani za spomenuti nadrazed genotipa.

Izgled neuronske mreže definira korisnik u obaveznom parametru xml datoteke "structure". Parametar je oblika npr. "2 3 1" gdje prvi broj definira broj neurona na ulaznom sloju, zadnji broj neurona na izlaznom sloj, dok brojevi izmežu definiraju broj neurona za svaki skriveni sloj mreže.

Logika neuronke mreže implementirana je pomoću jednostavnog matričnog množenja. Vektor koji je rezultat množenja na prethodnom sloju prosljeđuje se na sljedeći sloj mreže gdje se matrično množi s matricom težina tog sloja. Nakon množenja na svaki neuron primijenjuje se aktivacijska funkcija koja je definirana od strane korisnika u xml datoteci parametara ( "activations"). Linearna ("lin") i sigmoidalna ("sig") funkcija su već implementirane, dok se nova aktivacijska funkcija lako dodaje nasljeđivanjem abstraktnog razreda AbstractActivationFunction koji se potom u glavnom programu dodaje pozivom metode setActivactionFunction.

Genotip prima ulazne vrijednosti mreže iz evaluacijskog operatora, ali je moguće predati ulaznu datoteku koja sadrži iste (xml parametar "dataFile"). U tom slučaju datoteka mora također sadržavati očekivane vrijednosti za svaku grupu ulaznih vrijednosti jer tada nudi i računanje pogreške aproksimacije mreže koja kasnije ima veliku ulogu u daljnjem "učenju" mreže. Dohvat izračunate pogreške dohvaća se pozivom metode getTotalError(). Mreža nudi nekoliko različitih funkcija izračuna pogreške koje se odabiru pomoću xml parametra errorFunc:

  • MSE ( Mean Squared Error )
  • RMSE ( Root Mean Square Error )
  • MAE ( Mean Absolute Error )

Moguće je i definirati važnosti pojedinih setova podataka mrežu preko parametra errorWeights zadajući cijeli broj u svakoj liniji, koji sam program pretvara u postotak važnosti kojim se množi greška za taj podatak.

Operatori križanja i mutacije

U evolucijskim algoritmima nad jedinkama se koriste operatori modificiranja rješenja (mutacije) i operatori kombiniranja rješenja (križanja) kako bi se populacija mijenjala i time stvarala nova generacija.

Svaki genotip može definirati jedan ili više operatora križanja i operatora mutacije. U datoteci s parametrima može se zadati vjerojatnost korištenja svakoga od njih. Ako vjerojatnost korištenja nije zadana, operator će se odabrati slučajnim odabirom svaki put kada se izvodi križanjanje ili mutacija.

U primjeru naše neuronske mreže dodani su neki novi operatori u svoje zasebne klase. Svaki operator mutacije izveden je iz bazne klase MutationOp i implementira metode registerParameters, initialize i mutate.

Operatori mutacije koji su implementirani za rad na neuronskoj mreži i opis njihove funkcionalnosti, koja je implementirana u metodi mutate, zapisani su u tablici:

Naziv klase (operatora)Opis funkcionalnosti
MutScale
  • odabire nasumičnu težinu po uniformnoj distribuciji i pomnoži ju s nasumičnim faktorom uzetim iz intervala [0.5, 1.5]
  • provjerava da je li prijeđena dozvoljena donja ili gornja granica (metodom getLBound()/getUBound()); ako je, vrijednost težine postavlja na maksimalnu/minimalnu dozvoljenu
MutScaleNeuron
  • slično kao MutScale, samo radi nad svim težinama jednog neurona
  • odabere jedan neuron nasumičnim odabirom koristeći uniformnu razdiobu i za svaku njegovu težinu bira faktor iz intervala [0.5, 1.5] s kojim će ju pomnožiti
MutNeuron
  • sve težine nasumično odabranog neurona promijeni dodajući im nasumično odabranu double vrijednost
  • radi provjeru da sve vrijednosti težina ostanu u dozvoljenim granicama
MutNeuronN
  • modificira nekoliko neurona
  • broj neurona koji će se modificirati bira se nasumično, kao i neuroni koji će se modificirati
  • radi kao MutNeuron, samo na svim odabranim neuronima
MutGauss
  • mijenja nasumično odabranu težinu tako da joj pridoda vrijednost iz normalne razdiobe oko nule sa standardnom devijacijom koja ima iznos desetine vrijednosti gornje granice (upper bound, parametar UBound)

Algoritam backpropagation

Neuronske mreže mogu se učiti raznim metodama, od kojih smo već pokazali populacijske. Populacijske metode uče mrežu više ili manje nasumičnim izmjenama u vrijednostima težina dviju ili više jedinki neuronske mreže. No takav način učenja ne nudi garanciju da će se optimalno rješenje pronaći. Važno je napomenuti da se radi o Feedfoward neuronskoj mreži.

Stoga koristimo backpropagation koji je usko povezan uz samu strukturu mreže. Za razliku od populacijskih metoda, backpropagation se ne oslanja na nasumičnost učenja i križanja jedinki, već na gradijentnom spustu i samoj grešci mreže.

Korištenjem gradijentnog spusta podrazumjeva se mogućnost zapinjanja u lokalnom minimumu.

Kao što mu ime kaže, "backpropagate" - propagirajući unazad, iznos greške na izlazu mreže se propagira kroz slojeve, pritom ukazujući na velićinu greške pojedinih neurona/težina koja u konačnici, uz ostale parametre, određuje smjer i iznos promjene određene težine u mreži.

Klasa algoritma Backpropagate, koja nasljeđuje Algorithm, ne implementira sam algoritam, već samo poziva metodu backpropagate() nad svakom jedinkom populacije, što ujedno znači da su jedinke međusobno odvojene, odnosno ne ovise jedna o drugoj.

Omogućuje postavljanje parametra brzine učenja (learningRate) kroz konfiguracijsku datoteku ECFa.

Metoda backpropagate() prima jedan argument tipa double koji odgovara parametru brzine učenja (eng. learning rate). Implementriano je online učenje, što znači da se u jednoj iteraciji algoritma za svaki uzorak učenja mijenjaju vrijednosti težina neuronske mreže.

Pseudokod implementiranog algoritma:

Za svaki (input, target) iz baze uzoraka čini
  Izračunaj derivaciju aktivacijske funkcije za ulaznu sumu svakog neurona, Derivatives
  Izračunaj iznos aktivacijske funkcije za ulaznu sumu svakog neurona, Outputs

  Za svaki izlazni neuron k izračunaj pogrešku Errors[k]:
    Errors[k] ← Derivatives[k] * ( target[k] − Outputs[k] )

  Za svaki skriveni neuron h izračunaj pogrešku Errors[h]:
    Errors[h] ← Derivatives[h] * ∑s є HigherLayer(h) ( weight[h,s] * Derivatives[s] )

  Ugodi svaki težinski faktor weight[i,j]
    weight[i,j] ← weight[i,j] + learningRate * Outputs[i] * Errors[j]
Kraj