Questo testo contiene qualche appunto pratico e sintetico sull'uso delle reti neurali di tipo feed forward addestrate con il metodo back propagation. Le reti neurali di cui si parla qui non sono un modello stretto di quelle naturali, ma funzionano come un potente sistema matematico di interpolazione "addestrabile" per calcolare funzioni non lineari di qualsiasi complessità a partire da esempi di ingressi/uscite noti. Un altro grosso vantaggio, oltre a quello di poter "apprendere" funzioni complesse e poter calcolare in parallelo più funzioni contemporaneamente, è quello di richiedere solo semplici somme e moltiplicazioni, più l' applicazione di una funzione di trasferimento in uscita uguale per tutte le unità.
L'unità elementare di calcolo
di una rete neurale è il neurone. Può avere uno o
più ingressi provenienti da altri neuroni e ha una
sola uscita eventualmente diretta verso uno o più neuroni successivi. I segnali, sia
in ingresso che in uscita possono assumere un qualsiasi valore continuo compreso tra 0 e 1.
In questa figura il neurone Y ha 4 ingressi (sinapsi o connessioni)
a cui arrivano i segnali H1..H4 ciascuno associato ad
un peso (w1..w4). I pesi sono un fattore moltiplicativo applicato al
segnale di ingresso, possono essere positivi o negativi (sinapsi eccitatoria o inibitoria),
maggiori o minori di 1 (sinapsi amplificatrice o attenuatrice).
In più ogni neurone ha un peso di bias (wb) che serve per regolare il
punto di lavoro del neurone stesso. Questa sinapsi si considera collegata ad
un'unità fittizia che genera un segnale sempre pari a 1,
e pertanto non è necessario rappresentarla nel disegno.
Si definisce attivazione interna del neurone (chiamata A)
la somma pesata di tutti i segnali in ingresso:
A = H1*w1 + H2*w2 + H3*w3 + H4*w4 + wb
Il segnale in uscita dal neurone è invece
chiamato attività ed è calcolato applicando una
funzione di trasferimento di forma sigmoidale che ha dato buoni risultati pratici e semplifica
la trattazione matematica. Nella formula seguente X rappresenta
l'attivazione interna ed "e" è la base dei logaritmi naturali (2,71828):
Y = 1 / (1 + e^(-A))
A seconda del valore dei pesi, al variare dei valori in ingresso l'uscita
del neurone riproduce una diversa funzione matematica.
Per semplicità è possibile considerare il caso di
un singolo neurone con uno o due ingressi (problema a 1 o 2 dimensioni). I grafici seguenti
mostrano alcuni possibili andamenti dell' attività di uscita
in funzione di diverse configurazioni dei pesi.
Per essere utile una rete neurale va addestrata. Si parte con i
pesi impostati a piccoli valori casuali e si sottopongono ad essa degli esempi di
ingressi e uscite corretti, si applica un procedimento di correzione dei pesi
per piccoli passi fino ad ottenere un'uscita effettiva che per ogni esempio
non si discosti oltre una certa percentuale dall' uscita
campione usata per l'addestramento. Per fare questo si usa la regola delta
generalizzata, in sostanza per ogni esempio fornito in ingresso si valuta
l'errore in uscita rispetto al valore desiderato, e si calcola
un delta del neurone che serve poi per modificare i diversi pesi.
In questa figura si suppone che il
neurone Y sia l'uscita della nostra rete, pertanto per determinare il suo
errore possiamo semplicemente fare la differenza tra l'uscita desiderata (D) e
quella effettiva (Y). Il delta del neurone si calcola moltiplicando l'errore
per la derivata della funzione di uscita (Y*(1-Y)), in questo modo il delta
rappresenta in un certo senso la velocità di variazione dell'errore del neurone
al variare complessivo dell'attivazione interna. I pesi possono poi essere modificati
come da formula. Ogni peso viene modificato di una quantità pari al delta
del neurone per il segnale in arrivo su quella connessione e il tutto ancora moltiplicato
per un fattore Epsilon (Eps) detto fattore (o tasso) di apprendimento
(compreso tra 0,1 e 0,9) scelto a priori. Tanto maggiore è Epsilon
e tanto più rapida è la variazione dei pesi durante
l'addestramento, ma questo puo portare ad errori eccessivi e oscillazioni
nell'apprendimento. Viceversa un Epsilon basso permette un addestramento
più preciso ma il tempo occorrente per esso aumenta notevolmente
ed è possibile che l'apprendimento si blocchi in un minimo locale
della funzione di errore.
Nel caso in cui il neurone da addestrare appartenga
ad uno strato intermedio non è possibile determinare il suo errore come
differenza tra l'uscita voluta e quella effettiva, in questo caso si
usa "retropropagare" l'errore che era stato calcolato sull'uscita effettiva
attraverso il delta. In questa figura si vede che l'errore del neurone
H viene considerato pari al delta del neurone successivo moltiplicato per il peso
della connessione che unisce i due neuroni. Nel caso in cui la sua
uscita andasse verso più neuroni, l' errore è la somma di tutti
i singoli delta pesati come calcolato nella seguente figura:
1) Si sceglie un epsilon iniziale grande maggiore di 0.6 2) Si impostano i pesi a piccoli valori casuali 3) Per ogni esempio a) Si presenta l'esempio alla rete e la si esegue per determinarne l'uscita b) Si calcolano i delta di tutte le unità c) Si modificano i pesi 4) Se il massimo errore commesso da una qualsiasi unità di uscita durante un qualsiasi esempio è maggiore di quello ammesso tornare al punto 3 5) Se continuando l'addestramento l'errore non diminuisce, allora diminuire epsilon e tornare al punto 3
const NX = 2; const NH = 4; const NY = 2; var A: real; var i, k, j: integer; var X: array[1..NX] of real; var H: array[1..NH] of real; var Y: array[1..NY] of real; var wh: array[1..NH, 1..NX+1] of real; var wy: array[1..NY, 1..NH+1] of real;
Di seguito sono riportate le procedure di "esecuzione" della rete, cioè il procedimento per ottenere i valori di uscita Y[j] a partire dagli ingressi X[i], la struttura dati aggiuntiva per l'apprendimento (l'array D[j] contiene le uscite desiderate) e la procedura di retropropagazione dell'errore e modifica dei pesi.
//Calcola strato H for k := 1 to NH do begin A := 0.0; for i := 1 to NX do A := A + (wh[k,i] * X[i]); A := A + wh[k, NX+1]; H[k] := 1.0 / (1.0 + EXP(-A)); end; //Calcola strato Y for j := 1 to NY do begin A := 0.0; for k := 1 to NH do A := A + (wy[j,k] * H[k]); A := A + wy[j, NH+1]; Y[j] := 1.0 / (1.0 + EXP(-A)); end;
const Eps = 0.3 var Err: real; var D array[1..NY] of real; var DeltaY array[1..NY] of real; var DeltaH array[1..NH] of real;
//Calcolo errore strato Y for j := 1 to NY do begin Err := D[j] - Y[j]; DeltaY[j] := Err * Y[j] * (1 - Y[j]); end; //Calcolo errore strato H for k := 1 to NH do begin Err := 0.0; for j := 1 to NY do Err := Err + (DeltaY[j] * wy[j, k]); DeltaH[k] := Err * H[k] * (1 - H[k]); end; //Modifica pesi strato Y for j := 1 to NY do begin for k := 1 to NH do wy[j, k] := wy[j, k] + (Eps * DeltaY[j] * H[k]); wy[j, NH+1] := wy[j, NH+1] + (Eps * DeltaY[j]); end; //Modifica pesi strato H for k := 1 to NH do begin for i := 1 to NX do wh[k, i] := wh[k, i] + (Eps * DeltaH[k] * X[i]); wh[k, NX+1] := wh[k, NX+1] + (Eps * DeltaH[k]); end;
Come primo esperimento con una rete semplice ho
voluto provare ad addestrarla al calcolo di una sinusoide. In pratica
in ingresso voglio fornire un valore compreso tra 0 e 1 che
rappresenta l'angolo tra 0 e 360 gradi, e in uscita mi aspetto di ottenere il
seno dell'angolo nel range 0,1..0,9 (il valore da 0,1 a 0,9 in uscita
deve rappresentare il seno da -1 a +1).
Il motivo per cui ho
scelto un range in uscita da 0,1 a 0,9 è che per arrivare esattamente ai
limiti 0 e 1 occorrono pesi molto grandi, mentre è relativamente
semplice arrivare al 10% o al 90%.
Il numero di unità intermedie dipende dalle caratteristiche del problema
da risolvere, empiricamente un buon valore può essere
2,5 volte le unità di ingresso. Poche unità intermedie non
permettono alla rete di apprendere una configurazione di pesi valida, troppe
aumentano oltre il necessario il già consistente tempo di addestramento.
Oltre a questa regola bisognerebbe però tenere anche conto della
complessità della funzione da calcolare. In fondo
le unità del livello H creano tanti "segmenti" o suddivisioni
semplici dello spazio del problema, ed è compito
dell'unità Y sommare tutte queste suddivisioni elementari per ottenere
la funzione complessiva. Se non ci sono sufficienti unità intermedie per descrivere
tutte le caratteristiche elementari della funzione non si riesce a ricostruirla.
Ho scelto questo esempio perché la funzione matematica è nota
ed è facile preparare le coppie in/out di addestramento. L'insieme dei
campioni non va scelto a caso, anzi, la scelta è una cosa di fondamentale importanza
per il corretto addestramento. Bisogna cioè prendere per quanto possibile
campioni distribuiti omogeneamente in tutto lo spazio del problema, e devono anche
essere abbastanza fitti da seguire e descrivere tutte le caratteristiche della
funzione voluta (e possibilmente includere i suoi punti estremi), altrimenti si
rischia di avere una rete che fornisce risultati corretti solo per i
valori campione, ma al di fuori di quelli sbaglia completamente (si specializza
su quei valori e non generalizza).
La tabella seguente mostra 17 campioni del valore
del seno presi nell'intero range 0..360 gradi. La colonna Ingresso X
rappresenta l'ingresso della rete, cioè i gradi traslati nel
range 0..1 tramite la formula:
X = gradi / 360
La colonna uscita D rappresenta invece i valori desiderati di sen(X) traslati nel range 0,1..0,9 tramite la formula:
D = 0,1 + (0,8 * (seno + 1) / 2)
Campione Gradi Seno Ingresso X Uscita D 1 0.0 0.0000 0.0000 0.5000 2 22.5 0.3827 0.0625 0.6531 3 45.0 0.7071 0.1250 0.7828 4 67.5 0.9239 0.1875 0.8696 5 90.0 1.0000 0.2500 0.9000 6 112.5 0.9239 0.3125 0.8696 7 135.0 0.7071 0.3750 0.7828 8 157.5 0.3827 0.4375 0.6531 9 180.0 0.0000 0.5000 0.5000 10 202.5 -0.3827 0.5625 0.3469 11 225.0 -0.7071 0.6250 0.2172 12 247.5 -0.9239 0.6875 0.1304 13 270.0 -1.0000 0.7500 0.1000 14 292.5 -0.9239 0.8125 0.1304 15 315.0 -0.7071 0.8750 0.2172 16 337.5 -0.3827 0.9375 0.3469 17 360.0 0.0000 1.0000 0.5000
A questo punto l'apprendimento consiste nel presentare uno
alla volta i valori di X e determinare l'errore di uscita rispetto al
valore desiderato D. Una volta conosciuto l'errore si modificano i pesi
con la regola delta retropropagando l'errore anche allo strato intermedio
H. Una volta terminati i 17 esempi è terminata un'epoca di
apprendimento, il cui errore sarà l'errore massimo commesso
dall' unità di uscita durante la presentazione degli esempi.
Se l'errore è abbastanza basso l'apprendimento è terminato, con
la certezza che l'unità non sbaglierà più di
quel valore su ciascun esempio, altrimenti si ricomincia con una nuova epoca
ripresentando di nuovo gli esempi e aggiustando i pesi. L'errore dell'epoca può
essere tenuto sotto controllo per valutare l'avanzamento dell'apprendimento della rete.
Le immagini seguenti mostrano l'uscita della rete (in rosso) rispetto alla curva
campione (verde) su cui sono riportati i punti di addestramento.
La prima immagine è la rete non addestrata,
la seconda è la risposta dopo 1000 epoche, la terza dopo
10000 epoche e l'ultima dopo 100mila epoche. Il tasso di apprendimento
utilizzato è stato di 0,5. Come si può vedere si arriva
ad avere un comportamento molto "simile" a quello voluto salvo una
difficoltà a raggiungere i punti estremi della curva.
PESI DOPO L'ADDESTRAMENTO: STRATO H ---------------------- X Bias H1 3.7558 -2.1814 H2 12.2071 -10.9423 H3 14.5007 -1.5133 H4 -2.0168 -2.9522 UNITÀ Y --------------------------------------------- H1 H2 H3 H4 Bias Y -13.1986 6.8320 5.0785 -1.9914 0.5348