Išči po prispevkih:

Home | 6-tehnologija | 68-računalništvo


Simulacija nevronskih mrež v programskem okolju Pure-Data

By: leskovsek


1. TERMINOLOGIJA IN TEORETICNO OZADJE:

Umetne nevronske mreže so matematicna simulacija delovanja bioloških nevronskih mrež s dolocenimi tehnicnimi poenostavitvami. Nevron namrec abstrahiramo na preprost element, ki zna seštevati utežene vhodne signale in rezultat normalizirati s pragovno funkcijo. Take preproste umetne nevrone potem lahko povezujemo v poljubno kompleksne in poljubno plastne nevronske mreže. Najpomembnejši dejavnik pri dobrem delovanju nevronske mreže je postopek ucenja. Ucenje poteka tako, da mreži osredujemo znane vzorce in uteži nevronov nastavljamo tako da se vedno bolj prilegajo tem vzorcem. Primerni izbor ucne množice in ucnega algoritma je tu kljucnega pomena.

V nadaljevanju bomo simulirali »troplastni perceptron« - to je nevronska mreža estavljena iz treh plasti nevronov, ki so namešcene zaporedno ena za drugo, tako da je vsak nevron prejšnje plasti povezan z vsemi nevroni naslednje plasti.

2. POSTOPEK:

Simulacijo sem izvajal v programskem okolju "Pure Data" (v nadaljevanju "pd", www.puredata.org ). To je odprtokodno programsko okolje napisano v jeziku C, namenjeno digitalni obdelavi signalov (DSP), najveckrat se uporablja pri obdelavi slikovnih, zvocnih in video signalov.

Za opis teoreticnega nevrona lahko uporabimo sledec model:

Kar lahko v okolju pd modeliramo s sledecim modelom:

Ker pa je pd napisan v Cju in zaradi tega ni objektno zasnovan, bi bilo grajenje nevronskih mrež z takšimi nevroni skrajno neugodno. Ena možnost je, da v zgornji nevron pošiljamo vektorske signale, kar pomeni, da nam takšen nevron predstavlja celotno plast nevronov,. Druga možnost pa je, da celotno nevronsko mrežo napišemo kar v okolju C ali C++ in ga v okolju pd uporabljamo kot objekt. Za pisanje teh objektov - pd modulov (imenovani "externals") moramo slediti strogim pravilom, ki jih narekuje pd externals SDK. Vec o tematiki tukaj.

Ker se nisem nameraval lotiti pisanja objekta sam, sem pobrskal sem po spletni strani puredata.org in našel objekt nevronske mreže avtorja Davide Morelli. Ugotovil sem da je ta objekt zgolj "wrapper" za razrede iz knjižnjice Fast Artificial Neural Network Library (FANN), ki ima na voljo sledece funkcije:

fann_create_standard
Creates a standard fully connected backpropagation neural network.
fann_create_standard_array
Just like fann_create_standard, but with an array of layer sizes instead of individual parameters.
fann_create_sparse
Creates a standard backpropagation neural network, which is not fully connected.
fann_create_sparse_array
Just like fann_create_sparse, but with an array of layer sizes instead of individual parameters.
fann_create_shortcut
Creates a standard backpropagation neural network, which is not fully connected and which also has shortcut connections.
fann_create_shortcut_array
Just like fann_create_shortcut, but with an array of layer sizes instead of individual parameters.
fann_destroy
Destroys the entire network and properly freeing all the associated memmory.
fann_run
Will run input through the neural network, returning an array of outputs, the number of which being equal to the number of neurons in the output layer.
fann_randomize_weights
Give each connection a random weight between min_weight and max_weight
fann_init_weights
Initialize the weights using Widrow + Nguyen’s algorithm.
fann_print_connections
Will print the connections of the ann in a compact matrix, for easy viewing of the internals of the ann.

fann_print_parameters
Prints all of the parameters and options of the ANN
fann_get_num_input
Get the number of input neurons.
fann_get_num_output
Get the number of output neurons.
fann_get_total_neurons
Get the total number of neurons in the entire network.
fann_get_total_connections
Get the total number of connections in the entire network.
fann_get_decimal_point
Returns the position of the decimal point in the ann.
fann_get_multiplier
returns the multiplier that fix point data is multiplied with.
fann_train
Train one iteration with a set of inputs, and a set of desired outputs.
fann_test
Test with a set of inputs, and a set of desired outputs.
fann_get_MSE
Reads the mean square error from the network.
fann_get_bit_fail
The number of fail bits; means the number of output neurons which differ more than the bit fail limit (see fann_get_bit_fail_limit, fann_set_bit_fail_limit).
fann_reset_MSE
Resets the mean square error from the network.
Training Data Training

