"Lezione_06_1.gif"

Università di Foggia

Dipartimento di Scienze Biomediche

Prof. Crescenzio Gallo

"Lezione_06_2.gif"

c.gallo@unifg.it

Reti neurali artificiali con Mathematica

Programmiamo una rete neurale

Lezione 6

Introduzione

Al fine di rendere le cose più semplici per il lavoro futuro, trasformiamo quello che abbiamo fatto nelle precedenti sezioni in un semplice programma Mathematica che possiamo chiamare in un solo passaggio (o due) in modo da poter testare idee diverse senza dover passare attraverso tutto questo noioso calcolo manuale. Stiamo per trasformare il codice scritto in recedenza in piccoli programmi.

Routine

Stiamo per utilizzare questa struttura di base come una piattaforma per alcune future aggiunte nelle sezioni successive. Per ora, inizieremo attraverso la creazione di quello che abbiamo già fatto nella forma di un programma che renderà più facile iterare e testare con Mathematica.

Utilità

Per prima cosa, definiamo alcune funzioni di utilità che useremo nel nostro processore principale a rete neurale: la funzione sigmoidea non lineare "Lezione_06_3.gif", ed una funzione per scegliere casualmente un elemento da una lista, chooseRandom. Utilizzeremo la prima per condizionare l'uscita dei neuroni artificiali, e la seconda per selezionare i candidati per il training (NB: potremmo semplicemente scorrere gli elementi del training set, ma è più divertente lasciare che il non determinismo prenda il sopravvento).

Cominciamo con alcune definizioni:

"Lezione_06_4.gif"

"Lezione_06_5.gif"

setupNN

Qui definiamo una funzione di 'setup' che crea le due matrici dei pesi "Lezione_06_6.gif" and "Lezione_06_7.gif". Ricordiamo che esse sono i principali 'ingredienti' dell'algoritmo di backpropagation discusso nella sezione precedente. Per costruire queste matrici abbiamo bisogno della dimensione dei vettori di ingresso e uscita così come il numero di unità nascoste.

La routine setupNN prende tre parametri, la dimensionalità degli strati di ingresso, nascosti e di uscita, rispettivamente, e restituisce due matrici. È possibile assegnare il risultato a una sola variabile per passare il tutto alla funzione di addestramento.

"Lezione_06_8.gif"

Quindi, per impostare una rete con 5 neuroni di input, 10 neuroni nascosti e 3 di uscita:

"Lezione_06_9.gif"

Questa è la matrice 5×10 dei pesi tra le unità di input e quelle nascoste:

"Lezione_06_10.gif"

"Lezione_06_11.gif"

e questi sono i pesi (10×3) tra le unità nascoste e di output:

"Lezione_06_12.gif"

"Lezione_06_13.gif"

Per ora, questo semplice stato basterà per i nostri bisogni immediati. Nelle sezioni successive aggiungeremo funzionalità alla nostra rete che richiederanno ulteriori informazioni di stato. Per rendere le cose più facili, le aggiungeremo semplicemente alla struttura di stato esistente.

trainNN

Ecco il grosso dell'algoritmo di addestramento backpropagation. La routine trainNN prende un training set e un insieme di pesi e itera l'algoritmo standard di addesramento backpropagation 1000 volte con un tasso di apprendimento η di 0.5. È possibile modificare le iterazioni e il tasso di apprendimento fornendo i parametri appropriati nei rispettivi argomenti.

trainNN restituisce una lista di due elementi dopo l'addestramento: un elenco di errori (al quadrato), uno per ogni iterazione, e la matrice finale dei pesi (NB: si potrebbe modificare il programma in modo da restituire sia l'errore che la matrice dei pesi ad ogni iterazione. Questo può essere utile quando si desidera confrontare le prestazioni dell'addestramento della rete).

È istruttivo separare l'errore complessivo e farne il grafico per vedere quanto bene la rete sta imparando. Quindi, se si ritiene che la stessa ha bisogno di più addestramento, si estraggono le matrici dei pesi e le si passano di nuovo a trainNN per un'altra sessione.

Osserviamo anche che abbiamo fatto alcuni piccoli cambiamenti per gestire le unità di bias (distorsione) introdotte in precedenza. In questo caso, l'algoritmo di training si accorge al volo se vi sono unità di bias osservando la forma delle matrici dei pesi.

"Lezione_06_14.gif"

Infine, abbiamo bisogno di un modo per passare un dato vettore di input attraverso le matrici dei pesi per ottenere l'output desiderato. feedForward prende un vettore di input e le matrici dei pesi e restituisce il vettore di output.

"Lezione_06_15.gif"

Queste routine sono la sintesi programmatica del codice precedentemente sviluppato. Le utilizzeremo come base per il lavoro futuro, modificandole all'occorrenza.

