The Backpropagation
Un altro argomento di notevole interesse nel campo delle reti neurali riguarda l'algoritmo di
backpropagation, uno dei più efficienti sistemi di apprendimento delle reti multilivello caratterizzate
da funzioni di uscita differenziabili.
Questo algoritmo viene implementato nel toolbox di Matlab in tutte le sue varianti, che nel tempo
sono state aggiunte alla versione originale per ottimizzare il tempo di computazione. Si tenga
presente che una rete con bias, livello di uscita lineare e livello intermedio sigmoidale è capace di
approssimare, se addestrata con l'algoritmo di backpropagation, qualunque funzione con un numero
finito di discontinuità. Ciò che rende di queste reti un punto di partenza per tipologie più complesse
è la capacità di determinare delle risposte "ragionevoli" ( conformi quindi alla funzione da
approssimare ) a input nuovi, ossia non utilizzati come esempi di addestramento.
In questo capitolo vedremo di mettere in pratica le conoscenze basilari che abbiamo assimilato sino
a questo punto per mostrare come implementare una rete multilivello che si appoggia sulla
backpropagation.
Feedforward Network
Una rete con architettura feedforward è costituita da un livello neuronale di uscita con funzione di
trasferimento lineare preceduto da uno o più livelli nascosti di tipo sigmoidale. La funzione lineare
come livello ultimo garantisce alla rete di avere in uscita valori non compresi in un range limitato,
come è il caso del sigmoide o del gradino. Un esempio di feedforward network è quello riportato in
figura 1. In questo caso la rete ha due ingressi, il primo livello è costituito da 4 neuroni con
funzione sigmoidale, il secondo da 3 neuroni lineari che costituiscono l'uscita della rete stessa.
Figura 1 - Un esempio di feedforward network
Il primo passo da compiere è quello di creare l'oggetto che contiene le informazioni della rete che si
vuole realizzare. Il comando matlab adatto allo scopo è newff che accetta 4 parametri di ingresso:
1.
2.
3.
4.
Una matrice Rx2 contenente i valori minimo e massimo di ogni input della rete
Un vettore riga contenente il numero di neuroni che costituisce ogni livello della rete
Un array di celle con i nomi delle funzioni di trasferimento utilizzate in ciascun livello
Il nome della funzione di addestramento che si vuole utilizzare per allenare la rete
Per esempio, la seguente istruzione:
•
net = newff( [-1 2 ; 0 5 ] , [ 3 1 ] , { 'tansig' , 'purelin' } , 'traingd' );
crea una rete a due livelli che accetta come input i un vettore di due elementi. Il primo ingresso è
limitato tra -1 e 2 mentre il secondo varia tra 0 e 5. Il primo livello della rete è costituito da 3
neuroni sigmoidali mentre il secondo da un solo neurone lineare. Traingd, che analizzeremo in
seguito, è la funzione di addestramento della rete.
Molti dei concetti che abbiamo analizzato per il caso delle funzioni newlin e newp valgono anche
per newff. Di conseguenza è possibile modificare i valori dei pesi e dei bias e riportarli al loro
valore di default attraverso il comando init. La modifica dei pesi, cosi' come la loro visualizzazione,
avviene sempre attraverso le proprietà IW e b dell'oggetto net creato. In questo caso, visto che ci
sono più livelli, dobbiamo stare attenti a quali indici specificare a seconda del livello di riferimento.
Per esempio:
•
net.LW{2,1}.initFcn = 'rands';
va a modificare, in modo casuale, i valori di inizializzazione dei pesi che congiungono i neuroni del
livello 1 con quelli del livello 2. Altrimenti è possibile modificare direttamente i pesi come al solito:
•
netl.LW{2,1} = W;
dove W è una matrice 1x3 ( ovviamente ci stiamo riferendo alla rete net creata sopra, che ha 3
neuroni nel livello 1 e uno solo nel livello 2 ).
Anche la simulazione segue pari passo quanto abbiamo visto nel caso dei percettroni o delle reti
lineari, e di conseguenza si utilizza il comando sim senza alcuna modifica sintattica.
Chiaramente ciò su cui conviene soffermarci riguarda la politica di addestramento della rete. L'idea
è quella di aggiornare i pesi e i bias in modo incrementale e partendo sempre da un training set di
riferimento, di cui conosciamo ingressi e uscite. L'obiettivo è quello di minimizzare una funzione di
errore che per default è la consueta sommatoria del quadrato degli errori tra uscita reale e uscita
della rete ( sommatoria di tutte gli errori, uno per ogni linea di uscita ) definita qui sotto:
Gli algoritmi di adattamento sono numerosi, ma tutti si basano sul concetto di backpropagation,
vale a dire una modifica dei pesi nella direzione opposta al gradiente, inteso come derivata parziale
dell'errore E sulla matrice dei pesi Wji. Un algoritmo di backpropagation è il seguente:
Il Toolbox implementa algortimi a discesa del gradiente simili a quelli riportato sopra cercando di
ottenere la miglior ottimizzazione possibile. In generale anche nel caso del backpropagation esiste
l'apprendimento incrementale in cui i pesi vengono aggiornati ad ogni esempio e il batch training
dove l'aggiornamento dei pesi avviene solo dopo che tutti gli esempi sono stati valutati.
La funzione adapt, che abbiamo già analizzato, esegue l'apprendimento incrementale e la sua
computazione varia a seconda di alcuni parametri che vengono fissati modificando le proprietà
dell'oggetto net. In particolare si può scegliere il numero di epoche di aggiornamento
( net.adaptParam.passes ), il fattore di apprendimento ( net.LW{i,j}.learnParam.lr ) e la funzione di
addestramento incrementale ( net.LW{i,j}.learnFcn - Di default è settata a learngd, ma è
consigliabile utilizzare learngdm che risulta essere più efficiente ). E' consigliabile come al solito
visualizzare l'elenco completo delle proprieta' dell'oggetto rete creato direttamente dalla consolle
dei comandi di matlab.
La funzione train, viceversa, esegue il training di tipo batch. In questo caso i gradienti calcolati ad
ogni passo dell'algoritmo vengono sommati ( per aggiornare i pesi ) solo al termine della
valutazione di tutti gli esempi. La funzione di apprendimento eseguita da train è settabile, come
quella di adapt. In particolare sono 7 i parametri relativi a train che possono essere modificati.Quelli
significativi sono:
1. net.trainParam.epochs = N : setta il numero delle epoche dopo le quali l'algoritmo termina
( se non converge prima )
2. net.trainParam.lr = L : setta il fattore di apprendimento
3. net.trainParam.goal = E : rappresenta l'errore massimo che si può commettere tra l'uscita
vera e quella reale e viene preso come test di uscita dell'algoritmo
4. net.trainParam.show = A, significa che ogni A epoche viene visualizzata nella finestra di
output di matlab lo stato dell'algoritmo ( funzione di addestramento usata, numero di epoche
trascorse, valore della funzione di errore confrontata con il goal test, valore del gradiente ecc
).
La funzione di apprendimento è il famoso 4 parametro di ingresso di newff. Si osservi la tabella
rappresentata in figura 2 per avere un idea delle prestazioni di questi algoritmi. Per maggiori
informazioni sul loro significato, consultare il capitolo 5 della guida inglese al toolbox.
Tempo
Epoche MFlops
(s)
Funzione
Tecnica
traingdx
Variable
Learning Rate
57.71
980
2.50
trainrp
Rprop
12.95
185
0.56
trainscg
Scaled
Coniugate
Gradient
16.06
106
0.70
traincgf
FletcherPowell CG
16.40
81
0.99
traincgp
Polak-Ribiere
CG
19.16
89
0.75
traincgb
Powell-Beale
CG
15.03
74
0.59
trainoss
One-Step
Secant
18.46
101
0.75
trainbfg
BFGS quasiNewton
10.86
44
1.02
trainlm
LevenbergMarquardt
1.87
6
0.46
Figura 2 - Prestazioni dei diversi algoritmi di backpropagation
Il miglioramento della Generalizzazione
L'ultimo aspetto che verrà trattato e che consentirà di acquisire nuovi strumenti per migliorare la
propria rete neurale è inerente al problema del cosidetto overfitting. Può succedere molto
frequentemente che, dopo l'addestramento della rete, l'errore calcolato sugli esempi del training set
venga minimizzato ma che, quando nuovi dati vengono portati in ingresso alla rete stessa, l'errore
ad essi associato sia molto grande ( uscite della rete discordanti da quelle attese ). In pratica la rete
ha memorizzato gli esempi del training set ma non è stata addestrata per generalizzare le nuovi
situazioni che ad essa si presentano.
La figura 3 mostra il risultato di una rete 1-20-1 ( 2 livelli rispettivamente di 20 e 1 neurone e 1
ingresso) che è stata allenata per approssimare una funzione sinusoidale perturbata dal rumore.
Figura 3 - Approssimazione di funzione con una rete soggetta a overfitting
La funzione seno pulita dal rumore è graficata in modo tratteggiato, le misurazioni rumorose sono
rappresentate dai simboli '+' mentre l'uscita della rete neurale è data dalla linea continua. L'obiettivo
di questa rete è quello di avere una buona raffigurazione della funzione seno nonostante la presenza
del rumore. Come si può facilmente osservare, le due curve non coincidono affatto e si dice che la
rete ha causato un overfit dei dati in ingresso, ossia non ha predetto le corrette uscite come ci si
attendeva.
Uno dei metodi per ridurre questo problema è quello di progettare una rete con un adeguato numero
di livelli e di neuroni. E' vero che, più una rete è grande e più complesse sono le funzion che può
simulare, ma si è altrettanto osservato che la complessità della rete aumenta la sua possibilità di
creare overfitting sui dati. L'aspetto critico è che attualmente non c'è nessuna teoria che illustri
quanto grande deve essere progettata una rete in relazione all'applicazione in cui deve essere
utilizzata.
Il Neural Network Toolbox implementa due metodi per limitare il problema dell'overfitting.
Il primo metodo è chiamato regolarizzazione e consiste nel modificare in modo opportuno la
funzione di errore che viene minimizzata durante l'addestramento. Per default questa funzione è
mse, definita precedentemente sempre in questa trattazione. E' tuttavia possibile modificare questa
funzione aggiungendo un termine relativo alla sommatoria pesata dei quadrati dei pesi:
La funzione di errore msereg contiene tutti i pesi della rete e, dato che viene minimizzata, questo
comporta sempre la determinazione dei pesi più piccoli durante l'addestramento. Pesi di dimensioni
ridotte sono fondamentali affinchè il responso della rete in prossimità a ingressi mai visti non si
discosti troppo dal valore desiderato. E' possibile modificare il valore della funzione di errore
attraverso il relativo attributo dell'oggetto net creato con il comando newff e specificare il
coefficiente di performanza ( y ) che compare nella formula msereg.
•
•
net.performFcn = 'msereg';
net.performParam.ratio = 0.5;
Il problema principale della regolarizzazione manuale è che non è facile determinare il giusto valore
del coefficiente y. Se è troppo alto, infatti, si rischia l'overfitting mentre se è troppo basso c'è il
rischio che la rete non approssimi bene neppure gli esempi del training set. Questa situazione critica
viene risolta affidandosi alle routines del toolbox che automaticamente settano il corretto valore di
questo parametro. L'esempio di codice riportato qui sotto crea una rete 1-20-1 addestrata con la
funzione trainbr che implementa in modo automatico alcune tecniche statistiche per determinare i
valori dei parametri di performanza, togliendo notevole carico di lavoro al progettista. L'obiettivo è
sempre quello di approssimare una funzione seno con rumore.
•
•
•
•
•
•
•
•
net = newff( [ -1 1 ] , [ 20 , 1 ] , { 'tansig' , 'purelin' } , 'trainbr' );
net.trainParam.show = 10;
net.trainParam.epochs = 50;
randn( 'seed', 192736547 );
p = [ -1:.05:1 ];
t = sin( 2 * pi * p ) + 0.1*randn( size(p) );
net = init(net);
net = train( net, p, t );
Si definisce un set di esempi costituiti da valori casuali di una funzione seno perturbata dal rumore (
rappresentato dall'aggiunta del termine casuale ai valori delle uscite t ). Sebbene il tutto è nascosto
agli occhi del progettista, l'algoritmo memorizzato in trainbr provvede a determinare quanti
parametri della rete ( pesi e bias ) sono necessari affinchè la rete stessa implementi una certa
funzione. Nel nostro esempio. nonostante i parametri totali siano 61 quelli che sono stati settati
diversi da zero ( e quindi tutti gli altri saranno inutilizzati ) sono appena 14, riducendo
sensibilmente la dimensione della rete ( e ricordando che la grandezza di una rete è indice del suo
grado di overfitting dei dati ). In figura 4 viene visualizzata l'uscita della rete in questione. Si può
notare come i nuovi dati vengano generalizzati molto bene.
Figura 4 - Approssimazione di funzione con una rete regolarizzata
Un altro metodo per migliorare la generalizzazione è chiamato early stopping. L'idea è quella di
creare un training set in base al quale modificare pesi e bias della rete nel consueto modo, e un
validation set che serve per arrestare l'addestramento quando comincia a presentarsi il fenomeno di
overfitting. In pratica si cerca di eseguire parallelamente l'addestramento della rete con il training
set e la simulazione con il validation set. All'inizio dell'addestramento l'errore del validation set è
piccolo, vale a dire che la rete si comporta bene anche con dati diversi da quelli con cui è stata
allenata. Tuttavia, quando la rete comincia a overfittare i dati, anche l'errore sugli esempi di
validazione aumenta e l'addestramente viene bloccato.
Tutte le funzioni elencate in tabella 3 supportano l'early stopping. Non esiste un comando
particolare per applicare questo metodo ma si tratta semplicemente di passare opportuni parametri
di ingresso al comando train che già abbiamo analizzato. A tale scopo, questo è l'uso del comando
che deve essere fatto.
•
net = train( net, p, t, [], [], v );
dove net è la rete, p e t rappresentano il training set e v è una struttura che contiene i vettori P e T,
rispettivamente degli ingressi e delle uscite del validation set.
•
•
v.P = [ -0.975:.05:0.975 ];
v.T = sin( 2 * pi * v.P ) + 0.1*randn( size( v.P ) );
Per concludere, osserviamo il risultato della rete soggetta a early stopping in figura 5. Nonostante la
curva non sia regolare come nel caso precedente, la rete non genera overfitting dei dati.
Figura 5 - Approssimazione di funzione con una rete addestrata con early stopping
Scarica

scarica - Automatica