fann_train_on_data
Trains on an entire dataset, for a period of time.
fann_train_on_file
Does the same as fann_train_on_data, but reads the training data directly from a file.
fann_train_epoch
Train one epoch with a set of training data.
fann_test_data
Test a set of training data and calculates the MSE for the training data.

Torej hitro ugotovim, da bom za svojo uporabo potreboval predvsem sledece funkcije:
• fann_create
• fann_train_on_file
• fann_run

Po kratkem študiju dokumentacije FANN, sem kreiral sledeco nevronsko mrežo in delovno okolje zanjo:

3. PREIZKUS:

Preizkus smo izvedli na t.i. XOR problemu: To je problem simulacije funkcije seštevanja po modulu 2, torej funkcija ki daje izhod 1, kadar sta oba vhoda razlicna.

Za simulacijo te funkcije smo ustvarili 3-plastni perceptron s 2 vhodoma in 1 izhodom.

Ta perceptron smo naucili XOR funkcije s sledecimi ucnimi vzorci:

IN 1 IN 2 OUT
0 0 0
1 0 1
0 1 1
1 1 0

Pri ucenju smo opravili 100 iteracij RPROP (Resilient Propagation, M.Riedmiller and H.Braun) ucenja. Ta metoda se od standardne metode (backpropagation) razlikuje predvsem v tem, da nastavljanje uteži izvede šele po celotni etapi (ang. 'epoch') in zaradi tega k pravilnim rezultatom konvergira že mnogo hitreje. Metoda RPROP spada med t.i. 'batch training' metode.

Simulacijo smo preizkusili na istih vzorcih, ter dobili sledece rezultate:

IN 1 IN2 OUT1
0 0 0.122
0 1 0.695
1 0 0.675
1 1 0.564

Sedaj smo pri ucenju uporabili 1000 iteracij RPROP ucenja in dobili:

IN 1 IN2 OUT1
0 0 0.000
0 1 0.984
1 0 0.984
1 1 0.003

Sedaj smo pri ucenju uporabili 10000 iteracij RPROP ucenja in dobili:

IN 1 IN2 OUT1
0 0 -0.000140925
0 1 0.99407
1 0 0.994069
1 1 -0.000806645

Nad simulacijo XOR funkcije sem bil zadovoljen, saj je napaka povsod manjša od 0.01.

4. UCENJE MREŽE Z REALNIMI PODATKI:

Za ucenje sem prvo preizkusil incremental backpropagation metodo, ki se je izkazala za mnogo prepocasno in se zato odlocil za RPROP metodo (Resilient Propagation, M.Riedmiller and H.Braun). Ucenje sem izvedel v okolju "Pure Data", podatke sem pa predtem obdelal v okolju Matlab s pomocjo te skripte:

path (path, 'C:\Program Files\pd\extra\NN\nn-0.03');
clear all
close all
inputs = CSVREAD('wdbc.data',0,2);
outputs = CSVREAD('wdbc.data',0,1,[0,1,568,1]);
% normalize ins
for i = 1:30
inputs(:,i)=inputs(:,i)/max(inputs(:,i));
end
in1=inputs(1:400,:);
in2=inputs(401:569,:);

out1=outputs(1:400,:);
out2=outputs(401:569,:);
out1(:,2)=abs(out1(:,1)-1);
out2(:,2)=abs(out2(:,1)-1);

%pisanje fajlov za pd

id_train = fopen('wdbc1.txt','w'); % id_train= train sequence

fprintf(id_train,'400 30 2') ; %header
fprintf(id_train,'\n') ; %nova vrstica

for i = 1:length(in1)
for j = 1:30 %inputs
fprintf(id_train,'%f ', in1(i,j)) ;
end

fprintf(id_train,'\n') ; %nova vrstica

for j = 1:2
fprintf(id_train,'%f ', out1(i, j)) ; %outputs
end

fprintf(id_train,'\n') ; %nova vrstica
end

id_ins = fopen('wdbc2.txt','w'); %id_ins = ins

for i = 1:length(in2)
fprintf(id_ins,'wdbc2 ') ; %naslov
for j = 1:30 %inputs
fprintf(id_ins,'%f ', in2(i,j)) ;
end