Test

Dovremmo provare le routine sviluppate per assicurarci di ottenere i risultati sperati. Eseguiamo il test delle lettere T-C.

Test T-C

Ricordiamo dalla lezione 5 che questa rete, una volta addestrata, può distinguere tra una 'T' e una 'C'. Per completezza, assicuriamoci che il tutto funzioni ancora nella nuova versione:

"Lezione_06_16.gif"

Per prima cosa, impostiamo lo stato. Questa rete ha 9 input e 1 output, e utilizzeremo 5 unità nello strato nascosto come nell'esempio precedente:

"Lezione_06_17.gif"

Ora addestriamo la rete passandole il training set ts e i pesi ws. Di default la rete eseguirà 1000 iterazioni con η=0.5. Il risultato dell'addestramento è una tabella di errori (chiamata εTable) e il set finale dei pesi ws.

"Lezione_06_18.gif"

Ecco un grafico degli errori in funzione del tempo:

"Lezione_06_19.gif"

"Lezione_06_20.gif"

Sembra piuttosto buono. Infatti sembra che si ottengano errori accettabili dopo 7-800 iterazioni.

Controlliamo gli input (ricordiamo che 0.9=T e 0.1=C). I primi quattro input sono delle T, gli altri quattro delle C.

"Lezione_06_21.gif"

"Lezione_06_22.gif"

Bene. Verifichiamo ora la stabilità della rete in presenza di rumore. Prima una T 'danneggiata':

"Lezione_06_23.gif"

"Lezione_06_24.gif"

Inseriamola nella rete:

"Lezione_06_25.gif"

"Lezione_06_26.gif"

Abbiamo ottenuto un valore molto vicino a 0.9, il valore atteso per una 'T'.

E se esaminiamo una C 'distorta'?

"Lezione_06_27.gif"

"Lezione_06_28.gif"

"Lezione_06_29.gif"

"Lezione_06_30.gif"

Ache la 'C' distorta è stata riconosciuta. Successo in presenza di rumore!

Salvare lo stato

Una valida caratteristica di questo approccio modulare è la possibilità di salvare lo stato e addestrare la rete ancora un po' più tardi (o con un training set alternativo, etc.). La matrice dei pesi ws può essere scritta in un file:

"Lezione_06_31.gif"

e può essere successivamente recuperata:

"Lezione_06_32.gif"

Possono essere forniti nuovi dati:

"Lezione_06_33.gif"

"Lezione_06_34.gif"

o la rete puà essere ancora un po' più addestrata:

"Lezione_06_35.gif"

Ulteriori prove

Facciamo qualche ulteriore esperimento con più o meno rumore. Qual è il limite?

Ad esempio, aggiungiamo questo rumore:

"Lezione_06_36.gif"

"Lezione_06_37.gif"

Aggiungiamo un po' di rumore dall'intervallo [-0.1,0.1]:

"Lezione_06_38.gif"

"Lezione_06_39.gif"

"Lezione_06_40.gif"

"Lezione_06_41.gif"

La rete riconosce questo tipo di lettera? Quanto rumore possiamo ancora aggiungere?

Noterete che l'unità di output è binaria. Cioè, dice che abbiamo una cosa o un'altra. È possibile aggiungere un'altra lettera per il training set (ad esempio, una 'X'), dargli un valore di riconoscimento (ad esempio 0.5) e addestrare la rete. Che cosa succede ora? Quante lettere pensiamo sia possibile far riconoscere alla rete (dato il vincolo di 3×3 pixel, ovviamente)? Quanto ancora fino a quando avremo bisogno di aggiungere ulteriori unità di output?

Immagini più grandi

Siamo un po' più creativi. Le T e le C sono divertenti, ma che dire del riconoscimento di immagini di tipo più sofisticato? Vediamo se possiamo ottenere una semplice rete per riconoscere la differenza tra le immagini di persone. Dopo tutto, noi esseri umani siamo molto bravi a farlo, e ci sono tipi di danno cerebrale (prosopagnosia), che suggeriscono che abbiamo alcuni meccanismi speciali nel nostro cervello per riconoscere i volti.

Questo processo è alquanto simile al problema T-C. Abbiamo bisogno di qualche materiale per addestrare la rete. Utilizzeremo Mathematica per importare diverse immagini del musicista jazz Sun Ra e diverse immagini di altri musicisti. Addestreremo la rete (si spera) per discriminare tra i vari personaggi.

Il codice seguente carica le immagini che utilizzeremo per addestrare il modello. Utilizziamo il comando Mathematica Import che restituisce un oggetto Graphics. Il primo elemento di tale oggetto contiene i dati raster dell'immagine (i valori dei 190×190 pixel), e nel primo elemento della struttura dati raster ci sono i data veri e propri, ciò cui siamo interessati per alimentare l'algoritmo di apprendimento.

"Lezione_06_42.gif"

Le variabili dsunra e dnosunra contengono i dati delle immagini (livelli di grigio), 4 ciascuna:

"Lezione_06_43.gif"

"Lezione_06_44.gif"

Eccellente, le prime quattro sono il nostro eroe e la parte inferiore contiene altri musicisti. Useremo queste immagini per l'addestramento e quindi proveremo altre immagini per vedere come la nostra rete ha imparato.

La seguente funzione controlla la distribuzione dei livelli di grigio nei pixel di un'immagine:

"Lezione_06_45.gif"

Verifichiamolo per la prima immagine:

"Lezione_06_46.gif"

"Lezione_06_47.gif"

Per costruire il training set abbiamo bisogno di una semplice lista di valori di input e di output per ciascuna immagine. Continueremo ad utilizzare il range (0,1) per tutti i valori. Ciò significa che avremo bisogno di 'appiattire' mediante la funzione Flatten i dati di input per farne un'unica lista, e dividere per 255 poichè i dati dell'immagine sono nell'intervallo [0,255]:

"Lezione_06_48.gif"

Imposteremo una rete con 100 unità nascoste (un numero dettato esclusivamente dall'esperienza):

"Lezione_06_49.gif"

E ora diamo il via all'addestramento (che dura un bel po'):

"Lezione_06_50.gif"

Salviamo l'informazione di stato in modo da poterla riutilizzare in seguito:

"Lezione_06_51.gif"

Possiamo rileggerla in qualsiasi momento:

"Lezione_06_52.gif"

Diamo un'occhiata agli errori:

"Lezione_06_53.gif"

"Lezione_06_54.gif"

Potremo migliorare in seguito l'errore. Controlliamo ora la capacità previsionale della rete:

"Lezione_06_55.gif"

"Lezione_06_56.gif"

che sembra funzionare (ricordiamo che 0.9 è "Sun Ra" e 0.1 è "qualcos'altro").

Ora la parte difficile. Leggiamo qualche altra immagine e classifichiamole:

"Lezione_06_57.gif"

"Lezione_06_58.gif"

"Lezione_06_59.gif"

"Lezione_06_60.gif"

Ora incrociamo le dita - vediamo se la rete individua le differenze:

"Lezione_06_61.gif"

"Lezione_06_62.gif"

I risultati non sono molto soddisfacenti: evidentemente la rete deve ancora apprendere, è stata addestrata con troppo pochi esempi! Infatti, mentre ha quasi del tutto riconosciuto la seconda immagine, ha dedotto in maniera errata che Freud è un musicista jazz (!); inoltre, la rete è confusa sull'immagine del pollo (la quarta) e su quella di Marvin Minsky (la sesta). I risultati per la prima e la terza rappresentano dei falsi negativi.

Risulta chiaro che il training set è un po' disperso: abbiamo forse bisogno di più prototipi del nostro musicista. Inoltre, dovremmo anche provare a far riconoscere immagini ruotate o trasposte per valutare la capacità di riconoscimento della rete.

Approfondimenti

Potremmo modificare il training set per fargli usare anche immagini ruotate e riflesse.

"Lezione_06_63.gif"

"Lezione_06_64.gif"

"Lezione_06_65.gif"

"Lezione_06_66.gif"

Cosa succede se si aggiungono queste immagini al training set?

E cosa succede se riduciamo l'immagine ad un quarto, facciamo la media dei valori nei quattro quadranti e passiamo questa informazione alla rete? Sarà sufficiente questa informazione 'impoverita' per riconoscere la differenza tra i due tipi di immagini?

"Lezione_06_67.gif"

"Lezione_06_68.gif"

"Lezione_06_69.gif"

"Lezione_06_70.gif"

"Lezione_06_71.gif"

"Lezione_06_72.gif"

"Lezione_06_73.gif"

"Lezione_06_74.gif"

"Lezione_06_75.gif"

"Lezione_06_76.gif"

N.B. Il comando Reverse è necessario per rimettere le immagini nell'orientamento giusto...

"Lezione_06_77.gif"

"Lezione_06_78.gif"

"Lezione_06_79.gif"

"Lezione_06_80.gif"

Bibliografia

   [1]  Freeman, JA. 1994, Simulating Neural Networks with Mathematica, Addison-Wesley, Reading MA.

   [2]   Pratt, W. 1978, Digital Image Processing, John Wiley & Sons, New York.

Appendice

Utilità

Imposto la directory corrente:

"Lezione_06_81.gif"

Disattivo i warnings

"Lezione_06_82.gif"

Spikey Created with Wolfram Mathematica 7.0