fprintf(id_ins,';') ; %semicolon

fprintf(id_ins,'\n') ; %nova vrstica
end
fclose(id_train)
fclose(id_ins)

Mrežo sem uporabljal s 30 nevroni v skriti plasti, za primerjavo sem pa izvedel nekaj meritev tudi z 20 nevroni v skriti plasti. Mrežo sem nato treniral s 1000, 10 000 in 100 000 ponovitvami in preizkusil njeno delovanje sprva na 2/3 vzorcih (na katerih je bila trenirana) in kasneje še na 1/3 vzorcih (na katerih ni bila trenirana), razultati sem naknadno statisticno obdelal s pomcjo matlaba in te skripte:

path (path, 'C:\Program Files\pd\extra\NN\nn-0.03');
out1k = CSVREAD('out1k.txt',0,0);
out10k = CSVREAD('out10k.txt',0,0);
out100k = CSVREAD('out100k.txt',0,0);

%---------------------------------------------

out1ktanh = CSVREAD('out1ktanh.txt',0,0);
out10ktanh = CSVREAD('out10ktanh.txt',0,0);
out100ktanh = CSVREAD('out100ktanh.txt',0,0);

%zaokrozenje na 0 in 1

for i=1:169
for j=1:2
if out1k(i,j)<0.5;
out1kk(i,j)=0;
end
if out1k(i,j)>0.5;
out1kk(i,j)=1;
end
end
end

for i=1:169
for j=1:2
if out10k(i,j)<0.5;
out10kk(i,j)=0;
end
if out10k(i,j)>0.5;
out10kk(i,j)=1;
end
end
end

for i=1:169
for j=1:2
if out100k(i,j)<0.5;
out1kk(i,j)=0;
end
if out100k(i,j)>0.5;
out100kk(i,j)=1;
end
end
end

for i=1:169
for j=1:2
if out1ktanh(i,j)<0.5;
out1kktanh(i,j)=0;
end
if out1ktanh(i,j)>0.5;
out1kktanh(i,j)=1;
end
end
end

for i=1:169
for j=1:2
if out10ktanh(i,j)<0.5;
out10kktanh(i,j)=0;
end
if out10ktanh(i,j)>0.5;
out10kktanh(i,j)=1;
end
end
end

for i=1:169
for j=1:2
if out100ktanh(i,j)<0.5;
out1kktanh(i,j)=0;
end
if out100ktanh(i,j)>0.5;
out100kktanh(i,j)=1;
end
end
end

% binarna napaka (primerjava z "out2")
binerr1k=sum(((out2(:,1)==out1kk(:,1))&(out2(:,2)==out1kk(:,2)))==0)
binerr10k=sum(((out2(:,1)==out10kk(:,1))&(out2(:,2)==out10kk(:,2)))==0)
binerr100k=sum(((out2(:,1)==out100kk(:,1))&(out2(:,2)==out100kk(:,2)))==0)

binerr1ktanh=sum(((out2(:,1)==out1kktanh(:,1))&(out2(:,2)==out1kktanh(:,2)))==0)
binerr10ktanh=sum(((out2(:,1)==out10kktanh(:,1))&(out2(:,2)==out10kktanh(:,2)))==0)
binerr100ktanh=sum(((out2(:,1)==out100kktanh(:,1))&(out2(:,2)==out100kktanh(:,2)))==0)

err1k(:,1)=abs(out1k(:,1)-out2(:,1));
err1k(:,2)=abs(out1k(:,2)-out2(:,2));

err10k(:,1)=abs(out10k(:,1)-out2(:,1));
err10k(:,2)=abs(out10k(:,2)-out2(:,2));

err100k(:,1)=abs(out100k(:,1)-out2(:,1));
err100k(:,2)=abs(out100k(:,2)-out2(:,2));

error1k=err1k(:,1)+err1k(:,2);
error10k=err10k(:,1)+err10k(:,2);
error100k=err100k(:,1)+err100k(:,2);

napaka1k=sum(error1k)/196
napaka10k=sum(error10k)/169
napaka100k=sum(error100k)/169

%------------------------------------

err1ktanh(:,1)=abs(out1ktanh(:,1)-out2(:,1));
err1ktanh(:,2)=abs(out1ktanh(:,2)-out2(:,2));

err10ktanh(:,1)=abs(out10ktanh(:,1)-out2(:,1));
err10ktanh(:,2)=abs(out10ktanh(:,2)-out2(:,2));

err100ktanh(:,1)=abs(out100ktanh(:,1)-out2(:,1));
err100ktanh(:,2)=abs(out100ktanh(:,2)-out2(:,2));

error1ktanh=err1ktanh(:,1)+err1ktanh(:,2);
error10ktanh=err10ktanh(:,1)+err10ktanh(:,2);
error100ktanh=err100ktanh(:,1)+err100ktanh(:,2);

napaka1ktanh=sum(error1ktanh)/196
napaka10ktanh=sum(error10ktanh)/169
napaka100ktanh=sum(error100ktanh)/169

a) napaka pri znanih vzorcih:

napaka1k =

0.2161

napaka10k =

0.1752

napaka100k =

0.1752

b) napaka pri neznanih vzorcih:

napaka1k =

0.2166

napaka10k =

0.1830

napaka100k =

0.2761

Zanimiva ugotovitev je da se je napaka pri 100 000x trenirani mreži povecala za neznane vzorce, medtem ko se ni vec spreminjala za znane vzorce. Ta problem je znan kot overfitting, rešimo se ga pa tako, da ob vsaki iteraciji (ali pa vsaki 10, 100ti iteraciji) stestiramo mrežo na neznanih vzorcih. Takoj ko zasledimo da se napaka ne zmanjšuje vec, proces ucenja zakljucimo.

Oglejmo si kakšna je bila število narobe razvršcenih vzorcev pri teh simulacijah (torej koliko vzorcev je bilo narobe razporejenih):

a) napaka pri znanih vzorcih:

binerr1k =

8

binerr10k =

8

binerr100k =

8

b) napaka pri neznanih vzorcih:

binerr1k =

6

binerr10k =

6

binerr100k =

11

SPREMINJANJE ŠTEVILA NEVRONOV V SKRITI PLASTI:

Zaradi casovne zahtevnosti ucnega algoritma sem primerjal le mreže s 30 in 20 nevroni v skriti plasti:

a) 30 nevronov: napaka pri neznanih vzorcih:

napaka1k =

0.2166

napaka10k =

0.1830

napaka100k =

0.2761

in še število narobe razvršcenih vzorcev:

binerr1k =

6

binerr10k =

6

binerr100k =

11

b) 20 nevronov: napaka pri neznanih vzorcih mreže s 20 nevroni v skriti plasti (za primerjavo):

napaka1k =

0.2550

napaka10k =

0.2696

napaka100k =

0.3796

in še število narobe razvršcenih vzorcev:

binerr1k =

13

binerr10k =

16

binerr100k =

13

SPREMINJANJE AKTIVACIJSKE FUNKCIJE NEVRONOV V SKRITI PLASTI:

Zaradi casovne zahtevnosti ucnega algoritma sem primerjal le sigmoidno in tangenshiperbolicno funkcijo:

sigmoidna funkcija (vir: wikipedia)

tangens hiperbolicna funkcija (vir: wikipedia)

a) sigmoidna funkcija

napaka1k =

0.2166

napaka10k =

0.1830

napaka100k =

0.2761

in še število narobe razvršcenih vzorcev:

binerr1k =

6

binerr10k =

6

binerr100k =

11

b) tangens hiperbolicus funkcija

napaka1ktanh =

0.2159

napaka10ktanh =

0.1725

napaka100ktanh =

0.2700

in še število narobe razvršcenih vzorcev:

binerr1ktanh =

7

binerr10ktanh =

5

binerr100ktanh =

14

Funkcija tanh se je torej izkazala za malenkost boljšo izbiro, problem overfittinga se tudi tukaj pojavi in zato predlagam isto metodo kot sem navedel zgoraj. Za reševanje problema overfitting ima ta knjižnica (library FANN) nastavljiv parameter epochs_between_reports, ki nam omogoca vmesno preverjanje mreže ob tocno dolocenih intervalih. Ucenje lahko torej ustavimo takoj ko se napaka pri testnih vzorcih zacne povecevati - v našem primeru torej nekje med 10 000 in 100 000 ponovitvami.


Ta prispevek je na portalu Publikacije.net objavil/a leskovsek dne 2007-02-21.


Ocenite prispevek:

 

# of Ratings = 1 | Rating = 5/5

Kliknite na XML znak in spremljajte kategorijo [68-Računalništvo] preko RSS!



Publikacije.net - portal svobodnega znanja









Powered by Article Dashboard