Introduzione alle reti neurali artificiali

Page 1

INTRODUZIONE ALLE RETI NEURALI ARTIFICIALI Matteo Tosato 2010/2011 Revisione 3.0


2

Sommario 

Introduzione storica...................................................... 4

-

Ciò che l’occhio della rana comunica al cervello della rana, da Kant alle reti neurali artificiali. .................................................... 4

-

Anni 40’: il sogno booleano ............................................. 4

-

Anni 50’: l’età dell’oro del sogno booleano ............................. 5

-

Anni 80’: Il risveglio del sogno di Boole ............................... 8

-

Ringraziamenti .......................................................... 9

Aspetti biologici........................................................ 10

Il neurone artificiale................................................... 13

-

Considerazioni principali .............................................. 13

-

Interpretazione vettoriale ............................................. 19

Apprendimento del perceptron............................................. 20

-

La regola di Hebb ...................................................... 21

-

La regola postsinaptica ................................................ 22

-

La regola presinaptica ................................................. 22

-

La regola delta ........................................................ 22

-

Valutazione delle disuguaglianze ....................................... 27

Reti neurali MLP (Multi layer perceptron)................................ 29

-

Reti neurali a due strati (M-Adeline) .................................. 30

-

Architetture Feed-forward .............................................. 32

-

Feed-forward ricorrenti ................................................ 34

-

Error back-propagation (EBP) ........................................... 35

-

Migliorie per l’algoritmo EBP .......................................... 42

-

Algoritmo Resilient back-propagation (RPROP) ........................... 46

-

Reti di Hopfield ....................................................... 48

Reti neurali auto-organizzanti........................................... 51

-

Mappe di Kohonen ....................................................... 51

Esempio di rete SOM ................................................. 56 

Competitive learning reale ............................................. 63 Problemi e strategie..................................................... 67

-

Neuro-fuzzy ............................................................ 67

-

Reti ART ............................................................... 69

Metodologie di progettazione............................................. 70

Il problema dei dati in ingresso ....................................... 73 Esempi di applicazioni pratiche.......................................... 74

-

Intrusion detection .................................................... 74

-

Previsione di fenomeni complessi ....................................... 84

Matteo Tosato - Introduzione alle reti neurali artificiali


3

-

Analisi di segnale ..................................................... 86

Reti di reti neurali..................................................... 88

Conclusione e ringraziamenti............................................. 89

Appendice A – basi matematiche........................................... 90

Appendice B – Riferimenti................................................ 98

Appendice C – Aneuro32................................................... 99

Matteo Tosato - Introduzione alle reti neurali artificiali


4

Introduzione storica a cura di Andres Reyes

Ciò che l’occhio della rana comunica al cervello della rana, da Kant alle reti neurali artificiali.

Figura 1: Walter Pitts

Walter Pitts nacque a Detroit il 23 aprile del 1923 e all'età di 15 anni scappò di casa perché il padre voleva abbandonasse gli studi per il lavoro. Da qui la storia prosegue con un aneddoto che amava raccontare il neuro-fisiologo Warren McCulloch (1898-1969) . Il giovane Pitts arrivato a Chicago passò le sue ore nel parco vicino all'università conversando di filosofia e logica con Bert. Ignaro in realtà che fosse il famoso matematico Bertrand Russell (1872-1970) ospite alla University of Chicago nell'anno accademico 1938-39 per un seminario. Sempre secondo McCulloch, Russell consigliò a Pitts di leggere “La costituzione logica del mondo” (1928) del logico Rudolf Carnap (1891-1970) membro del circolo di Vienna, che si trovava a Chicago per il seminario di Russell. Pitts si accorse che il libro presentava un errore ed andò a trovare Carnap in ufficio all'università, ma non essendosi presentato solo mesi dopo Carnap seppe chi era il giovane e per aiutarlo gli trovò un lavoro presso l'università.

Anni 40’: il sogno booleano Nel 1943 il giovane Pitts e McCulloch proposero uno studio pionieristico "A logical calculus of the ideas immanent in nervous activity" sulle reti neurali artificiali (ANN) dimostrando che l'algebra di Boole poteva essere applicata allo studio delle reti neurali biologiche , studiando come 1 (eccitato) e 0 (inibito) l'attività del neurone.

Matteo Tosato - Introduzione alle reti neurali artificiali


5 Inserendosi così negli intenti, già definiti dal matematico inglese George Boole fin dal 1854, “di ricercare le leggi fondamentali di quelle operazioni dello spirito mediante le quali si attua il ragionamento, dare loro un'espressione nel linguaggio simbolico del calcolo, e costruire su questo fondamento la scienza della logica e il suo metodo”. Boole (1815-1864) riteneva che le sue ricerche fossero più un contributo alla psicologia che alla matematica e che leggi da lui scoperte fossero realmente quelle del pensiero perché dimostravano che la logica proposizionale e quella sillogistica aristotelica erano due aspetti della stessa realtà. Bastava infatti sostituire "1 = vero" e "0 = falso" come due insiemi che rappresentano il Tutto ed il Nulla. Infine l'ingegnere Claude Shannon (1916-2001) nella sua tesi del 1937 “A Symbolic Analysis of relay and Switching Circuits” evidenziò come l'algebra di Boole poteva rappresentarsi anche come un interruttore aperto (0) o chiuso (1) mettendo così le basi per la costruzione dei computers. L'analogia tra circuiti elettrici e neuronali permetteva quindi di pensare metaforicamente al cervello come a un computer biologico (wetware) e al computer come un cervello elettronico permettendo così di risolvere il dualismo cartesiano in chiave materialista. Inoltre trasformava le reti neuronali in una macchina di Turing (TM) portando all'identificazione dei processi cognitivi con quelli formali algoritmici di una macchina dando così la possibilità di riprodurre meccanicamente la coscienza come auspicato dalla nascente ricerca sull'intelligenza artificiale . Posizione che sarebbe stata criticata negli anni '80 dal filosofo americano John Searle con l'esperimento mentale della stanza cinese (“Menti, cervelli e programmi”,1980) e da Thomas Nagel con la celeberrima frase: ”Che cosa si prova ad essere un pipistrello?”. Per Searle,l'approccio computazionale fornisce solo un una dottrina formale del funzionamento della mente,parla di operazioni mentali e di processi, ma solo raramente di contenuti. Mentre sembra che l'intenzionalità sia una caratteristica della coscienza, cioè la sua necessità di pensare qualcosa, vedere qualcosa, immaginare qualcosa. Con questa metodologia dunque non è possibile spiegare la questione dei qualia, le qualità soggettive degli stati di coscienza, ciò che il filosofo americano Thomas Nagel ha definito come l'effetto che fa essere un determinato tipo di essere.

Il modello proposto da McCulloch e Pitts mancava però di una caratteristica fondamentale: la possibilità di apprendere. Contributo che giunse nel 1949 dallo psicologo canadese Donald Hebb (1904-1985) che propose nella sua opera “L'organizzazione del comportamento” un semplice meccanismo di apprendimento: “quando un assone della cellula A prende parte ripetitivamente nel processo di eccitamento della cellula B, qualche cambiamento strutturale o metabolico subentra in una o entrambe le cellule in modo che l'efficienza di A, come cellula eccitatrice di B, aumenti”.

Anni 50’: l’età dell’oro del sogno booleano

Matteo Tosato - Introduzione alle reti neurali artificiali


6 Alla fine degli anni '50 vennero pubblicate tre opere che incentivarono ulteriori sviluppi delle reti neurali artificiali : “Pandemonium: A paradigm for learning” (1958) di Selfridge, Il Perceptron (1958) di Rosenblatt e l'Adaline di Windrow e Hoff. L'idea di Pandemonium, termine coniato dal poeta inglese John Milton nel “Paradiso perduto”(1667), secondo Oliver Selfridge “era quella di avere un gruppo di demoni che davano la voce a quelli del livello superiore, e questi a quelli di un livello ancora superiore” cioè di disporre di diverse reti neurali , appunto di demoni, semiindipendenti che comunicavano il loro output per ogni singola proprietà per esempio di un volto o di una parola (Pattern Recognition).

Figura 2: Modello del perceptron

L'idea dei demoni venne ripresa nel 1985 da Marvin Minsky come modello della mente come società di multi-agenti (“La società della mente”,1985). Nel 1958 lo psicologo Frank Rosenblatt scrisse “The Perceptron, a Probabilistic Model for Information Storage and Organization in the Brain” rifiutando l'uso della logica simbolica di Pitts e McCulloch a favore di metodi probabilistici. Il perceptron risultava quindi un classificatore e riconoscitore di schemi che simulava la visione umana. Nel loro saggio “Adaptive Switching Circuits” (1960) Bernard Windrow e Marcian Hoff introdussero una rete simile al perceptron, chiamata Adaline (Adaptive Linear Element) ed in seguito la Madaline (Multiple Adaline) la prima rete ad essere applicata nel mondo reale per la riduzione dell'eco nelle linee telefoniche. La principale differenza con il modello di Rosenblatt era l'algoritmo di apprendimento basato sulla minimizzazione della somma dei quadrati degli errori nelle sinapsi (delta rule) e sulla somministrazione di esempi per ottenere l'output desiderato (apprendimento supervisionato). In questo contesto ritroviamo anche l'esperimento del 1959 condotto da Jerome Lettvin (1920-2011), dal neuroscienziato cileno Humberto Maturana, da McCulloch e Pitts sulla percezione visiva della rana: “Ciò che l'occhio della rana comunica al cervello della rana”.

Matteo Tosato - Introduzione alle reti neurali artificiali


7

Figura 3: Pitts e Lettvin osservando una rana

Il rapporto fra vista e percezione, fra il vedere ed il decodificare un' immagine, è da sempre uno degli argomenti alla base delle questioni legate all'esperienza cosciente. Lo scopo dell'esperimento, richiamandosi alla “Critica della ragion pura” (1787) del filosofo tedesco Immanuel Kant (1724-1804), era di dimostrare sperimentalmente le "basi fisiologiche del sintetico a priori" cioè all'esistenza di filtri naturali che selezionano i contenuti dell'esperienza. Se ad esempio una mosca passasse davanti all'occhio della rana questa verrebbe percepita e di riflesso mangiata però se dinanzi alla rana poniamo un oggetto statico, anche una mosca uccisa dallo sperimentatore, non se la mangerebbe, non perché lo decida, ma perché non la vede! L'immagine della mosca si forma nella retina della rana però l'informazione non viene elaborata dal cervello. Le opere di Rosenblatt, Windrow e Hoff stimolarono numerose ricerche tuttavia l‟entusiasmo cessò nel 1969 con il saggio “Perceptrons: An Introduction to Computational Geometry” di Marvin Minsky e Seymour Papert in cui si dimostravano i limiti del perceptron come l'impossibilità di realizzare funzioni linearmente separabili come la Xor. Infatti ciò avrebbe richiesto l'addestramento dei neuroni nascosti (Hidden Layer).

Matteo Tosato - Introduzione alle reti neurali artificiali


8 Termina così per più di un decennio ciò che il matematico Douglas Hofstadter , autore di “Godel, Escher e Bach”, aveva definito come l'età dell'oro del sogno booleano dell'intelligenza artificiale.

Figura 4: Marvin Minsky e Seymour Papert

Anni 80’: Il risveglio del sogno di Boole Nel 1982 il finlandese Teuvo Kohonen, ispirandosi alla topologia della cervello, introdusse un nuovo tipo di ANN le SOM (self-organizing map) sviluppare un comportamento auto-organizzante senza l‟addestramento da supervisore, attraverso l‟eccitazione di neuroni vicini e l'inibizione lontani.

corteccia del in grado di parte di un di quelli

Nel 1986 venne introdotto, grazie alle pubblicazioni degli psicologi David Rumelhart, Geoffrey Hinton e Ronald Williams, l‟algoritmo di retro-propagazione (Error BackPropagation EBP), capace di addestrare anche il livello di neuroni nascosti (hidden layer) attraverso una modifica sistematica degli errori tra i nodi, superando così le critiche degli anni 60'. Bisogna però ricordare che tale algoritmo era già stato pensato nel 1974 da P. Werbos nella sua tesi di dottorato. Inoltre David Rumelhart (1942-2011) ed il suo collega James McClennand pubblicarono in due volumi “Parallel Distributed Processing” (PDP) rilanciando così il programma connessionista. Proponevano quindi tre elementi predominanti nella riproduzione dell'attività cognitiva attraverso le ANN: il processamento in parallelo, memoria distribuita ed adattabilità. Al contrario di come avveniva nell'intelligenza artificiale: processamento sequenziale, memoria localizzata ed istruzioni imperative. Il connessionismo definiva l'intelligenza come apprendimento e non più come mera elaborazione e programmazione di simboli infine come nel cervello i dati venivano “evocati” e non “cercati”.

Matteo Tosato - Introduzione alle reti neurali artificiali


9 Ulteriore successo del connessionismo fu che Rumelhart e McClennand resero disponibili per anni i sorgenti dei loro programmi agli studenti delle universitĂ dove insegnavano esortandoli a testarli e a modificarli.

Ringraziamenti Vorrei ringraziare il filosofo e neuro-psicologo Marco Mozzoni , direttore della rivista di neuroscienze BrainFactor, per l'aiuto datomi nella stesura di questo breve articolo introduttivo alle reti neurali. Andres Reyes.

Matteo Tosato - Introduzione alle reti neurali artificiali


10

Aspetti biologici Il cervello, È l‟oggetto più complesso e misterioso che si conosca: 1.300-1.500 grammi di tessuto gelatinoso composto da 100 miliardi di cellule (i neuroni), ognuna delle quali sviluppa in media 10 mila connessioni con le cellule vicine. Durante la vita fetale l‟organismo produce non meno di 250 mila neuroni il minuto. Ma 15-30 giorni prima della nascita, la produzione si blocca e per il cervello comincia una seconda fase che durerà per tutta la vita: la creazione di connessioni tra le cellule. In questo processo, le cellule che falliscono le connessioni vengono eliminate, tanto che al momento della nascita sono già dimezzate. Il cervello umano (più correttamente "encefalo") è il risultato della sovrapposizione dei tre tipi di cervello apparsi nel corso dell‟evoluzione dei vertebrati. Dal basso (alla base del cranio), il cervello più antico, o romboencefalo, specializzato nel controllo di funzioni involontarie come vigilanza, respirazione, circolazione e tono muscolare. Comprende il cervelletto e le parti del midollo spinale che si allungano nel cervello. Salendo, c‟è il mesencefalo: una piccola porzione di tessuto nervoso costituita dai cosiddetti peduncoli cerebrali e dalla lamina quadrigemina. Infine c‟è il prosencefalo, la parte più "moderna", suddiviso in diencefalo e telencefalo. Il primo, chiamato anche "sistema limbico", contiene strutture come talamo, ipotalamo, ipofisi e ippocampo, da cui provengono sensazioni come fame, sete o desiderio sessuale. Infine, la parte più recente in assoluto: la corteccia, dove hanno sede le funzioni intelligenza e linguaggio. Ma veniamo ai mattoni del cervello, i neuroni: cellule specializzate nel raccogliere, elaborare e trasferire impulsi nervosi. Dal loro corpo cellulare si diramano vari rametti, i dendriti, e un ramo più grosso, l‟assone. I primi ricevono i segnali in arrivo, il secondo conduce i messaggi in uscita. Grazie a dendriti e assoni, il numero totale delle connessioni che i neuroni di un cervello umano riescono a stabilire supera il numero di tutti i corpi celesti presenti nell‟universo. L‟esistenza di queste connessioni, o sinapsi, fu scoperta alla fine del XIX secolo dal fisiologo inglese Charles Scott Shemngton, anche se non si tratta di connessioni fisiche perché tra due neuroni s‟interpone sempre una microscopica fessura. Per superare questo varco, i segnali cambiano faccia: da elettrici, diventano chimici. La terminazione dell‟assone rilascia sostanze, dette neurotrasmettitori, che sono raccolte dagli appositi recettori presenti sulla membrana della cellula-obiettivo. Catturato il neurotrasmettitore, il messaggio chimico viene riconvertito in impulso elettrico. Per rendere il viaggio più veloce, sull‟assone l‟impulso procede a balzi. L‟assone, infatti, è ricoperto da un materiale isolante chiamato guaina mielinica, che però lascia scoperti alcuni punti: i nodi di Ranvier. E saltando da un nodo all‟altro, l‟impulso raggiunge i 400 km/h.

Matteo Tosato - Introduzione alle reti neurali artificiali


11 "Il cervello ha alcuni punti di contatto con i computer, ma anche una differenza essenziale: è "plastico" Che cosa significa? Che ogni volta che lo usiamo, si modifica". Proprio qui volevo arrivare dove l'informatica di distrae e cambiando direzione incontra la neurologia. Abbiamo già visto che due neuroni, per comunicare, si scambiano sostanze chimiche che li inducono a generare particolari impulsi elettrici. Immaginate di ripetere questo processo milioni, miliardi di volte e avrete descritto, anche se in maniera semplificata, il trasferimento di un‟informazione (visiva, acustica...) all‟interno di un circuito neuronale del cervello umano. Vediamo un caso semplice. immaginiamo per esempio di cogliere un fiore mai visto prima e caratterizzato da un profumo piacevolissimo. Questo tipo di informazione viaggerà dalla mucosa olfattiva (la parte interna del naso che "sente" gli odori), lungo il nervo olfattivo, fino alla parte della corteccia cerebrale organizzata per analizzare e comprendere i profumi. Nel fare ciò, l‟informazione attraverserà un numero enorme di sinapsi creando l‟equivalente di un "sentiero" neuronale. Al ripetersi dell‟esperienza, l‟informazione viaggerà nuovamente lungo lo stesso percorso rinforzandolo ancora di più, proprio come il passaggio di molte persone in un bosco crea un sentiero. Una cosa però è certa: alla base della memoria c‟è la plasticità neuronale. Con queste parole si definisce l‟abilità del cervello di plasmare se stesso attraverso il continuo rimodellamento delle sinapsi vecchie e la creazione di sinapsi nuove. Il cervello è infatti in costante rimodellamento, ed è proprio per questo che si deve mantenerlo sempre in esercizio per garantirne l‟efficienza. Certo, è legittimo pensare che l‟apprendimento sia qualcosa di più della ristrutturazione di un certo numero di sinapsi... ma esiste una prova concreta che senza la plasticità neuronale non saremmo più capaci di apprendere. Concentriamoci su quella che è la struttura del neurone biologico, che abbiamo visto essere struttura fondamentale.

Figura 5: Neurone

Le sinapsi quindi comprendono sia l'assone che i dentriti, possono essere eccitatorie oppure inibitorie a seconda della loro capacità di trasporto del segnale. Il neurone, dato che può emettere o meno un segnale elettrico avrà anche una soglia di attivazione. Fin quando la membrana del neurone resta indisturbata non si origina alcun potenziale d‟azione, ma se un qualsiasi evento provoca un sufficiente aumento del potenziale del livello di -90mV verso il livello zero, è lo stesso voltaggio in aumento Matteo Tosato - Introduzione alle reti neurali artificiali


12 che fa si che molti canali del sodio voltaggio dipendenti comincino ad aprirsi. Ciò permette un rapido ingresso di ioni sodio, che provoca ancora un nuovo aumento del potenziale di membrana, che fa aprire un numero ancora maggiore di canali del sodio accrescendo il flusso di ioni sodio che entrano nella cellula. Il processo si autoalimenta con un circolo vizioso di feedback positivo fino a che tutti i canali del sodio non risultano totalmente aperti. Ma a questo punto in una successiva frazione di millisecondo il potenziale di membrana in aumento provoca una chiusura dei canali del sodio e una apertura di quelli del potassio, dopodiché il potenziale d‟azione termina. Perché si inneschi il potenziale d‟azione è necessario che il potenziale di membrana aumenti di 15/30mV, portando quest‟ultimo a circa -65mV (soglia di eccitazione). Quindi non sempre è detto che la soglia di attivazione viene raggiunta, dipende proprio dalla velocità del processo suddetto.

Figura 6: Attivazione neurone

Questo potenziale è diffuso lungo tutta la struttura neuronale, anche alle estremità delle sinapsi la dove il segnale elettrico diventa chimico per passare al neurone interconnesso. Se questo si verifica la sinapsi sarà quindi di eccitazione, altrimenti di inibizione. questo è quanto ci conviene sapere per comprendere come funziona il neurone, esistono poi altre importanti questioni che si potrebbe citare per quanto riguarda tutta la parte chimica, il neurone è in effetti un sistema pompa sodio-potassio. I neuroni interconnessi formano una rete neurale, questa nel corso del tempo e delle esperienze si modifica adattandosi alla nuova necessità di risolvere sempre problemi differenti e prendere decisioni diverse. Le ricerche riguardo il funzionamento del cervello hanno fatto grandi passi in avanti negli ultimi anni, diversi sono i progetti interessanti ai quali attualmente si sta lavorando in tutto il mondo; c‟è chi ha come obiettivo quello di costruire un cervello artificiale e chi mette a punto tecniche sempre più sofisticate per l‟analisi del suo funzionamento. La seguente immagine mostra una mappa del cervello ricostruita al computer.

Matteo Tosato - Introduzione alle reti neurali artificiali


13

Figura 7: Mappa 3D del cervello (non completa)

Il neurone artificiale Considerazioni principali Che cosa ha fatto l‟informatica dunque? Bè nulla di speciale parere personale, ha spudoratamente copiato dalla Natura il trovato nel neurone biologico, le equazioni che descrivono i generatori di elettricità furono scoperte da due premi Nobel

se posso esprimere un principio di funzionamento neuroni come dei di Cambridge.

Per applicare tale principio, il neurone è stato di molto semplificato e adattato. Nonostante questo le sue proprietà fondamentali sono state conservate. La struttura base è costituita da „i‟ ingressi, una funzione di attivazione „Ө‟, una uscita „y‟, un peso „w‟ per ogni sinapsi ed un valore soglia „‫ ‟ט‬il quale servirà eventualmente per normalizzare gli input.

Matteo Tosato - Introduzione alle reti neurali artificiali


14

X1 W1

X2 W2

. . .

P

Ө(P)

Y

Wi

Xi Figura 8: Neurone artificiale schematizzato

Il numero degli ingressi può variare da un minimo di 1 ad un valore positivo intero qualunque. In figura, ciò che ho indicato con la lettera P all‟interno del neurone è il suo potenziale. Questo potenziale si ottiene dalla sommatoria dei valori in input tenendo conto del peso sinaptico ad essi associata. Definiamo quindi la seguente formula per il potenziale P:

∑(

)

Questo potenziale non è il valore trasferito al neurone successivo ma solo un valore intermedio che verrà utilizzato della funzione al momento del trasferimento. La variabile „n‟ rappresenta ovviamente il numero di input presenti. La variabile soglia , che viene sottratta ogni volta che l‟input viene moltiplicato al peso sinaptico, ha lo scopo di normalizzare tale input in modo che rientri in un valore compreso tra -1 e 1. Ma questo non sempre viene fatto, spesso i dati sono normalizzati prima di essere inseriti in ingresso. Difficilmente una rete neurale può lavorare con input molto grandi e nello scopo dell‟utilizzo avrebbe poco senso. Pensiamo a una immagine la quale ridotta in pixel viene analizzata attraverso una rete neurale, ogni pixel può benissimo avere valore 0 o 1, ad indicare colore o bianco. Il trasferimento, in questo caso, coincide anche con il valore di uscita Y definito dalla formula seguente:

( ∑(

))

Ci resta ora da definire la funzione di trasferimento . Essa dipende strettamente con quello che la rete neurale dovrà fare e dal tipo di architettura scelta. Di seguito presento le funzioni di utilizzo più comune. (Riporto grafico e sintassi di matlab). La prima che vediamo è chiamata funzione a gradino o binaria. Vi è solo una lieve differenza fra le due. Questa è utilizzata nelle configurazioni semplici quando si ha un solo elettrone ed un solo strato.

Matteo Tosato - Introduzione alle reti neurali artificiali


15

{

e

{

Questa funzione viene utilizzata quando è prevista un‟uscita con valore zero oppure eccitatoria, quindi quando input e output sono booleani. quella a gradino naturalmente varia per il caso con x minore o uguale a zero dove il valore di y è -1. Un‟altra funzione di uso comune è quella lineare anche chiamata identità, la bisettrice del primo e terzo quadrante.

*

Per valori di y crescenti possiamo invece ricorrere alla funzione a saturazione lineare.

{

La funzione sigmoidale è una delle più importanti, specie nelle reti dove viene utilizzato l‟apprendimento back-propagation. Viene chiamata anche funzione logistica. L‟equazione che la descrive è:

Matteo Tosato - Introduzione alle reti neurali artificiali


16

Essa è sempre crescente, è continua su tutto l‟asse dei reali ed è derivabile, vale 1 a +∞ e 0 a -∞, questa funzione è la più utilizzata nelle reti multistrato dove è importante avere risultati nel continuo, però presenta un “problema”, essendoci un esponenziale risulta piuttosto gravosa in termini di calcoli (considerate che ogni neurone ha una funzione di attivazione e le uscite vanno calcolate miliardi di volte). Quando si ha necessità di avere spesso valori in uscita inibitori possiamo utilizzare la tangente iperbolica. La cui equazione è:

In ultimo le “radial basis functions”, simile alla funzione di Gauss:

Ora che conosciamo il neurone artificiale siamo in grado di affrontare le principali configurazioni. I neuroni possono essere combinati in tantissimi modi per ottenere la rete neurale dal comportamento voluto. Nelle reti i neuroni si stimolano reciprocamente, quindi l‟uscita y che abbiamo visto prima diventa l‟input di un altro neurone e via dicendo fino ad arrivare ad una uscita non connessa che rappresenta l‟output dell‟intera rete. La rete come per quelle biologiche, va a modificare i propri pesi sinaptici dei neuroni attraverso algoritmi di apprendimento dipendenti dalla configurazione, in questo modo si possono ottenere comportamenti diversi. L‟output della rete potrà convergere, crescere oppure oscillare. Solitamente una rete utile ha una uscita che converge verso il valore desiderato. Una NN con uscita sempre Matteo Tosato - Introduzione alle reti neurali artificiali


17 crescente si dice che “esplode”, proprio perché la sua uscita diventerà presto intrattabile. Una NN con uscita oscillatoria significa che compie un ciclo ripetitivo, come vizioso. Riassumendo, possiamo dire che una NN è composta da neuroni e che corregge i propri pesi sinaptici ad ogni attivazione di questi. La rete appena creata sarà inservibile per il suo scopo finale, perché avrà pesi sinaptici non corretti. (e questo vale per le configurazioni più utilizzate). Dobbiamo sottoporre tale rete ad un periodo di “training”. Durante questa fase dovremo fornire in input un set di valori e il corretto output, in modo da correggere i pesi in maniera opportuna. La rete in questo modo raggiunge il comportamento desiderato per tutta la classe di input. Una volta che è stata raggiunta la precisione desiderata, i pesi sinaptici vengono congelati e la NN è pronta all‟uso. Le configurazioni più comuni sono NN non ricorrenti, totalmente connesse, a livelli, simmetriche, auto associative, stocastiche e asincrone. Di seguito mostro alcuni modelli. Rete non ricorrente: output

input

In questo tipo di reti le connessioni vanno in un solo senso, dall‟input all‟output. E‟ esattamente il contrario delle reti chiamate “cicliche”.

Rete totalmente connessa:

In questo tipo di rete ogni neurone che la compone è connesso con tutti gli altri, escluso se stesso. Si noti che un peso sinaptico con valore nullo corrisponde all‟assenza di connessione, abbiamo visto prima l‟equazione del potenziale dove input viene moltiplicato al peso, quindi con w = 0, l‟input non viene trasferito. Solitamente in queste reti alcuni pesi vengono inizializzati a zero. Rete a più livelli: (totalmente connessa a più livelli)

Matteo Tosato - Introduzione alle reti neurali artificiali


18 output

input

Reti in cui le unità sono organizzate in insiemi separati e disgiunti di cui generalmente uno è detto di input, un altro di output e gli altri vengono detti “nascosti” oppure “intermedi”. Rete simmetrica:

Nelle reti simmetriche ogni connessione fra due qualsiasi neuroni è uguale in entrambi i sensi: Wij = Wji

Rete auto associativa:

Quest‟ultimo tipo è utilizzato proprio per l‟intelligenza artificiale. Queste hanno il compito di ricevere un segnale in input e farlo evolvere restituendo un output leggermente modificato, proprio quelle che avviene nel cervello umano. Per gli scopi più applicativi, vengono utilizzate quelle a più livelli interamente interconnesse tra questi. A questa poi si dovrebbe aggiungere anche un ulteriore configurazione, ovvero quelle a più livelli ricorrenti. Queste possono avere anche sinapsi che collegano neuroni di uno stesso livello. Matteo Tosato - Introduzione alle reti neurali artificiali


19 Ma la configurazione più semplice è quella che fa uso di un solo neurone. Essa trova impiego oltre ai fini didattici, anche in diversi campi di applicazione. È infatti già in grado di fare alcuni tipi di classificazioni, partiremo da questa e proseguendo, vedremo i suoi limiti rispetto alle configurazioni più avanzate.

Interpretazione vettoriale In questo paragrafo interpretiamo il comportamento del neurone artificiale in chiave vettoriale. Possiamo considerare i valori di input come componenti del vettore pesi sinaptici componenti del vettore . Tale che:

*

+ e

*

, e i valori dei

+

La sommatoria eseguita dal neurone, corrisponde al prodotto scalare dei due vettori:

∑(

è l‟angolo formato dai due vettori.

α

W

Dove

)

X

Secondo le considerazioni appena fatte, il prodotto interno dei due vettori corrisponde alla risposta del neurone. Se immaginiamo di muovere i due vettori, il prodotto interno sarà proporzionale al coseno dell‟angolo

.

Immaginiamo la situazione seguente:

W1 = 0,3

X1 = 0,7

Con pesi normalizzati si ha: Matteo Tosato - Introduzione alle reti neurali artificiali

W2 = 0,8

X2 = 0,3


20

|| |||| || || |||| || La norma del vettore corrisponde alla sua lunghezza: || ||

Il prodotto interno sarà tanto maggiore quanto è minore la distanza dei due vettori all‟interno dello stesso quadrante. Quando la risposta è uguale a 0 i due vettori sono ortogonali fra di loro; quando è maggiore di 90° la situazione è simmetrica e l‟unità assume valori negativi. Prendendo come esempio il neurone con funzione di attivazione binaria, avremo che l‟output può assumere valore attivo (1, vettori con distanza < 90°) o rimanere silente (0, vettori con distanza > 90°). In una rete di molti neuroni è possibile determinare quale neurone possiede valori sinaptici più simili all‟input dato il suo valore di attivazione solo se i valori sono normalizzati, ovvero se la loro somma risulta uguale all‟unità.

Apprendimento del perceptron Il punto di forza assoluto delle ANN (Artificial neural networks) è l‟apprendimento. Secondo Donald Hebb, a livello biologico un collegamento fra due neuroni subisce un rinforzo nel momento in cui si ha una attivazione del rispettivo neurone postsinaptico. Allo stesso modo, nelle reti artificiali avremo un aggiustamento in positivo del valore sinaptico secondo un determinato algoritmo di addestramento.

1

1

0

0

0

1

0

Generalizzando, il processo di addestramento per una ANN si suddivide in 5 fasi, queste sono valide in quasi ogni scenario di addestramento: - Si crea la struttura dati necessaria; si può lavorare ad oggetti o con matrici, quest‟ultimo prevede uno sforzo in più ma è altamente più efficiente e sintetico. - I pesi vengono inizializzati, ad esempio con un valore casuale compreso tra -1 e 1. - Viene eseguita la fase di training. Dovremo fornire alla rete neurale due input: gli ingressi e le uscite (target, ovvero quello che la rete dovrebbe dare in uscita) Si esegue la computazione secondo il principio di attivazione del neurone visto prima e viene confrontato il risultato ottenuto con il desiderato. Matteo Tosato - Introduzione alle reti neurali artificiali


21 - A questo punto vengono calcolate le variazioni dei pesi quindi aggiornati di conseguenza. La fase di training viene ripetuta fino a che la rete non converge al risultato desiderato. - A questo punto la rete è pronta per l‟utilizzo. Ogni ciclo a cui la rete è sottoposta durante la fase di training è detto un‟epoca. Durante un‟epoca vengono presentati tutti gli input del training set. Ad esempio, per un problema come l‟OR logico ne occorreranno davvero poche. L‟algoritmo di apprendimento è quindi iterativo e viene eseguito per ogni esempio. Notare che, in termini più generali, esistono due modalità con le quali correggere i valori sinaptici. La prima, denominata addestramento „batch‟, consiste nell‟aggiornarne i valori dopo la presentazione dell‟intero pattern set, ovvero dopo aver proposto alla rete tutti gli esempi. L‟errore della rete per il pattern set è l‟errore quadratico medio che analizzeremo in seguito. Un‟altra modalità è detta „on-line‟. In questo caso la correzione dei pesi viene eseguita ad ogni iterazione su singolo esempio.

La regola di Hebb Donald Hebb, studioso originale e unico nel panorama della psicologia del „900, fu un precursore di molte teorie e scoperte successive, e fu uno dei primi scienziati ad approfondire il legame tra il sistema nervoso e comportamento. Come abbiamo detto in precedenza, una connessione subisce un rinforzo quando si ha un‟attivazione del suo neurone postsinaptico. Quando si utilizza questo metodo, il valore di inizializzazione per i neuroni è 0. La variazione sinaptica è definita dall‟equazione:

Dove è il tasso di apprendimento (learning rate), questo valore specifica la capacità di apprendimento della rete, può avere un valore compreso tra 0 e 1. Più questo valore è alto più la rete apprenderà velocemente, ma sarà anche meno precisa, è in definitiva, un parametro dinamico per risolvere eventuali problemi di prestazione e precisione in fase di apprendimento. Per ciascuna coppia di pattern

si ha:

∑ Questo metodo rappresenta la base su cui tutti o quasi i successivi metodi di addestramento si sono sviluppati. Nonostante questo essa possiede molti limiti. Ad esempio, data lo sola possibilità di aumentare il valore delle sinapsi, i pattern di input non dovrebbero avere elementi in comune, perché questo causerebbe l‟attivazione di tutti i corrispondenti neuroni attivati in precedenza per qual particolare input. Questo effetto viene definito come “interferenza”. Pertanto la regola di Hebb permette di apprendere solo pattern ortogonali, ovvero pattern la cui somma dei prodotti dei singoli componenti è zero.

Matteo Tosato - Introduzione alle reti neurali artificiali


22

La regola postsinaptica Stent (1987) e Singer (1973), viste le grosse limitazione della regola di Hebb misero a punto un metodo chiamato regola postsinaptica che ha come obiettivo ridurre l‟effetto di “interferenza” prima definito, quando si lavora con input parzialmente sovrapposti. L‟idea sta nell‟aggiunta di una regola oltre quella di Hebb, la condizione permette di diminuire il valore delle sinapsi quando l‟unità postsinaptica è attiva e l‟unità presinaptica è inattiva:

(

(

) )

Nonostante l‟introduzione di questa regola riduca l‟effetto interferenza, essa tende a creare sinapsi inibitorie quindi non è in grado di apprendere correttamente in caso di input sovrapposti parzialmente.

La regola presinaptica La regola presinaptica prevede una condizione simmetricamente opposta alla regola di Stent-Singer. Il valore della sinapsi viene diminuito quando l‟unità postsinaptica è inattiva e quella presinaptica è attiva:

(

(

) )

Questa funzione meglio della precedente quando molti pattern di input diversi parzialmente sovrapposti debbono essere classificati con lo stesso pattern.

La regola delta Ritornando ad una rappresentazione più astratta dei vettori sinaptici e di input, prendiamo in esame un problema come quello dell‟operazione logica OR. Tale operazione prevede due input che possono assumere valore 0 oppure 1. E prevede per l‟uscita medesimi possibili valori. Si ha quindi una tabella di verità del tipo seguente: X1 0 0 1 1

X2 0 1 0 1

Y 0 1 1 1

Tabella 1: Tabella di verità OR logico

La rappresentazione n-dimensionale del problema OR è la seguente:

Matteo Tosato - Introduzione alle reti neurali artificiali


23 X1

(0,1)

(1,1)

(0,0)

(1,0) X2

Quando si parte con pesi sinaptici casuali, equivale ad avere in partenza una retta che non separerà i punti in modo corretto. (sarà una retta con inclinazione e posizionamento casuale) Cambiando l‟inclinazione della retta, ovvero modificando il valore dei pesi sinaptici, possiamo arrivare a separare gli input correttamente. E‟ evidente anche la necessità di utilizzare anche delle soglie, chiamate „bias‟ per poter traslare oltre che ruotare la retta. Uno degli algoritmi notevoli che consente di effettuare questa operazione è appunto la regola delta, anche chiamato algoritmo di addestramento specifico del perceptron. Il principio è semplice, si basa sul calcolo dell‟errore in output. Poi, su questo, si aggiornano i pesi in modo tale da convergere verso il risultato corretto. Definiamo l‟errore con la seguente equazione:

Il delta è l‟errore commesso della rete neurale per l‟esempio k-esimo, „Exp‟ è il valore atteso mentre Y è l‟output dato dalla rete. Da cui troviamo il “delta weight”, ovvero la variazione da applicare al peso:

Dove η è il “learning rate”, X è l‟input k-esimo della rete. Dunque, l‟aggiornamento del peso sinaptico avviene seguendo la regola seguente:

(

)

Quando il “delta error” raggiunge il valore desiderato, i pesi possono essere “congelati”. E la rete è pronta per svolgere il suo lavoro.

Esempio addestramento perceptron tramite delta rule Vediamo una rete a percettrone a livello di programmazione, con mio rammarico molti libri trattano le reti neurali solo in modo teorico, senza fornire sorgenti in linguaggio o pseudo-linguaggio di esempio nemmeno per le più semplici configurazioni. Per questo motivo cerco dove possibile di fornire anche un minimo di materiale esemplificativo. Matteo Tosato - Introduzione alle reti neurali artificiali


24 All‟interno del sorgente, la prima cosa di cui occuparci è definire le strutture dati che compongono la rete neurale. Qui sono possibili approcci diversi. Seguendo un metodo prettamente logico, possiamo definire delle strutture dati o classi che descrivono ogni parte della rete. Ad esempio ragionando ad oggetti, definiremo la classe neurone ed i suoi metodi. Una classe neurone è adatta ad contenere già tutte le possibili funzioni di trasferimento. Poi una classe sinapsi, una classe livello per le reti multilivello e via discorrendo… Oppure, possiamo utilizzare un approccio un po‟ meno ordinato ed utilizzare in modo intelligente le matrici. Utilizzando le matrici si ha un notevole risparmio di risorse e maggiore velocità di elaborazione. Per la fase di training dobbiamo definire un metodo per fornire input e target alla rete. Di norma sono utilizzati file .xml. Infatti la stessa rete neurale è utile per scopi differenti, il file xml prepara questa al lavoro desiderato. Per semplicità noi cominceremo da un file .dat che contiene i dati disposti semplicemente in righe. Per l‟operazione OR ci basterà definire gli input. L‟output possiamo calcolarlo con l‟operazione che fornisce il linguaggio. Se vi state chiedendo che utilità ha un rete neurale che fa un‟operazione come l‟OR, già fornita dalla comodissima operazione C “|”, vi rispondo che non serve a nulla, la presento al sol scopo di capire come sia possibile impartire al calcolatore istruzioni sequenziali per descrivere una logica, in comprensivo, non sequenziale.

#include #include #include #include

<stdio.h> <stdlib.h> <string.h> <time.h>

// Uncomment follow line to avoid debug messages #define _DEBUG_ #define FAILURE 1 #define SUCCESS 0 // Define neurons #define INPUT_N 2 #define OUTPUT_N 1 // Set precision #define PRECISION double // Define Learning rate #define _L_RATE 0.5 // Define network desired accuracy #define ACCEPTABLE_DELTA 0.05 // Training file #define FTRAINING_PATH "H:\\AI_sources\\Windows\\Learning\\test_nn01\\bin\\Debug\\training.dat" // Perform training int __n_network_training(const char*,unsigned int); // Neural network weights PRECISION weights[INPUT_N][OUTPUT_N]; PRECISION pot; int input[INPUT_N]; PRECISION output[OUTPUT_N];

La matrice weights rappresenta tutti i pesi del percettrone che saranno solamente due dati i soli due input. Come vedete ho definito anche il file training.dat il quale Matteo Tosato - Introduzione alle reti neurali artificiali


25 contiene gli input 0 e 1 nelle loro combinazioni. La funzione __n_training() si occupa dellâ€&#x;addestramento della rete. La funzione è la seguente:

int __n_network_training(const char* path,unsigned int epochs) { // Checks... if(epochs <= 0) return FAILURE; if(path <= 0) return FAILURE; int input[2]; PRECISION exp_output,netout,pot,delta_err,delta[INPUT_N]; char*n = malloc(16); char*nptr = n; unsigned int k = 0, persistant_accuracy = 0; input[0] = -1; input[1] = -1; // Get training set from file FILE* fp = fopen(path,"r"); if(fp == NULL)return FAILURE; char c = 0; while(c != EOF || epochs > 0) { while(input[0] == -1 || input[1] == -1) { c = (char)fgetc(fp); if(c == ';') { input[k] = atoi(n); (k == 1) ? k = 0 : k++; n = nptr; } else { sprintf(++n,"%c",c); } } // Learn OR operation exp_output = input[0] | input[1]; // Potential of output neurons pot = weights[0][0] * input[0]; pot += weights[1][0] * input[1]; // Net output (with linear transfer function) netout = (pot > 0.0) ? 1.0 : 0.0; // Compute delta error delta_err = exp_output - netout; // Compute delta delta[0] = delta_err * _L_RATE * input[0]; delta[1] = delta_err * _L_RATE * input[1]; // Update weigths weights[0][0] += delta[0]; weights[1][0] += delta[1]; // Check neural network accuracy if(persistant_accuracy >= 10) { break; } else { if(delta_err < ACCEPTABLE_DELTA) { persistant_accuracy++; } else persistant_accuracy = 0; } #ifdef _DEBUG_ Matteo Tosato - Introduzione alle reti neurali artificiali


26 printf("in: %d - %d out: %lf\n",input[0],input[1],netout); #endif // Restore input[0] = -1; input[1] = -1; // Decrement epochs epochs--; } fclose(fp); free(n); return SUCCESS; }

Dopo le inizializzazioni, un ciclo while() su occupa dell‟addestramento, vengono prelevati i dati dal file di training. Questi sono memorizzati in questo modo: 0;0;0;1;1;0;1;1;0;0;0;1;1;0... E‟ una serie che si ripete. Il risultato target viene calcolato con l‟OR canonico, poi viene calcolato il potenziale, subito dopo la funzione di attivazione restituisce 1 se il potenziale è maggiore di 0, infine viene calcolato il delta error e la variazione post-sinaptica che viene utilizzata poi per l‟aggiornamento dei pesi. La funzione main() è così definita: int main() { char answer[16]; // Init int x,y; for(x=0;x<INPUT_N;x++) { for(y=0;y<OUTPUT_N;y++) { weights[x][y] = ((rand()%2000)/(PRECISION)(1000.0)-(PRECISION)1.0); } } if(__n_network_training(FTRAINING_PATH,-1) == FAILURE) { fprintf(stderr,"[+] FATAL! - Training failure... or epochs not sufficient"); return FAILURE; } else { printf("[+] - Neural network trained!\n"); } while(1) { printf(" - Type first input: "); scanf("%d",&input[0]); printf(" - Type second input: "); scanf("%d",&input[1]); // Calculate potential of output neuron pot = weights[0][0] * (PRECISION)input[0]; pot += weights[1][0] * (PRECISION)input[1]; // Net output (with linear transfer function) output[0] = (pot > 0.0) ? 1.0 : 0.0; printf(" - Network output: %f\n",output[0]); printf("continue s/n?"); scanf("%16s",answer); Matteo Tosato - Introduzione alle reti neurali artificiali


27 if(strcmp(answer,"s")) { break; } } return SUCCESS; }

Prima vengono iniziati i pesi ad un valore casuale nel range -1:1. Poi viene chiamata la funzione di addestramento. A seguito viene chiesto all‟utente di inserire gli input, la rete calcola il risultato correttamente. La rete neurale è inoltre in grado di restituire valori corretti anche in presenza di input non pulito (rumore in input). Ad esempio provate ad inserire 0.90 al posto di 1 oppure 0.20 al posto di 0. La rete restituisce comunque output coerenti, dato che la funzione di attivazione fornisce una certa “tolleranza” d‟errore, Le reti neurali fanno quindi parte delle tecnologie dette a “logica fuzzy”. Più utilizziamo un learning rate basso, più tempo la rete ci metterà ad apprendere, più sarà in grado di restituire output corretti anche in presenza di rumore.

Valutazione delle disuguaglianze Il metodo che vedremo di seguito viene chiamato “valutazione delle disuguaglianze”. Lo scopo di questo metodo sta nel poter valutare a priori, la dove è possibile, se stiamo affrontando un problema che possiede delle soluzioni “linearmente separabili”. Ovvero se esiste un retta in grado di separare gli input. Come è avvenuto nell‟esempio OR oppure no. Partiamo da un esempio, la tabella che segue mostra input ed output del “problema di parità”. Qui abbiamo 3 input, quindi un neurone tridimensionale. L‟output dipende dal numero di ingressi posti ad uno. Quando essi sono dispari l‟output del neurone deve dare uno, quando sono pari 0. X1 0 0 0 0 1 1 1 1

X2 0 0 1 1 0 0 1 1

X3 0 1 0 1 0 1 0 1

Y 0 1 1 0 1 0 0 1

Tabella 2: Tabella di verità del problema di parità

Abbiamo quindi due classi A e B.

*(

)(

)(

)(

)+

*(

)(

)(

)(

)+

Siccome non abbiamo un numero di input eccessivo, possiamo ancora rappresentare graficamente le soluzioni:

Matteo Tosato - Introduzione alle reti neurali artificiali


28 X1

(0,0,1)

(0,1,1)

(1,0,1)

(1,1,1)

X3

(0,0,0)

(1,0,0)

(1,1,0)

(0,1,0) X2

I pallini bianchi sono l‟output pari a 0. Ora partendo dall‟equazione utilizzata per trovare l‟output della rete Y:

( ∑(

))

Possiamo considerare il valore soglia ‫ ט‬come un ulteriore peso “-W0”. Da questo deriva che:

(

)

A questo punto per ogni serie di argomenti possiamo scrivere la relativa disuguaglianza:

1) 2) 3) 4) 5) 6) 7) 8)

- (0,0,0) – (0,0,1) – (0,1,0) – (0,1,1) – (1,0,0) – (1,0,1) – (1,1,0) – (1,1,1)

Ora qui è sufficiente trovare le contraddizioni per capire che il problema non è risolvibile linearmente. Ne basta una, ad esempio la quarta si contraddice con la numero 2 e 3. Dati n input sono presenti disuguaglianze. Tornando alle due classi A e B e alle loro definizioni, possiamo dire che la prima avrà:

E per la seconda: Matteo Tosato - Introduzione alle reti neurali artificiali


29

Allora la serie di punti per i quali rappresentano un iperpiano bidimensionale in uno spazio tridimensionale che separa le soluzioni del problema se questo è separabile linearmente. Un altro principio molto importante è il teorema di Cover del 1965, di cui non sto a riportare la dimostrazione. Esso afferma che dato un numero di input per l‟addestramento non linearmente separabili, uno può probabilmente trasformare questo in un set linearmente separabile proiettandolo in uno spazio multidimensionale attraverso alcune trasformazioni non lineari. La seguente equazione definisce C come il numero di funzioni a soglia lineari per un problema ad n dimensioni. (n input):

( )

∑(

)

La funzione parziale training di un‟epoca.

, viene sostituita dal numero di esempi, serie di valori per il

Reti neurali MLP (Multi layer perceptron) Nei capitoli precedenti abbiamo descritto ampiamente il perceptron e capito cosa si intende per separazione lineare degli input. Nell‟esempio „OR‟ sono presenti i passaggi minimi necessari per avere una rete neurale minimale, con un solo neurone, che simula la porta logica OR. In questo capitolo inizieremo con il descrivere alcuni dei limiti di cui questa configurazione soffre, e in seguito, vedremo che partendo dal perceptron è possibile comporre reti più ampie componendole in più livelli. Tornado alla rappresentazione n-dimensionale del problema OR, dove „n‟ corrisponde al numero degli input del neurone: X1

(0,1)

(1,1)

(0,0)

(1,0) X2

La combinazione di input 0,0 è l‟unica che fornisce 0 in output. Si dice quindi che questo problema (l‟OR) ha soluzioni linearmente separabili. Ora cercheremo di addestrare la rete per eseguire l‟operazione booleana ExclusiveOr,(XOR). La seguente è la tabella di verità: Matteo Tosato - Introduzione alle reti neurali artificiali


30 X1 0 0 1 1

X2 0 1 0 1

Y 0 1 1 0

Tabella 3: Tabella di verità XOR

Il grafico delle soluzioni sarà identico al precedente a parte la disposizione della retta di colore rosso. O meglio, non si tratterà più di una retta. Non riusciremo mai a separare le soluzioni con l‟ausilio una retta. Questo significa che il percettrone bidimensionale non è in grado di risolvere problemi che hanno soluzioni separabili non linearmente. Come in questo caso, il percettrone non può risolvere il problema XOR. Le soluzioni dell‟XOR non sono linearmente separabili. Ci occorre un altro tipo di configurazione, il seguente grafico mostra come le soluzioni vengono divise da una rete neurale completamente connessa tra i suoi 3 livelli: X1

(0,1) (1,1)

(0,0)

(1,0) X2

Reti neurali a due strati (M-Adeline) Aggiungendo al percettrone un livello in più si ottiene una rete “M-Adeline” costituita da 2 livelli distinti. Questa non consente ancora di risolvere il problema XOR, ma si presta per introdurre un nuovo genere di algoritmi di addestramento, più complessi. Metodi adeguati anche per reti con più di due livelli.

Matteo Tosato - Introduzione alle reti neurali artificiali


31

X1 W3 X2

W2 Y

. . . Wi Xi Figura 9: M-Adeline

In questo caso, i neuroni di output devono avere una funzione di attivazione derivabile, i neuroni di input una funzione binaria o continua. Questa configurazione è in grado di distinguere gli input e suddividerli in classi diverse, l‟importante è che ognuna sia linearmente separabile dall‟altra. E‟ sufficiente che ogni neurone di output j rappresenti la corrispondente classe j e che, presentando input della classe j, si abbia:

∑(

)

∑(

) per tutti gli indici k diversi da un determinato j.

La reti M-ADALINE possono essere addestrate tramite l‟algoritmo di “Windrow-Hoff”. In modalità on-line viene riassunto nelle fasi seguenti: - Si presentano sequenzialmente gli esempi del training set. Per ogni esempio k, ogni neurone j fornirà generalmente un output diverso da quello target , con un errore. Quindi avremo che l‟errore della rete è dato da ( ) e un errore quadratico medio E su tutti gli esempi dato da:

∑ ∑(

)

- L‟errore ottenuto deve essere minimizzato aggiornando il valore dei pesi, questa variazione delta è definita come: (ottenuta tramite la regola del gradiente da

):

∑[(

)

]

Qui a seconda della funzione di attivazione utilizzata avremo casi differenti, ad esempio: 

Funzione di trasferimento sigmoide:

( ) (

)

( ∑[(

) )

(

)

]

Funzione di trasferimento tangente iperbolica:

( ) (

)

Matteo Tosato - Introduzione alle reti neurali artificiali


32

∑[( 

)(

)

]

Funzione di trasferimento lineare:

(

( ) ) ∑[(

)

]

- Infine la correzione dei pesi sinaptici avviene sempre tramite la formula:

(

)

( )

.

- A questo punto viene valutato l‟errore e se ancora non soddisfacente viene ripetuto il procedimento. L‟algoritmo descritto viene eseguito effettuando le correzioni dei pesi sinaptici ad ogni singolo esempio sottoposto:

[(

)

]

Architetture Feed-forward Una rete MLP (Multi layer perceptron) è una rete i cui neuroni compongono più livelli e, tra loro, sono completamente interconnessi. I livelli sono minimo 3, fra input, output e nascosto. Questi ultimi possono variare a qualsiasi numero. Questa configurazione è una delle più utilizzate in assoluto ed è in grado di risolvere molti problemi, quindi anche quelli che non hanno soluzioni separabili linearmente. Si parla di configurazioni feed-forward perché gli input seguono una sola direzione, dai neuroni di input a quelli di uscita, senza mai tornare indietro o passare a un neurone vicino. Se supponiamo di avere una rete con 3 input, ed un problema con soluzioni non separabili, ora i neuroni facente parte del layer nascosto inseriscono piani aggiuntivi consentendo la separazione dei vertici di classe A da quelli di classe B. Quindi per un generico problema tridimensionale la rappresentazione grafica delle sue soluzioni appare come la seguente:

Matteo Tosato - Introduzione alle reti neurali artificiali


33 X1

X3

X2

Figura 10: Piano bidimensionale di separazione in uno spazio tridimensionale

La struttura della rete è dunque come la seguente:

X1

X2

X3

Figura 11: Esempio di ANN feed-forward

PiĂš layer nascosti inseriamo piĂš siamo in grado di effettuare separazioni complesse. I neuroni degli strati nascosti non possono avere funzioni di trasferimento lineare altrimenti la rete sarebbe equivalente ad una a due strati e non potrebbe separare le soluzioni in modo non lineare. Per fissare il concetto, immaginiamo la seguente situazione di lavoro; due input due numeri nascosti con pesi sinaptici rispettivi e un neurone output O, con pesi sinaptici e . N1 W11

X1

V1 W21

O W12

V2 X2

W22

Matteo Tosato - Introduzione alle reti neurali artificiali

N2

,


34

( (

)

(

)

(

)

)

Date le equazioni che abbiamo ricavato possiamo considerare tutto come due input , un output O e pesi sinaptici . A differenza dei livelli input ed output il cui numero di neuroni è definibile, non c‟è un metodo altrettanto rigoroso per definire il numero di livelli nascosti necessari e nemmeno il numero dei neuroni necessari per ognuno di questi livelli. La definizione del numero di livelli avviene per via di osservazioni ripetute sull‟allenamento e tramite l‟esperienza accumulata su problemi simili.

Feed-forward ricorrenti Una configurazione particolare delle reti feed-forward è la cosiddetta “rete di Elman”. Questa possiede oltre al primo strato di neuroni nascosti, anche un layer „contesto‟ in cui vengono memorizzati gli stati di attivazione precedenti. Questo ha lo stesso comportamento degli altri strati, in partenza il valore di attivazione di questi neuroni sarà pari ad 1. Poi conterrà sempre il valore di uscita del primo layer nascosto all‟istante . Le connessioni sinaptiche dallo strato intermedio a quello di contesto valgono 1, mentre quelle in direzione inversa sono inizializzate casualmente e soggette al processo di addestramento in modo analogo a tutte le altre. Questa configurazione permette alla rete di ricordarsi di ciò che è successo in precedenza. E‟ un‟ottima soluzione quando si utilizza una rete per la previsione di funzioni complesse. I1 H1(t)

I2 H2(t)

yj

I3 Hn(t)

In H1(t-1) H2(t-1) Hn(t-1)

Figura 12: Rete feed-forward con connessioni ricorrenti Matteo Tosato - Introduzione alle reti neurali artificiali


35 Le connessioni segnate in rosso sono quelle di valore unitario che riportano le uscite dei neuroni all‟ingresso del ciclo successivo.

Error back-propagation (EBP) Uno degli algoritmi con cui è possibile addestrare reti di questo tipo è denominato “Error back-propagation”. Questo nome è dovuto al fatto che l‟errore calcolato su ciascun layer da partire dall‟ultimo viene consegnato al layer precedente e così via. La definizione matematica dell‟algoritmo è la seguente: Partendo dall‟equazione dell‟errore quadratico medio, e applicando a questo la regola del gradiente, ricaviamo l‟equazione per il calcolo della variazione dei pesi, riporto tutti i passaggi:

( (

) ( )

) ( ) (∑ (

) ) ( )

(A)

Ponendo:

(

) ( ) (B)

Si ottiene la seguente:

(C) Questa regola viene utilizzata per aggiornare i pesi dei livelli output e nascosto. Analogamente, per quanto riguarda le connessioni tra stato di input e strato nascosto:

(

)

Se confrontiamo questa formula con la precedente formula A, al posto degli errori noti (

) troviamo le derivate

, che dobbiamo ora calcolare retropropagando l‟errore

dallo strato output a ogni neurone nascosto:

∑(

)

∑(

) ( )

O anche applicando la formula B:

∑ Matteo Tosato - Introduzione alle reti neurali artificiali


36

Ottenendo:

(

)(∑

)

(D)

Che ponendo:

(

) .∑

/

(E)

Assume anche la forma della C:

(F) Le formule D,E,F sono valide per aggiornare i pesi dei livelli nascosto, input e anche, in reti più generali, per le connessioni tra due strati nascosti consecutive. Ad esempio se adottiamo la funzione sigmoide, è:

( ) ( )

(

)

(

)

Dove k è proporzionale alla pendenza della sigmoide nel suo punto di flessione:

Normalmente si pone k = 1, altre volte k=1/T dove T è denominata temperatura per motivi che vedremo in seguito. In ogni caso le formule A e D diventano rispettivamente:

(

) ( (

)(∑

)

(A‟) )

(D‟)

I pesi sinattici vengono aggiornati, ad ogni tempo t dell‟intervallo 1,2...,(t1),t,(t+1),...etc.

(

)

( )

(G)

(

)

( )

(H)

Per comprendere in modo completo le nuove nozioni, ricorriamo per esempio al problema dell‟XOR. In precedenza avevamo visto come la configurazione a singola unità è incapace di risolvere il problema. Ora invece utilizzeremo una rete che abbia due input, due neuroni nello strato nascosto ed un neurone come uscita. Introduciamo nella rete neurale un peso fittizio. Il bias. Questo corrisponde alla soglia vista per le funzioni di trasferimento binarie a soglia, con la differenza che questa avrà la particolarità di essere variabile. Il bias è rappresentabile nella rete come un neurone aggiuntivo nel layer precedente a quelli che hanno funzioni sigmoidali. Nel nostro caso strati nascosti e di output avranno funzioni a sigmoide, quindi il neurone bias lo introdurremo nei layer di input e nascosto. Questo neurone trasferirà sempre il valore 1, mentre la sinapsi che collega questo ai neuroni dello strato successivo ha un peso variabile come per gli altri casi. La nostra rete: Matteo Tosato - Introduzione alle reti neurali artificiali


37

ΣE

BIAS WEIGHT

Figura 13: Esempio di rete MLP addestrata con EBP

In questo modo saremo in grado di inserire più livelli di separazione, quindi di addestrare la nostra rete per eseguire l‟operazione XOR correttamente per tutte le casistiche. L‟errore commesso dallo strato di output sarà propagato all‟indietro. Faccio notare che il numero di neuroni che compone il layer nascosto è variabile, più questo cresce più il numero di iterazioni necessarie per l‟apprendimento sarà basso. Il numero minimo necessario è naturalmente 2. La regola di back-propagation presenta anche qualche controindicazione. In primis si tratta di un processo molto oneroso in termini di impegno CPU, La funzione di apprendimento può assumere un andamento molto complesso data la presenza di diverse sequenze di operazioni non-lineari. I minimi locali rappresentano le zone più pericolose perché l‟algoritmo può rimanerci intrappolato; Pericolose sono anche le zone pianeggianti, ad esempio XOR ne possiede parecchie, queste sono zone ove i pesi sono modificati in modo minimo rallentando la convergenza. Mentre valli troppo strette potrebbero far saltare l‟algoritmo lontano dalla soluzione.

Esempio addestramento tramite EBP Ci preoccuperemo poi di come migliorare l‟algoritmo per rendere la convergenza più veloce. Nel seguente esempio addestriamo la rete in modalità batch. #pragma once #include "targetver.h" #include #include #include #include #include

<iostream> <tchar.h> <stdlib.h> <cmath> <time.h>

using namespace std; #define u_int unsigned int // Comment to avoid logger procedure // #define LOG /* NEURAL LEGEND INPUT: Xi HIDDEN: Zk OUTPUT: Yj Matteo Tosato - Introduzione alle reti neurali artificiali


38 WEIGHTS: - HIDDEN: Wik (Bias: Wbik) - OUTPUT: Wkj (Bias: Wbkj) DELTA WEIGHTS: - HIDDEN: DWik - OUTPUT: DWkj SUMMATION: SUM FUNCTION: f() / derivate: f'() ERRORS: - HIDDEN: Ek - OUTPUT: Ej LEARNING RATE: N EXPECTED VALUE: EXP BIAS: B */ #include "stdafx.h" /* MLP with 3 layers - back-propagation learning algorithm - XOR test*/ #ifdef LOG FILE* log_file; #endif #define PRECISION double #define MAX_PATH 256 #define DBG_LEN 256 const PRECISION __l_rate = 0.2; const u_int INPUT_N = 2; const u_int HIDDEN_N = 2; const u_int OUTPUT_N = 1; const u_int DEFAULT_EPOCHS = 200000; const PRECISION M_E = 2.71828182845904523536; const PRECISION accuracy_target = 0.1; // ---------------------------------------// Allocates neural network structures in data section // ---------------------------------------// ----- COMPONENT --------------------------------- SYMBOL --// Weights matrices PRECISION W_hidden_in[HIDDEN_N][INPUT_N]; // Wik PRECISION W_out_hidden[OUTPUT_N][HIDDEN_N]; // Wkj // Weight vector on hidden layer PRECISION Bias_hidden[HIDDEN_N]; // Wbik // Weight vector on output layer PRECISION Bias_out[OUTPUT_N]; // Wbkj const PRECISION bias = 1.0; // B // Errors vectors PRECISION Error_hidden[HIDDEN_N]; // Ek PRECISION Error_out[OUTPUT_N]; // Ej // Delta weight matrices PRECISION Delta_W_hidden[HIDDEN_N][INPUT_N]; // DWik PRECISION Delta_W_output[OUTPUT_N][HIDDEN_N]; // DWkj // Output vectors PRECISION Out_hidden[HIDDEN_N]; // Zk PRECISION Out_output[OUTPUT_N]; // Yj PRECISION Net_out[OUTPUT_N];

// = Yj

// -----------------------------------------------------------//Prototypes: void Init_weights(void); PRECISION transf(PRECISION input); bool Training(const char* training_path_file, u_int* epochs); #ifdef LOG void Init_logger(void); void Tolog(char*string); #endif // ------------------------------------------------------------// --- PROGRAM ENTRY POINT --// ------------------------------------------------------------int main(int argc, char* argv[]) Matteo Tosato - Introduzione alle reti neurali artificiali

// Weights initialization // Sigmoid function // Training session // Logger initialization // Log routines


39 { u_int epochs = 0; char*pathfile = NULL; if(argc > 1) { pathfile = new(char[MAX_PATH]); strncpy(pathfile,argv[1],MAX_PATH); } #ifdef LOG Init_logger(); #endif Init_weights(); if(Training(pathfile,&epochs) != true) { cerr << "[+] WARNING - Neural network training failure" << endl; } else cout << "[+] SUCCESS - Neural network MLP trained in " << epochs << " epochs!" << endl; #ifdef LOG fclose(log_file); #endif PRECISION input[2]; // Network simulation while(1) { cout << " First input <0|1>:"; cin >> input[0]; cout << " Second input <0|1>:"; cin >> input[1]; if(input[0] < 0 || input[0] > 1 || input[1] < 0 || input[1] > 1) { cerr << "Input not valid. Retry." << endl; continue; } // Propagate into layers... for(int i = 0; i<HIDDEN_N; i++) { // Propagate into hidden layer Out_hidden[i] = transf( input[0]*W_hidden_in[i][0]+ input[1]*W_hidden_in[i][1]+ bias*Bias_hidden[i]); // Propagate into output layer Out_output[0] += W_out_hidden[0][i]*Out_hidden[i]; } Out_output[0] += Bias_out[0]*bias; // Calculate net output for(int i = 0; i<OUTPUT_N; i++) { Net_out[i] = transf(Out_output[0]); Out_output[0] = 0.0; } cout << "Network output: " << Net_out[0] << endl; } return 0; } // ------------------------------------------------------------// --- TRAINING SESSION --- BACK-PROPAGATION ALGORITHM --// ------------------------------------------------------------bool Training(const char* training_path_file, u_int* epochs) { FILE* fp = NULL; if(training_path_file != NULL) { fp = fopen(training_path_file,"r"); if(!fp) { cerr<<"[+] FATAL ERROR - Training file "<< training_path_file<<" not found!"<<endl; return false; } } char*n = new(char[16]); char*nptr = n; Matteo Tosato - Introduzione alle reti neurali artificiali


40 u_int k = 0, accuracy = 0; char c = 0; int set = 0; u_int input[2]; PRECISION Expected;

// Xi // EXP

input[0] = -1; input[1] = -1; while(*epochs < DEFAULT_EPOCHS) { // For training file if(fp != NULL) { while(input[0] == -1 || input[1] == -1) { c = (char)fgetc(fp); if(c == ';') { input[k] = atoi(n); (k == 1) ? k = 0 : k++; n = nptr; } else { sprintf(++n,"%c",c); } } } else { // Otherwise generate appropriate training set switch(set) { case 0: input[0] = 0;input[1] = 0;break; case 1: input[0] = 0;input[1] = 1;break; case 2: input[0] = 1;input[1] = 0;break; case 3: input[0] = 1;input[1] = 1;break; default: set = 0; continue; } set++; } // XOR Expected = (PRECISION)(input[0] ^ input[1]); // Propagate into layers... //---------------------------------------// // --- Zk = FhT(SUM(Xi*Wik)+(B*Wbik)) ---// // --------------------------------------// for(int i = 0; i<HIDDEN_N; i++) { // Propagate into hidden layer Out_hidden[i] = transf( input[0]*W_hidden_in[i][0]+ input[1]*W_hidden_in[i][1]+ bias*Bias_hidden[i]); // Propagate into output layer //---------------------------------------// // --- Yj = FhT(SUM(Wkj*Zk)+(B*Wbkj)) ---// // --------------------------------------// Out_output[0] += W_out_hidden[0][i]*Out_hidden[i]; } Out_output[0] += Bias_out[0]*bias; // Calculate net output for(int i = 0; i<OUTPUT_N; i++) { Net_out[i] = transf(Out_output[i]); Out_output[i] = 0.0; } // Calculate error on output layer //---------------------------------// // --- Ej = (EXP - Yj)*Yj(1-Yj) ---// // --------------------------------// for(int i = 0; i<OUTPUT_N; i++) Matteo Tosato - Introduzione alle reti neurali artificiali


41 Error_out[i] = (Expected - Net_out[i])*Net_out[i]*(1-Net_out[i]); // Calculate delta weights of output layer //-----------------------------// // --- DWkj = (Wkj+N*Ej*Yj) ---// // ----------------------------// for(int i = 0; i<HIDDEN_N; i++) Delta_W_output[0][i] = W_out_hidden[0][i]+__l_rate*Error_out[0]*Net_out[0]; // Calculate bias delta weight of output layer //-----------------------------// // --- Wbkj = (Wbkj+N*Ej*B) ---// // ----------------------------// for(int i = 0; i<OUTPUT_N; i++) Bias_out[i] = Bias_out[i] + __l_rate*Error_out[i]*bias; // Calculate error on hidden layer //-------------------------------// // --- Ek = (Zk*(1-Zk)*Ej*Wkj ---// // ------------------------------// for(int i = 0; i<HIDDEN_N; i++) Error_hidden[i] = Out_hidden[i]*(1-Out_hidden[i])*Out_hidden[i]*W_out_hidden[0][i]; // Calculate delta weights of hidden layer //-----------------------------// // --- DWik = (Wik+N*Ek*Xi) ---// // ----------------------------// for(int i = 0; i<HIDDEN_N; i++) Delta_W_hidden[i][0] = W_hidden_in[i][0]+__l_rate*Error_hidden[i]*input[0]; for(int i = 0; i<HIDDEN_N; i++) { Delta_W_hidden[i][1] = W_hidden_in[i][1]+__l_rate*Error_hidden[i]*input[1]; Bias_hidden[i] = Bias_hidden[i]+__l_rate*Error_hidden[i]*bias; } // Update weights //-------------------// // --- Wik = DWik ---// // ------------------// for(int j = 0; j<INPUT_N; j++) { for(int i = 0; i<HIDDEN_N; i++) W_hidden_in[i][j] = Delta_W_hidden[i][j]; } //-------------------// // --- Wkj = DWkj ---// // ------------------// for(int j = 0; j<OUTPUT_N; j++) { for(int i = 0; i<HIDDEN_N; i++) W_out_hidden[j][i] = Delta_W_output[j][i]; } (*epochs)++; if(fabs(Net_out[0] - Expected) < accuracy_target) accuracy++; else accuracy = 0; if(accuracy == 25) { delete n; if(fp)fclose(fp); return true; } // Uncomment for debug on prompt /* printf("[%u] IN: %d,%d OUT: %f EXP: %f ERR: %f\n", *epochs,input[0],input[1],Net_out[0],Expected,fabs(Net_out[0] - Expected)); */ #ifdef LOG char*debug_str = new(char[DBG_LEN]); sprintf(debug_str,"%u;%d;%d;%f;%f;%f\n",epochs,input[0],input[1],Expected,Net_out[0],fabs(Ne t_out[0] - Expected)); Tolog(debug_str); delete debug_str; #endif input[0] = -1; Matteo Tosato - Introduzione alle reti neurali artificiali


42 input[1] = -1; } delete n; if(fp)fclose(fp); return false; } // ------------------------------------------------------------// - TRANSFER FUNCTION FOR HIDDEN AND OUTPUT LAYER, SIGMOIDAL--// ------------------------------------------------------------PRECISION transf(PRECISION n) { return(1/(1+pow(M_E,-n))); } // ------------------------------------------------------------// --- WEIGHTS INITIALIZATION --// ------------------------------------------------------------void Init_weights(void) { srand((unsigned int)time(NULL)); for(int i = 0; i<INPUT_N; i++) for(int j = 0; j<HIDDEN_N; j++) W_hidden_in[j][i] = ((rand()%20000/10000.0)-1.0)+(rand()%10000/100000000.0f); for(int i = 0; i<OUTPUT_N; i++) for(int j = 0; j<HIDDEN_N; j++) W_out_hidden[i][j] = ((rand()%20000/10000.0)-1.0)+(rand()%10000/100000000.0f); for(int i = 0; i<HIDDEN_N; i ++) Bias_hidden[i] = ((rand()%20000/10000.0)-1.0)+(rand()%10000/100000000.0f); for(int i = 0; i<OUTPUT_N; i++) Bias_out[i] = ((rand()%20000/10000.0)-1.0)+(rand()%10000/100000000.0f); for(int i = 0; i<OUTPUT_N; i++) Out_output[i] = 0.0; } // ------------------------------------------------------------// --- LOG ROUTINES --// ------------------------------------------------------------#ifdef LOG void Init_logger(void) { log_file = fopen("NN_log_file.dat","w"); fprintf(log_file,"N°;IN;OUT;EXPECTED;NET_OUTPUT;ERROR\n"); } void Tolog(char*string) { fprintf(log_file,string); } #endif

Migliorie per l’algoritmo EBP Come abbiamo accennato, ci sono però due problemi fondamentali che rendono imperfetto questo algoritmo: - Il problema dei minimi locali. Il minimo locale può essere rappresentato in una depressione nella funzione di training in cui possiamo incappare e rimanervi bloccati. Ecco come può essere raffigurato graficamente:

Matteo Tosato - Introduzione alle reti neurali artificiali


43

Nell‟esempio riportato abbiamo sperimentato quanto questa affermazione è vera. Difatti il programma che addestra la rete FF per eseguire l‟operazione XOR soffre di blocchi frequenti. Ovvero come nel grafico, l‟algoritmo rimane intrappolato in una depressione che sembra essere la migliore soluzione che minimizza l‟errore, mentre in realtà non lo è. - Scarse performance. EBP è uno dei più dispendiosi algoritmi di addestramento in termini di tempo CPU. Questo perché le variazioni sinaptiche sono davvero minime, quindi servono molte iterazioni per convergere al valore desiderato. Infatti ecco come l‟errore potrebbe oscillare facendo crescere in maniera esponenziale il numero di epoche necessarie all‟apprendimento del comportamento voluto:

Dovremmo essere in grado ora di fare considerazioni più complesse e più generali sui problemi e soluzioni con cui abbiamo a che fare. Se eseguite un buon numero di test sull‟esempio che abbiamo appena visto, vi capiterà senz‟altro di accorgervi delle problematiche citate nel capitolo precedente. Esistono un buon numero di migliorie che sono state introdotte con lo scopo di migliorare l‟algoritmo. Ora ne vedremo alcune.

Il coefficiente “momentum” - La prima è l‟introduzione di una variabile costante da moltiplicare alla variazione delta dei pesi al tempo (t-1), ogni volta che un peso viene aggiornato, dunque la funzione diventa ora:

(

)

( )

( )

(

)

è un coefficiente come , anche esso compreso tra 0 ed 1. Il termine aggiuntivo rappresenta un “ricordo” dell‟aggiornamento avvenuti in precedenza. Questa tecnica è implementabile nel codice di esempio visto prima, semplicemente aggiungendo delle matrici di ugual grandezza a quelle utilizzate per immagazzinare i valori delta dei pesi, ed utilizzarle per salvare la variazione all‟istante t-1. Matteo Tosato - Introduzione alle reti neurali artificiali


44

Learning rate adattativo - Un‟altra tecnica utilizzata (in aggiunta) è l‟introduzione del learning rate “adattativo”. Questo significa che quando l‟algoritmo di addestramento rimane bloccato, il learnig rate assume un valore più utile ad uscire del problema. Questa operazione può essere fatta con diversi metodi: Una prima versione utilizza metodi euristici, rappresentati da regole del tipo: “se l‟errore è alto e la sua variazione delta è grande, allora occorre diminuire un po‟ il learning rate”. Le regole possono essere applicate in modo autonomo, da un sistema “espero” fuzzy, che assiste la rete neuronale durante l‟apprendimento. Una seconda variante propone la modifica del learning rate non solo ad ogni ciclo t del processo, ma anche per ogni connessione di peso. Indicando con ( ) il learning rate relativo al tempo t e alla connessione di peso (t), la regola suggerita, che ha consentito di accelerare la convergenza dell‟apprendimento, è:

( )

(

)

Dove il coefficiente u deve essere leggermente inferiore a 1 (tipicamente u = 0.7) o leggermente superiore a 1 (u = 1.05) a seconda che siano dello stesso segno o meno le due derivate:

( )

(

)

Il coefficiente di momentum viene invece lasciato invariato nel tempo. - Un‟altra variante propone la seguente legge per calcolare sia il valore del learning rate che quello del momentum:

( )

(

),

-

Dove, con riferimento ai pesi ( l‟angolo tra il vettore

e al vettore di cui essi sono componenti, ) e il vettore gradiente dell‟errore grad E(t).

è

Parametri di libertà aggiuntivi - Un‟altra ancora apporta una generalizzazione dell‟algoritmo BP, introducendo nuovi gradi di libertà della rete. Per ogni neurone i viene usata la seguente funzione di trasferimento di tipo sigmoidale:

(∑

)

Dove i parametri k, possono variare nel tempo nel corso dell‟apprendimento. Nel BP classico le regole d‟apprendimento riguardano solo i pesi W e la soglia , assimilata generalmente ai pesi mediante bias. Indicheremo questo con: ( ). Le regole sono quelle già viste, del tipo:

Possiamo generalizzare l‟algoritmo BP, facendo variare tutti i parametri della sigmoide prima introdotta: BP(W, ) aggiungendo alle regole precedenti le seguenti, ad esse analoghe: Matteo Tosato - Introduzione alle reti neurali artificiali


45

Dove SIGMA, LAMBDA, KAPPA sono opportuni coefficienti di learning rate, compresi tra 0 e 1.

Modifica della funzione sigmoidale In ultimo, segnalo una intelligente soluzione per il problema delle performance che mi è capitato di trovare in rete. L‟idea verte a sostituire la funzione di trasferimento dei neuroni con una equivalente o quasi dal punto di vista delle caratteristiche, ma che sia meno impegnativa per quanto riguarda i calcoli. La funzione sigmoide standard è:

Questa funzione presenta caratteristiche importantissime per noi. E‟ sempre crescente, a vale 0 e a 1, è derivabile e continua su tutto l‟asse dei reali. E‟ la più utilizzata di tutte proprio per questa particolarità. Essa ha solo un problema, al denominatore è presente un esponenziale. Questo è molto dispendioso in termini di impegno CPU. Dobbiamo tenere presente che ogni neurone della rete avrà una funzione di trasferimento di questo tipo, e che questi valori vengono calcolati migliaia di volte per ogni epoca. Ad ogni modo una soluzione è utilizzare funzioni di trasferimento alternative. Ecco un esempio:

(

| |

)

Questa funzione è del tutto simile in caratteristiche alla sigmoidale ma non ha l‟esponenziale, questa è in grado di ridurre notevolmente il tempo impiegato dalla rete per l‟addestramento. Non intendo il numero di epoche naturalmente ma il tempo CPU dedicato. I seguenti sono i due grafici delle funzioni. Quello in verde è relativo a quest‟ultima versione.

Ci sono altre migliorie possibili da applicare all‟algoritmo di addestramento backMatteo Tosato - Introduzione alle reti neurali artificiali


46 propagation, in generale però è possibile anche crearsi una propria regola di miglioramento a quanto già fa BP secondo le proprie esigenze.

Algoritmo Resilient back-propagation (RPROP) L‟algoritmo EBP che abbiamo affrontato, consente di addestrare una rete feed-forward. Abbiamo visto che esso soffre di alcuni inconvenienti come i minimi locali e i problemi legati alle prestazioni i quali poi dipendono dal tipo e potenza della CPU che deve eseguire l‟addestramento. RPROP sta per “Resilient propagation” ed è un nuovo sistema per l‟apprendimento supervisionato. Ideato nel 1993 da Martin Riedmiller. Esso attua un adattamento diretto sul peso in questione basandosi sul valore del gradiente locale. Il suo principio è eliminare l‟influenza della dimensione (il valore assoluto che assumono) delle derivate parziali sugli aggiornamenti dei pesi. Conseguenza di ciò, solo il segno di queste viene valutato. Viene introdotta di fatto una nuova regola, la quale determina l‟evoluzione del valore di aggiornamento ( ). La stima si basa appunto sull‟osservazione del comportamento della derivata parziale durante i successivi due step:

( )

(

)

( )

(

)

(

)

( )

(

)

(

{

)

Dove Ogni volta che la derivata parziale del peso corrispondente cambia il suo segno, fatto che indica che l‟ultimo aggiornamento fatto è stato molto importante e che l‟algoritmo ha saltato sopra un minimo locale, il valore di aggiornamento ( ) viene diminuito dal fattore Se la derivata mantiene il suo segno, il valore di aggiornamento è debolmente incrementato in modo da accelerare la convergenza nella regione superficiale. Questo per ogni peso. Una volta che il delta è stato adattato per ogni peso, il valore di aggiornamento segue una semplice regola: se la derivata è positiva (incremento di errore), il peso viene decrementato dal suo valore di aggiornamento, se la derivata è negativa, il valore di aggiornamento viene aggiunto:

( ) ( )

( ) ( )

{ Quindi come al solito:

(

)

( )

( )

Esiste anche una eccezione per le regole viste; se la derivata parziale cambia di segno significa che lo step di aggiornamento precedente era troppo importante e il minimo è andato perso, allora il precedente valore di aggiornamento viene invertito: Matteo Tosato - Introduzione alle reti neurali artificiali


47

( )

( ( )

) (

)

A causa di questa regola, la derivata può cambiare il suo segno più di una volta. Per evitare una doppia correzione del valore di aggiornamento, non ci sarà adattamento del valore nello step successivo al cambiamento di segno. In pratica questo può essere fatto settando la regola di aggiornamento nel modo seguente:

(

)

La derivata parziale dell‟errore totale è resa da:

( )

( )

Quindi, la derivata parziale degli errori deve essere accumulata per tutti i patterns di addestramento. Se volessimo fare un confronto con l‟algoritmo classico visto in precedenza, diremmo che la differenza sostanziale è espressa dalla seguente formula, ovvero quella che determina il valore delta per l‟aggiornamento dei pesi sinaptici:

(

) (

[EBP] )

[RPROP]

gioca quindi un ruolo determinante nell‟aggiornamento dei pesi sinaptici. I seguenti sono i valori più comuni da assegnare al fattore di incremento e decremento: e Per evitare che questi due valori crescano o diminuiscano senza controllo, vengono solitamente impostati anche due limiti, uno inferiore ed uno superiore: e Mentre il valore iniziale di questi è solitamente posto a 0.1. (

)

Di seguito propongo infine lo “pseudo-codice” del nuovo algoritmo di training: [Martin Riedmiller, Heinrich Braun; Institut fur Logik, Komplexitat und Deduktionssyteme University of Karlsruhe]

( ) (

)

( ) * Matteo Tosato - Introduzione alle reti neurali artificiali


48

(

(

)

( )

)

(

)

(

( ) (

( )

( ))

( )

( )

(

)

(

(

* ) ( )

( ) ( )

+ )

( )

( ) (

(

)

(

(

(

)

)

* )

+

( ) (

( ) ( ))

( )

(

)

( ) )

)

*

( )

( ) ( )

+ + (

)

La dimensione della derivata diminuisce esponenzialmente con la distanza tra il vettore dei pesi e quello dei valori neurali del layer di output. Usando RPROP la dimensione del valore di aggiornamento dei pesi dipende solo dalla sequenza dei segni assunti dal gradiente locale calcolato. Le reti neurali offrono un tipo di fuzzy logic estremamente adattabile, l‟importante è averne appreso il funzionamento base, per poi procedere con le proprie configurazioni. Quelle che abbiamo affrontato fino ad ora sono le configurazioni più utilizzate e più utili nelle moderne applicazioni come pattern-recognition, classificazione di dati, risoluzione di problemi di controllo automatico, sistemi adattativi ed altre faccende.

Reti di Hopfield Nelle reti appena viste, generalmente chiamate “feed-forward”, i segnali in ingresso andavano in una sola direzione, da qui i layer di input ed output. Naturalmente di reti ce ne sono di altri tipi e le abbiamo visto nella prima parte del testo. Un tipo di rete importante sono quelle che hanno neuroni con collegamenti, oltre che con quelli del livello successivo, anche con neuroni dello stesso livello.

Matteo Tosato - Introduzione alle reti neurali artificiali


49 S1

3

1

2 S2

S3 W3

W2

Figura 14: Rete caotica semplice

La complessità di queste reti stà nel fatto che le sue unità rimangono attive anche all‟istante successivo dalla ricevuta di input. La rete rimane perciò attivata per un periodo limitato (transitorio) di tempo. Possono verificarsi diversi casi, la rete può oscillare attraverso un numero finito T di stati, per poi ricominciare il ciclo. Oppure oscillare attraverso un numeri infinito di stati, senza mai riconseguire uno stato già visitato (Chaos deterministico). La rete in figura può avere un comportamento assai complesso. Si consideri che ogni neurone ha la seguente funzione di attivazione per ogni neurone:

( )

(

I cui stati

( ( (

) ) )

)

( ) cambiano nel tempo t con le solite regole (

( ) ( ( ( )) ( ( ))

sono i pesi sinaptici):

( ))

Se si definiscono i valori delle costanti nelle funzione di attivazione, lo stato cambia nel tempo in modo complesso, al variare del parametro . Le variazioni di tale parametro conferiscono alla rete comportamenti stabili, oscillatori oppure caotici. Le versione discreta (DH Discrete Hopfield) ha N neuroni con stati bipolari ( ) ( ), funzione di trasferimento a gradino (detta anche signum, abbreviato generalmente in sign o sgn) e connessioni simmetriche tra un neurone e qualsiasi altro, escludendo solo le connessioni di un neurone con se stesso. Si hanno quindi N(N-1) connessioni e N(N1)/2 pesi sinaptici diversi. Indicando con il peso sinattico della connessione j->i tra i due neuroni i,j si ha:

Per ogni neurone i, il potenziale si calcola:

( )

∑(

( ))

Dato che ogni neurone hanno una funzione a gradino e che per ( ) , si mantiene lo stato precedente, la rete ha una funzione E, denominata “energia” per un‟analogia formale con la energia meccanica:

∑ ∑(

)

La quale diminuisce nel tempo:

Matteo Tosato - Introduzione alle reti neurali artificiali


50

,∑(

-

)

Quindi l‟energia non cresce ma diminuisce, per L‟attivazione dei neuroni può essere per via iterativa, in modo deterministico oppure stocastico, sincrono o asincrono. Restando sulla modalità deterministica, se i neuroni vengono attivati in parallelo (sincronia), oltre a stati finali stabili si possono conseguire attrattori a ciclo limite T = 2. Esaminiamo la rete: S1

1

S2

1,5

-2

S3

I neuroni sono bipolari a soglia zero e per un totale di 6 connessioni. Si può notare che con una attivazione asincrona si hanno due stati stabili (+1,-1,+1) e (-1,+1,-1). Si ha infatti partendo dal primo stato e attivando sequenzialmente un neurone per volta, la situazione seguente: Tempo

Stato rete (S1,S2,S3)

t t+1 t+2

(+1,-1,+1) (+1,-1,+1) (+1,-1,+1)

Neurone attivato S1 S2 S3

Potenziale neurone attivato +0.5 -1 +3.5

Stato futuro del neurone attivato +1 -1 +1

La sequenza viene ripetuta, quindi lo stato della rete si ripete ad oltranza e rimane invariato. Lo stato (+1,-1,+1) è detto attrattore assieme allo stato (-1,+1,-1). La rete evolve sempre verso questi stati. Naturalmente a seconda della modalità il comportamento della rete di Hopfield cambia di conseguenza. Senza vedere tutti gli altri casi preferisco soffermarmi su quelli che sono i possibili utilizzi delle reti di Hopfield. I due principali sono come memoria associativa e come ottimizzatore. L‟ottimizzazione è un problema particolarmente adatto alle reti di Hopfield proprio perché queste convergono verso uno stato stabile. Un esempio classico è il problema del commesso viaggiatore. Date N città e conoscendo le distanze tra due qualunque di esse, si tratta di determinare un percorso di visite che soddisfa i vincoli e minimizza il percorso globale, con ritorno alla città iniziale. Il problema TSP può inoltre essere simmetrico o asimmetrico. Generalmente questi problemi vengono risolti con i metodi della ricerca operativa, che spesso offrono prestazioni accettabili, conseguite polinomiali. Tuttavia la programmazione lineare a numeri interi e la programmazione dinamica possono solo trattare problemi con Molto più efficienti sono alcuni moderni metodi come quello denominato “Branch and cut” e l‟algoritmo LinKerningham. L‟uso delle reti neuronali per risolvere problemi di questo tipo è stato proposto inizialmente da Hopfield, con una versione analogica della sua rete. Questa rete ha la proprietà di avere, come epifenomeno del normale funzionamento, una energia:

∑ ∑(

)

Matteo Tosato - Introduzione alle reti neurali artificiali


51 La quale, come abbiamo visto, diminuisce monotonicamente nel tempo. L‟energia è funzione quadratica degli stati e lineare dei pesi sinaptici . Se si riuscisse a costruire una funzione costo F che fosse funzione quadratica delle variabili del problema e ne soddisfacesse i vincoli, dall‟eguaglianza ( ) ( ) si potrebbero ricavare i pesi che consentirebbero alla rete, partendo da valori casuali, di evolvere verso uno stato finale che coincide con la soluzione ottimale (costo minimo e vincoli soddisfatti). La funzione F dipende perciò dal problema e, per quanto di disegno laborioso, non presenta difficoltà insormontabili.

Reti neurali auto-organizzanti Mappe di Kohonen Teuvo Kohonen (1934) è un ricercatore, ed è attualmente professore emerito nell‟accademia Finlandese. Uno dei suoi più famosi contributi fu quello sulle reti auto-organizzanti “SOM”. Le reti SOM (Self-organizing maps) sono un particolare tipo di ANN che creano una forte ed interessante analogia con le reti biologiche che compongono la corteccia celebrale. Questa corteccia può essere paragonata al cappello di un fungo che avvolge il suo gambo, i nuclei sottocorticali e il midollo spinale. Alla corteccia affluiscono le informazioni visive, acustiche, olfattive e le sensazioni tattili e dolorose: ogni sensazione è mappata e viene letta da una specifica area della corteccia. (area di differenza sensoriale). Insieme a quest‟area sensoriale, vi si trovano le aree motorie, che controllano la funzione dei muscoli volontari. E‟ stato possibile tracciare una mappa dei neuroni responsabili dei movimenti delle dita, della mano, delle braccia e di tutti gli altri muscoli volontari. Siccome non tutte le aree muscolari sono uguali, c‟è una differenza di sensibilità fra le diverse parti del corpo. La mappa dei movimenti, come quella delle sensazioni viene definita con il termine di “omuncolo” che indica la rappresentazione proporzionale delle funzioni motorie e sensoriali.

Figura 15: Omuncolo sensoriale e motorio

Le aree motorie si occupano di generare impulsi atti a contrarre le rispettive parti del corpo formate da muscoli volontari, mentre le aree sensoriali raccolgono informazioni dai vari organi recettori, più un‟area sensoriale è ampia più la sensazione è ricca di dettagli. Matteo Tosato - Introduzione alle reti neurali artificiali


52 Nelle reti SOM, i neuroni proiettano l‟input multidimensionale su una superfice di neuroni artificiali, rappresentandolo in due dimensioni ed effettuandone quindi una notevole compressione, pur conservando le similarità originarie. Inoltre la capacità di scoprire, in modo autonomo, proprietà interessanti di un input multidimensionale accomuna le reti SOM alla capacità degli organismi viventi di adattarsi all‟ambiente senza la necessità di una guida esterna. Le reti SOM sono in grado imparare in modo autonomo come classificare una serie di input attraverso la competizione fra i suoi neuroni che compongono la superficie. Il seguente schema mostra questo sistema di “Competitive learning” con „n‟ input e „m‟ output: X1

Xi -1

Xi +1

Wji

Wj1

Y1

Xi

Yj -1

Yj

Xn

Wjn

Yj +1

Ym

Figura 16: Schema di rete "Competitive learning"

Dove m neuroni j (con attivazione ) sono connessi a n input , tramite pesi sinaptici . Inoltre, ogni neurone j è connesso con tutti i neuroni k del suo strato (incluso se stesso) tramite pesi sinaptici . I neuroni k vicini al neurone j lo eccitano ( ), mentre quelli lontani lo inibiscono( ). La seguente è la dinamica della rete:

(

)

[∑(

)

∑(

( ))]

Dove, se v è il vicinato, (i + k) varia tra (j - v) e (j + v); F è una funzione di trasferimento non lineare con saturazione (per limitare il valore di ) e è un fattore di feed-back, compreso tra 0 e 1. Presentando in input un vettore X, di n componenti , verrà attivato maggiormente un ( ) certo neurone j( ( ) ),in base alla prima sommatoria dell‟equazione precedente. Nei tempi successivi entra in azione la seconda sommatoria e i neuroni in un intorno di j si rafforzano mutualmente e indeboliscono i neuroni lontani. In questo modo le uscite Y dei neuroni vanno a creare quella che viene chiamata “bolla di attivazione”, centrata su j, con una certa estensione “orizzontale” e una certa ampiezza “verticale”. L‟estensione dipende dal rapporto tra feed-back positivi e negativi, la prevalenza dei primi allargando la bolla e viceversa. L‟ampiezza dipende dal fattore di feed-back e cresce con esso. Quindi, ogni input attiverà la sua bolla ed input simili attiveranno le bolle dei neuroni limitrofi, mentre input differenti attivano bolle più lontane. Il seguente è il grafico di distribuzione dei pesi “a capello messicano” in un intorno del neurone di uscita Y.

Matteo Tosato - Introduzione alle reti neurali artificiali


53

Figura 17: Distribuzione dei pesi del tipo: "a cappello messicano" in un intorno del neurone Yk

E‟ possibile semplificare notevolmente la struttura della rete eliminando le connessioni ricorrenti laterali “a cappello messicano”, se si definisce un vicinato di neuroni contemporaneamente attivi. In tal caso la bolla si forma per definizione, dato che l‟attivazione iniziale di un neurone j trascina quella dei neuroni vicini. La struttura finale di una rete SOM utilizzata nelle applicazioni è quindi senza queste connessioni laterali, costituita da m neuroni j, ciascuno collegato a n input i tramite dei pesi sinaptici . Tutte le connessioni laterali vengono invece simulate utilizzando un vicinato topologico. Normalmente questo è un quadrato. Ad esempio gli 8 neuroni adiacenti. L‟apprendimento senza supervisione è piuttosto semplice, si istruisce la rete con m * n ( ) e si pesi sinaptici, si presenta in input il generico vettore ∑( determina quel vettore che rende massimo il prodotto scalare: ). I neuroni competono ed uno solo di essi vincerà. Questo, assieme a tutto il suo vicinato, avvicina il relativo vettore a X:

(

)

(

)

( )

Dove è il coefficiente learning rate che già conosciamo, compreso tra i valori 0 ed 1. Questo viene fatto diminuire nel tempo, partendo da 1. Notate che il vettore dei pesi è quello relativo al neurone vincente. Queste reti sono state applicate con successo per risolvere questioni riguardanti la classificazione e la valutazioni di parametri. Ad esempio in Germania, è stata sviluppata una rete di questo tipo per la valutazione di circuiti integrati, composta da 2500 neuroni con 10 input, ogni input rappresenta un parametro del circuito da analizzare. Come la quantità ed altri parametri di fabbricazione. Questa rete crea quindi diverse “bolle”, ognuna delle quali attrarrà gli input simili. Una rete di questo tipo viene quindi addestrata in modo tale da avvicinare il vettore dei pesi appartenenti al neurone vincente, verso il vettore degli input. (Immaginate di rappresentare graficamente i due vettori iscritti in una circonferenza di raggio 1). Aggiungendo la supervisione all‟algoritmo di addestramento si ottiene una rete sicuramente più adatta alla classificazione. Pilotando il tipo di vettore di input è possibile ottenere le bolle di attivazione desiderate. Il funzionamento della rete sarà quindi quello di inserire gli input ricevuti in certe aree neurali in modo da compiere Matteo Tosato - Introduzione alle reti neurali artificiali


54 delle classificazioni. Una rete SOM di grosse dimensioni riesce ad emulare almeno parzialmente ciò che avviene nel cervello umano. La rete di Kohonen lavora come una mappa e come un mappa viene costruita. Su questa mappa andranno a formarsi le nostre aree di attivazione che corrispondono a certi tipi di input esterni.

Il disegno è semplificato, ma ogni neurone è in realtà connesso ad ogni uscita tramite sinapsi di peso differente. I neuroni di output assieme formano una mappa. Questa mappa si attiverà in una certa zona e si indebolirà nelle altre visto il comportamento dei neuroni dello strato di output a formare “il cappello messicano”. L‟implementazione di questo tipo di rete non è molto più complesso del caso MLP. Sempre con matrici multidimensionali è possibile rappresentare ogni parte fisica della rete: neuroni e sinapsi. Riassumiamo qui di seguito quali sono le operazioni da compiere per lavorare con SOM: - Si inizializza un vettore per gli input ed uno per gli output. - Si inizializza un vettore multidimensionale per i pesi tra input ed output. - Una funzione learn() dovrà passare i valori di input normalizzati al vettore di input. - Una funzione exec() chiamata da learn() dovrà calcolare il valore per ogni neurone dello strato di output. - Un‟altra funzione dovrà scansionare il vettore di output in cerca del neurone vincente. - Questo viene passato ad una funzione che ha il compito di aggiornare il vettore dei pesi per il neuronodo. - Qui è possibile inserire una funzione adjust() di normalizzazione degli altri pesi dei neuroni del vicinato. - Una funzione test() si preoccuperà di testare la rete una volta che questa è arrivata ad attivare diverse aree. Tale configurazione è anche denominata “mappa di Kohonen”. Infatti rappresentando i neuroni di output in righe e colonne a formare una matrice ad esempio 100x100, si ottiene una mappa. I base a come i pesi sono stati inizializzati, la rete durante la fase di apprendimento, si auto-organizzerà in aree. Una per ogni classe di input. Un tipico esempio per comprendere come lavora questa configurazione è il riconoscimento di profili altimetrici. Un profilo può essere rappresentato anche da pochi input. Ad esempio 5. Valori: 0,3 – 0,2 – 0,3 – 0,3 – 0,4

Matteo Tosato - Introduzione alle reti neurali artificiali


55 00.001

00.000 1

2

3

4

5

Valori: 0,1 – 0,2 – 0,5 – 0,4 – 0,2 00.001 00.001 00.000 1

2

3

4

5

Valori: 0,4 – 0,1 – 0,0 – 0,2 – 0,6 00.001 00.001 00.000 1

2

3

4

5

La mappa di Kohonen è rappresentata invece in questo modo:

O

Il pallino nella rete rappresenta il neurone vincente. Durante la classificazione degli input possono verificarsi dei problemi. Ad esempio, il profilo piano si pone in una situazione neutrale rispetto alle altre tipologie. Così facendo può essere inserito all‟interno di altre classi. Per evitare questo problema, legato a questo particolare caso, si può ricorrere a due operazioni: - la normalizzazione degli input, o “stretching” dei profili; questa avviene sottraendo il valore di input più basso a ogni input, poi portare il valore più alto ad 1 e dividere tutti gli altri input con questo valore massimo. - L‟altra norma consiste nel tipo di funzione di trasferimento; invece di fare una moltiplicazione tra valore input e peso, si può ricorrere alla differenza vettoriale tra le due grandezze. Quindi la formula di trasferimento diventa: Matteo Tosato - Introduzione alle reti neurali artificiali


56

∑[ (|

] |

)

Dove “e” vale 0.01. Implementiamo una rete SOM per il riconoscimento dei profili altimetrici. Dovremo anche predisporre una serie di set per il training e una serie di set per la verifica. Possibilmente diversi, in modo da poter poi osservare la capacità di generalizzazione della rete. I set possono essere inseriti uno per riga in un file, in modo tale da poterli leggere con fscanf(). E saranno come quelli visti negli esempi sopra. Esempio di rete SOM Il seguente è il codice della rete di Kohonen a scopo esemplificativo che utilizza già la due ottimizzazioni di funzionalità che abbiamo descritto.

/* * SOM [Self organizing map] * by Matteo Tosato * header file */ #ifndef _HSOM_H #define _HSOM_H #include #include #include #include #include #include

<stdlib.h> <math.h> <string.h> <time.h> <stdio.h> <unistd.h>

#define neuron_state_t double #define BYTE char #define DUMP 0 #define MAP 1 #define MAX_PATH 256 // Initial learning rate const double __l_rate = 0.1; // Uncomment to create log file with network results in append mode // #define LOG const unsigned int NUM_INPUT_N = 100; const unsigned int NUM_OUTPUT_N = 100;

// GRAPHIC MAP 100 x 100 // Maximum size of input patterns

// Parameters structure: struct param { char* lrn_file; char* val_file; FILE* lrn; FILE* val; unsigned int iterations; unsigned int pattrn_size; double K; BYTE print_mode; unsigned int winner; }; // Neural network structure: struct SOM { Matteo Tosato - Introduzione alle reti neurali artificiali


57 neuron_state_t IN[NUM_INPUT_N]; neuron_state_t OUT[NUM_OUTPUT_N]; neuron_state_t WEIGHTS[NUM_INPUT_N][NUM_OUTPUT_N]; }; #endif /* _HSOM_H */ Questo è il file di implementazione principale: /* * SOM [Self organizing map] * by Matteo Tosato * * Kohonen neural networks, reproduces the "pseudo-biological networks behavior". * Parameters: * pattern dimension - training file - validation file - number of iterations (epoch) - [-d] Dump results | [-g] MAP * * tosatz{at}tiscali{dot}it */ #include "hsom.h" // Prototypes: struct param* parameters_parser(int argc, char**argv); void learn(struct param*,struct SOM*); void exec(struct param*,struct SOM*); void adjust(struct param*,struct SOM*); void Init_weights(struct param*, struct SOM*); void Test(struct param*, struct SOM*); void dump(struct param*, struct SOM*); void map(struct param*, struct SOM*); void stretching(struct param*, struct SOM*); /***************************************** * Entry Point ******************************************/ int main(int argc, char** argv) { struct SOM nn; struct param *param; if((param = parameters_parser(argc,argv)) == NULL) { fprintf(stderr,"[+] FATAL - Arguments parsing failure.\n"); printf("Usage:\n %s <pattern dimension> <path training file> " "<path validation file (test)> <number of iterations (epochs)>\n",argv[0]); printf("\ntosatz{at}tiscali{dot}it\n"); return 1; } param->lrn = fopen(param->lrn_file,"r"); param->val = fopen(param->val_file,"r"); if(param->lrn == NULL || param->val == NULL) { fprintf(stderr,"[+] FATAL - File not found\n"); return 1; } param->K = __l_rate; Init_weights(param,&nn); printf("[+] Learning ..."); learn(param, &nn); printf(" Done!\n"); printf("[+] Testing ... \n\n"); Test(param,&nn); fclose(param->lrn); fclose(param->val); delete param, &nn; return 0; } /***************************************** * Input stretching, (input normalization) ******************************************/ void stretching(struct param* p, struct SOM* nn) { neuron_state_t MIN = 10; neuron_state_t MAX = 0; u_int max_index = 0; Matteo Tosato - Introduzione alle reti neurali artificiali


58 // Search for minimum and maximum values: for(u_int j = 0; j < p->pattrn_size; j++) { if(nn->IN[j] < MIN) { MIN = nn->IN[j]; } if(nn->IN[j] > MAX) { MAX = nn->IN[j]; max_index = j; } } // Perform input normalization: for(u_int j = 0; j < p->pattrn_size; j++) { nn->IN[j] -= MIN; } nn->IN[max_index] = 1.0f; for(u_int j = 0; j < p->pattrn_size; j++) { if(j == max_index) continue; nn->IN[j] = nn->IN[j] / MAX; } } /***************************************** * Network in learning step ******************************************/ void learn(struct param* p, struct SOM* nn) { unsigned int j, c = 0; // Begin while(c < p->iterations) { while(1) { j = 0; // Get input while(j < p->pattrn_size) { fscanf(p->lrn,"%lf",&nn->IN[j++]); } if(feof(p->lrn)) break; exec(p,nn); adjust(p,nn); } p->K = p->K/1.3; fseek(p->lrn,0,0); c++; } return; }

// To input neurons

/***************************************** * Elaboration of inputs ******************************************/ void exec(struct param *p, struct SOM *nn) { // Input normalization: stretching(p,nn); // For each output neuron calculate rispective value: for(unsigned int k = 0; k < NUM_OUTPUT_N; k++) nn->OUT[k] = 0; for(unsigned int k = 0; k < NUM_OUTPUT_N; k++) { for(unsigned int j = 0; j < p->pattrn_size; j++) { //nn->OUT[k] += (nn->IN[j] * nn->WEIGHTS[j][k]); <-- normal nn->OUT[k] += 1/(fabs(nn->IN[j] - nn->WEIGHTS[j][k]/100) + 0.01); } } // Neighbourhood simulation, "Mexican bonnet weights": for(unsigned int j = 0; j < NUM_OUTPUT_N; j++) nn->OUT[j] += 0.1*(nn->OUT[j-1]+nn->OUT[j+1]-nn->OUT[j-2]-nn->OUT[j+2]); // Search for winner neuron: u_int winner = 0; neuron_state_t winner_value = 0.0f; Matteo Tosato - Introduzione alle reti neurali artificiali


59 for(unsigned int i = 0; i < NUM_OUTPUT_N; i++) { if(nn->OUT[i] > winner_value) { winner_value = nn->OUT[i]; winner = i; } } p->winner = winner; } /***************************************** * Simulation of the neighborhood ******************************************/ void adjust(struct param *p, struct SOM *nn) { // Updating weights of winner neuron and and nearest neighbors: for(unsigned int i = 0; i < p->pattrn_size; i++) { nn->WEIGHTS[i][p->winner] = nn->WEIGHTS[i][p->winner] + p->K * (nn->IN[i] - nn->WEIGHTS[i][p->winner]); nn->WEIGHTS[i][p->winner - 1] = nn->WEIGHTS[i][p->winner - 1] + p->K * (nn->IN[i] - nn->WEIGHTS[i][p->winner nn->WEIGHTS[i][p->winner - 2] = nn->WEIGHTS[i][p->winner - 2] + p->K * (nn->IN[i] - nn->WEIGHTS[i][p->winner nn->WEIGHTS[i][p->winner + 1] = nn->WEIGHTS[i][p->winner + 1] + p->K * (nn->IN[i] - nn->WEIGHTS[i][p->winner nn->WEIGHTS[i][p->winner + 2] = nn->WEIGHTS[i][p->winner + 2] + p->K * (nn->IN[i] - nn->WEIGHTS[i][p->winner } return; } /***************************************** * Function for Test ******************************************/ void Test(struct param *p, struct SOM *nn) { unsigned int k = 0; char answer[8]; while(1) { endfile: for(unsigned int j = 0; j < p->pattrn_size; j++) { fscanf(p->val,"%lf",&nn->IN[j]); } if(feof(p->lrn)) { rewind(p->val); k = 0; goto endfile; } k++; exec(p,nn); printf("[+] Test n째 %u\n",k); switch(p->print_mode) { case DUMP: dump(p,nn); break; case MAP: map(p,nn); break; } printf("[+] Continue? <y/n> "); scanf("%s",answer); if( (!strncmp(answer,"n",1)) || (!strncmp(answer,"N",1)) ) return; else system("clear"); } } /***************************************** * Collect parameters ******************************************/ struct param* parameters_parser(int argc, char**argv) { if(argc < 4) return NULL; struct param *p = new(struct param); p->pattrn_size = atoi(argv[1]); if(!(p->lrn_file = new(char[MAX_PATH]))) return NULL; strncpy(p->lrn_file,argv[2],MAX_PATH - 1); if(!(p->val_file = new(char[MAX_PATH]))) return NULL; strncpy(p->val_file,argv[3],MAX_PATH - 1); p->iterations = atoi(argv[4]); Matteo Tosato - Introduzione alle reti neurali artificiali

- 1]); - 2]); + 1]); + 2]);


60 if(!strcmp(argv[5],"-d")) { p->print_mode = DUMP; } else if(!strcmp(argv[5],"-g")) { p->print_mode = MAP; } else p->print_mode = 0; return p; } /***************************************** * Weights initialization ******************************************/ void Init_weights(struct param *p, struct SOM *nn) { srand((unsigned int) time(NULL)); for(unsigned int i = 0; i < NUM_OUTPUT_N; i++) { for(unsigned int k = 0; k < p->pattrn_size; k++) { nn->WEIGHTS[k][i] = fabs(((rand()%20000/10000.0)-1.0)+(rand()%10000/100000000.0f)); } } } /***************************************** * Dump values of input and output neurons ******************************************/ void dump(struct param* p, struct SOM* nn) { printf("[+] Neural networks simulation results:\n"); printf("\n\n[+] INPUT NEURONS:"); for(unsigned int i = 0; i < p->pattrn_size; i++) { printf("%lf ",nn->IN[i]); } printf("\n\n[+] OUTPUT NEURONS:"); for(unsigned int k = 0; k < NUM_OUTPUT_N; k++) { printf("%lf ",nn->OUT[k]); } printf("\n\n"); // Send network results to log file #ifdef LOG u_int j = 0; FILE* log_file = fopen("Results.txt","a"); fprintf(log_file," --- SON results: --- \n"); fprintf(log_file,"Input:\n"); while(nn->IN[j] > 0) { fprintf(log_file,"%lf;",nn->IN[j++]); } fprintf(log_file,"\nOutput:\n"); for(j=0;j < NUM_OUTPUT_N;j++) fprintf(log_file,"%lf;",nn->OUT[j]); fclose(log_file); #endif } /***************************************** * Build a simple graphic Kohonen map ******************************************/ void map(struct param* p, struct SOM* nn) { int c = 0, r = 0; printf("\n\n\n"); printf("INPUTS:\n"); for(u_int j = 0; j < p->pattrn_size; j++) { printf("%lf\n",nn->IN[j]); } printf("\n\n"); printf("0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9\n"); for(u_int j = 0; j < NUM_OUTPUT_N; j++) { if(nn->OUT[j] == nn->OUT[p->winner]) printf("*"); else printf("-"); if(++c == 10) { printf(" %d\n",r++); c = 0; } else { Matteo Tosato - Introduzione alle reti neurali artificiali


61 printf("---"); } } printf("Winner neuron: %d",p->winner); printf("\n-----------------------------------------\n"); }

Di seguito sono mostrati i valori in input, il loro valore durante e al termine del processo di stretching.

0,34

1° SET 0,24

0,34

0,50

0,40

0,57

0,80

0,70

1,00

0,41

0,31

0,44

0,10

0,00

0,00

001 Input 001

str 1° Dopo stretching

000 1

0,23

2° SET 0,00

0,00

0,56

0,33

0,44

0,98

0,75

1,00

0,62

0,39

0,52

0,23

0,00

0,00

2

3

4

5

001 Input 001

str 1° Dopo stretching

000 1

0,20

3° SET 0,00

0,00

0,20

0,00

0,00

0,30

0,10

1,00

0,20

0,00

0,00

0,20

0,00

0,00

2

3

4

5

001 Input 001

str 1° Dopo stretching

000 1

0,45

4° SET 0,44

0,49

0,16

0,15

0,17

0,01

0,00

0,00

0,45

0,44

0,49

0,90

0,89

1,00

2

3

4

5

001 Input 001

str 1° Dopo stretching

000 1

0,90

5° SET 0,89

1,00

0,70

0,69

0,78

0,30

0,29

0,33

0,12

0,11

0,12

0,01

0,00

0,00

2

3

4

5

001 Input 001

str 1° Dopo stretching

000 1

Matteo Tosato - Introduzione alle reti neurali artificiali

2

3

4

5


62

0,23

6° SET 0,00

0,00

0,40

0,17

0,24

0,76

0,53

0,74

0,90

0,67

0,93

0,95

0,72

1,00

001 Input 001

str 1° Dopo stretching

000 1

0,01

7° SET 0,00

0,00

0,30

0,29

0,42

0,70

0,69

1,00

0,40

0,39

0,57

0,10

0,09

0,13

2

3

4

5

001 Input 001

str 1° Dopo stretching

000 1

0,80

8° SET 0,40

0,70

0,50

0,10

0,18

0,40

0,00

0,00

0,70

0,30

0,53

0,97

0,57

1,00

2

9° SET 0,00

0,00

0,40

0,00

0,00

0,40

0,00

0,00

0,50

0,10

1,00

0,40

0,00

0,00

10° SET 0,80

1,00

0,60

0,50

0,63

0,30

0,20

0,25

0,30

0,20

0,25

0,10

0,00

0,00

Input str 1° Dopo stretching 000 2

0,20

0,00

0,20

0,00

0,00

0,30

0,10

0,17

0,50

0,30

0,50

0,80

0,60

1,00

3

4

5

001 Input 001

str 1° Dopo stretching

000 2

3

4

5

001 Input 001

str 1° Dopo stretching

000 1

11° SET 0,00

5

001

1 0,90

4

001

1 0,40

3

2

3

4

5

001 Input 001

str 1° Dopo stretching

000 1

2

3

4

5

La seguente è la mappa di Kohonen con i risultati, questi sono relativi ai vari set di verifica qui sopra con il colore uguale. 0

1

2

3

4

Matteo Tosato - Introduzione alle reti neurali artificiali

5

6

7

8

9


63 11

0 1 2 3 4

1 , 5 , 10

6,7

5 6 7

4,8

2 ,3 9

8 9

Come è evidente dagli input rappresentati nei grafici, la fase di stretching è importante per diminuire l‟influenza dei valori assoluti dei dati in input e rendere importante l‟andamento del profilo altimetrico. Se analizzate i valori di pendenza, possiamo concludere che la rete ha classificato i set in modo abbastanza corretto. Partendo dal codice e dal modello visto, è facile aggiungere l‟apprendimento supervisionato. Questo si fa andando a verificare se la classificazione della rete è corretta o meno. In caso affermativo, i pesi del neuro-nodo vincente e di tutto il suo vicinato vengono aumentati, nel caso opposto i pesi andranno indeboliti.

Competitive learning reale Il codice esemplificativo mostrato nel capitolo precedente prevede una notevole semplificazione che viene fatta nelle configurazioni SOM. Rivediamo invece l‟architettura di una rete SOM reale; La parte principale di questa configurazione è costituita da una mappa, o comunque da un vettore di neuroni che sono, tra loro, completamente interconnessi. Questa caratteristica mette già in conto una certa difficoltà realizzativa. Infatti dati 2000 neuroni avremmo sinapsi da tenere in considerazione nei calcoli. Nell‟esempio evitavo questo problema simulando un vicinato virtuale ogni volta che dovevo effettuare l‟aggiornamento dei pesi. Il disegno seguente mostra invece una architettura completa:

Matteo Tosato - Introduzione alle reti neurali artificiali


64

Ys*s Ys+1

...

Y0

...

Layer di output (mappa di Kohonen)

Ys

Layer di input

Figura 18: Apprendimento competitivo per il neurone Y0

Sono presenti tutte le connessioni sinaptiche che vanno dal layer di input alla mappa di Kohonen e anche tutte le connessioni laterali di uno dei neuroni topologici. Topologico perché il layer di output è disposto su un piano, la mappa appunto. L‟indice s è pari al lato della mappa. Oltre che essere connesso con tutti gli altri neuroni del piano, ogni neurone possiede una connessioni anche con se stesso. Il funzionamento della rete tenendo in considerazione tutte queste caratteristiche aumenta l‟impegno computazionale richiesto. Di seguito rivedo tutti i calcoli e le considerazioni fatte precedentemente riguardo le som, cercando di attenermi a questo modello reale. Comunque non dimentichiamo che molte volte non è necessario questo approccio complesso, ma è sufficiente attenersi alla versione semplificata. - Per prima cosa occorre inizializzare i pesi delle sinapsi, a differenza delle sinapsi tra input ed output che si inizializzano nel solito modo con valori casuali tra 0 e 1, le connessioni laterali avranno inizialmente tutte un valore uguale dato dalla relazione:

dove NY è il numero dei neuroni che compongono la mappa. Per costruire

la mappa ricorro a 400 neuroni (mappa di 20*20 neuroni). - Vengono inizializzati i valori di uscita dei neuroni di input. Per quanto riguarda il nostro problema dei profili altimetrici abbiamo bisogno di 5 neuroni in ingresso. (nelle applicazioni pratiche in genere si hanno un numero di neuroni in ingresso e in uscita molto più elevato rispetto ai numeri citati nel nostro esempio.) - Il primo calcolo da affrontare è quello della distanza tra la generica unità e l‟input. L‟equazione seguente esprime il calcolo della distanza:

√∑( In questa formula, gamma è il “fattore di variabile locale del neurone, quindi ogni inizializzato a priori, in genere in modo Delta invece dipende dall‟epoca corrente,

Matteo Tosato - Introduzione alle reti neurali artificiali

)

(

)

coscienza del neurone”. Questo è una neurone avrà il suo fattore di coscienza casuale. Non cambierà durante l‟esecuzione. varia secondo la relazione seguente:


65

esprime la frequenza di vincita del neurone, quini più esso vince, più è probabile accada ancora per input simili, innalzando e quindi definendo, l‟area di attivazione. L‟uscita finale del neurone di output è data da:

- Dopo che ogni neurone ha il suo valore di uscita, essi cominceranno a competere tra loro (come nelle reti biologiche). La competizione neuronale nello strato di uscita ha come obiettivo eleggere un vincitore e un “vicinato”. Il vicinato può essere più o meno esteso, relativamente alle dimensioni della rete. Nel caso della nostra rete 20*20 un vicinato iniziale di 2 o 3 neuroni (caselle nella mappa) è sufficiente. - A questo punto ogni neurone di uscita prima di tutto rinforzerà se stesso secondo l‟equazione:

Mentre verrà inibito da tutti gli altri secondo quest‟altra relazione:

( ) Dove rappresenta tutti gli altri neuroni. “A” rappresenta un certo potenziale responsabile del valore finale del neurone, dato generalmente da una funzione a rampa. La funzione rampa che ci occorre è molto semplice:

( )

2

Quindi:

( ) Dove è definito come “primo” perché deve essere confrontato con il valore di uscita attuale del neurone, finche essi saranno molto diversi l‟iterazione viene ripetuta per ogni neurone nuovamente (ultimo punto). (Nella libreria realizzata ho evitato di attendere una uguaglianza esatta tra il vecchio e il nuovo valore, perché genera un enorme numero di cicli da compiere. Ho preferito usare invece un‟espressione del tipo Matteo Tosato - Introduzione alle reti neurali artificiali


66 seguente: if (Math.Abs(t - OutputValue) >= 0.01). Questo mi ha evitato un eccessivo rallentamento di tutto il processo di apprendimento senza causare difetti nel funzionamento.) Quando la richiesta viene soddisfatta il valore del neurone viene impostato con - Una volta che la mappa di Kohonen raggiunge lo stato “stabile” un altro algoritmo ha il compito di ricercare attraverso la mappa il neurone vincente, ovvero quello con il valore più alto. Questo, assieme al suo vicinato, verrà “premiato” rinforzando le loro connessioni sinaptiche verso il layer di input. Il vicinato non rimane sempre al suo valore prestabilito inizialmente, ma diminuisce nel tempo, in modo da accentuare la punta della collina attivata nella mappa. La regola che stabilisce la sua evoluzione è:

(

)

Dove il valore delta riferisce sempre a quello citato prima. Quindi per ogni unità “vicina”. Compreso il neurone vincente vengono aggiornati i pesi sinaptici, ma solo delle sinapsi tra input ed output, quelle laterali non mutano:

(

)

Dove epsilon è il learning rate, ancora non citato. Questo è una costante globale nella maggior parte dei casi. Nella mia libreria, applicata alla classificazione dei profili altimetrici, ho ottenuto risultati soddisfacenti quando questo è impostato a 0.5 / 0.65. - Una volta aggiornati i valori sinaptici del neurone vincente e del suo vicinato, manca l‟ultima operazione, ovvero l‟aggiornamento della frequenza di vincita. Per tutta l‟area vincente avremo:

(

)

Mentre per tutti gli altri neuroni avremo un leggera diminuzione della costante, che però avrà limite inferiore maggiore o uguale a 0. (per non avere frequenze negative. Inoltre specifico che la frequenza di vittoria iniziale per tutti i neuroni è 0).

Reti SOM di grosse dimensioni sono piuttosto lente se addestrate con algoritmi di questo tipo. Esse mostrano in questo caso, una analogia più completa verso le cugine biologiche. Anche in questo caso la rete neurale ha come scopo principale la classificazione di pattern.

Matteo Tosato - Introduzione alle reti neurali artificiali


67

Problemi e strategie Neuro-fuzzy Le reti neurali, sono utilizzate nel campo della ricerca scientifica nel tentativo di riprodurre almeno in parte il funzionamento del cervello dell‟uomo. In campo applicativo o se volgiamo industriale, le reti hanno assunto nell‟ultimo decennio una posizione di rilievo, ed il loro utilizzo e potenzialità diventa sempre più presente nelle aziende tecnologiche. Molte volte le reti vengono affiancate a sistemi fuzzy, magari proprio dove questi falliscono, negli esempi trattati abbiamo avuto a che fare con problemi molto semplici, dove in realtà il problema poteva essere risolto anche senza una rete neurale. Molto spesso i sistemi fuzzy implementati nel modo classico presentano in molti punti del loro funzionamento varie criticità. Il numero dei controlli sui parametri può crescere di molto, peggiorando le performance. In questi casi la rete neurale trova applicazione. Pensate ad un complesso algoritmo di classificazione, questo può presentare in alcuni suoi punti di funzionamento delle criticità anche mai identificate durante la fase di test del sistema. Queste criticità possono presentarsi durante l‟utilizzo del prodotto da parte di un cliente finale. La rete neurale diminuisce questo rischio dato che non avrà nessun bisogno di sapere come trattare i dati in input. La rete neurale può, come abbiamo visto oramai piuttosto bene, imparare la soluzione corretta per quel problema, inoltre rimanendo una struttura dati “semplice”, non può generare titubanze nel suo algoritmo che rimarrà sempre lo stesso. La prima combinazione tra reti neurali e sistemi fuzzy logic di cui parleremo, definisce la NN come struttura dominante. E il sistema fuzzy come assistente al sistema di addestramento. Un sistema fuzzy può ad esempio intervenire sull‟aggiornamento dei vari coefficienti di training di una Error Back Propagation, come il learning rate o momentum, ad ogni presentazione di input. Il sistema fuzzy si servirà allora di algoritmi euristici per determinare il tipo di variazione a cui sottoporre i coefficienti. Lo stesso procedimento può essere applicato alle reti di Kohonen. In altre situazioni si parla di “equivalenza neuro-fuzzy”. Facciamo un esempio, abbiamo due input e e un output Y. Ciascuno dei due punti ha tre funzioni di membership, rispettivamente: , e l‟output sia a singleton. Il seguente è lo schema funzionale:

Matteo Tosato - Introduzione alle reti neurali artificiali


68

M11

R1

X1

X2

FUZZY 1

FUZZY 2

M12

M13

M21

M22

...

R2

M23

R9 R9 = M13 M23

R1 = M11 M21

U9 U1

Z1 = 1 / ƩRk

U2

Z2 = Ʃ(Uk Rk)

Y = Z1 Z2

Il sistema ha 6 strati, - Lo strato di input con e . - Il secondo strato ha due sezioni chiamate “pseudo-reti”, queste possono essere delle reti neurali o neuro-fuzzy a loro volta non definite ma atte a generare le 6 funzioni membership „M‟ citate prima. - Il terzo strato sono proprio le 6 funzioni membership - Questi definiscono le 9 regole secondo le seguenti equazioni:

Queste secondo le seguenti regole formano

e

.

∑ ∑(

)

Con k = 1,2,..,9 come avete visto dalle formule, in questo caso adottiamo anche i pesi „U‟. Mentre nel caso delle regole precedenti abbiamo adottato solamente la moltiplicazione. - Infine abbiamo il neurone di output „Y‟ che esegue il prodotto tra i due valori „Z‟. Questo esempio ha gli strati uno, due e tre che eseguono la “fuzzificazione”, il quarto strato esegue le inferenze e gli ultimi 2 strati effettuano la defuzzificazione. Matteo Tosato - Introduzione alle reti neurali artificiali


69 Questo tipo di configurazioni possono arrivare ad essere molto molto complesse, forse perdendo un po‟ della praticità e semplicità delle reti neurali pure. Invece è frequente vedere configurazioni neurali dove gli input di una rete sono output di un‟altra rete neurale. Sfruttando le diverse configurazioni e caratteristiche delle reti, è possibile risolvere molti tipi di problemi. Una rete neurale, una volta che il suo funzionamento è stato testato e addestrata per un certo compito è definibile come una “black box”. Quindi, come è frequente fare nella progettazione software, una rete viene trattata come oggetto e utilizzata per comporre un sistema più grande che ha bisogno di sfumare i suoi valori. Per il sistema precedente si applica l‟algoritmo EBP per trovare i valori con cui aggiornare i pesi.

(

)

e

(

)

( )

( )

(

)

Reti ART Questo tipo di rete neurale è interessante circa la capacità di apprendere continuamente e auto-modificarsi all‟occorrenza. Questa categoria di reti si suddividono a loro volta in ART1, le quali trattano solo input binari, ART2, con input analogici, Fuzzy-ART che usa le operazioni logiche fuzzy ed infine HART che esegue classificazioni e gerarchie. Le reti ART hanno comunque complessità minore rispetto le precedenti, ma sono molto utilizzate per risolvere problemi in cui si deve applicare la logica fuzzy. Sono caratterizzate da un buon compromesso stabilità-plasticità. Per stabilità si intende la capacità di non dimenticare quello che è già stato appreso dagli input precedenti. Infatti normalmente le comuni configurazioni si modificano secondo gli input che ricevono senza tenere traccia degli stati sinaptici passati. Per plasticità si intende invece la capacità di auto-modifica in funzione ai nuovi input o alla necessità di saper fare nuove classificazioni sui dati. Vedremo anche che queste reti possono aumentare o diminuire il numero dei neuroni nei suoi strati. Queste reti sono nate con lo scopo di riprodurre una caratteristica importante delle reti neurali biologiche. Ovvero la capacità di apprendere senza dimenticare i modelli precedenti. Si può ad esempio pensare alla capacità del cervello, questo ricorda volti visti non recentemente anche dopo averne acquisiti molti altri nuovi nel frattempo. Nelle reti invece abbiamo visto che i nuovi modelli imparati sostituiscono quelli precedenti. Questa rete riproduce il sistema biologico di memoria a breve termine e lungo termine. Questi meccanismi sono siglati rispettivamente STM e LTM. La prima apporta cambiamenti graduali nella seconda. La rete è costituita in due strati, ed . Questi sono interconnessi da pesi sinaptici top-down e botton-up. Essi costituiscono la STM. Il sistema di apprendimento è semplice; i pesi sono inizializzati ad uno, = . Mentre l‟opposta sinapsi dove N è il numero di neuroni dello strato . Gli input sono presentati allo strato . Vengono calcolate le funzioni di scelta:

|

( )

|

| |

Dove è il parametro di scelta il vettore dei pesi sinaptici, il simbolo |A| denota la norma del vettore A, cioè la sommatoria dei suoi componenti scalari: |A| = ∑ , l‟operatore logico fuzzy: equivale a MIN(X,W), cioè:

|

|

(

).

Le classificazioni quindi avranno luogo nel secondo livello Matteo Tosato - Introduzione alle reti neurali artificiali

. Il nodo di questo


70 livello che ha il maggior valore viene prescelto come categoria di X, purché venga soddisfatto il criterio: (di vigilanza)

|

| | |

Dove p è il parametro di vigilanza < 1. Lo schema della rete: F2

M neuroni

... LTM, Wf

j

LTM, Wb F1

N neuroni

...

i

Se il criterio di vigilanza viene soddisfatto i pesi sinaptici vengono aggiornati:

(

)

(

)

Nel caso in cui il criterio di vigilanza non è soddisfatto si crea un nuovo nodo, ovvero un nuovo neurone. Il suo peso sinattico verrà inizializzato con l‟equazione appena mostrata. Quindi viene ripresentato l‟input nuovamente fino a che la stabilità non viene raggiunta. Concludendo questa panoramica sulle ART, abbiamo appreso che in ogni caso il lavoro di queste è molto simile a quello delle MLP con EBP come algoritmo di apprendimento. Inoltre queste ultime abbiamo visto essere molto utili quando il numero di classi possibili non è predeterminato. Di conseguenza le reti ART risolvono questo particolare problema.

Metodologie di progettazione Siamo arrivati a trattare alcune tra le più utilizzate configurazioni di reti neurali. Tra queste abbiamo parlato, dandone buona importanza, il percettrone, il quale ricordo nonostante le limitate capacità di separazione, può essere impiegato largamente in problemi di classificazione, le reti multilivello MLP con algoritmo di apprendimento EBP, molto onerose in risorse hardware e più complesse dal punto di vista matematico, le reti di Kohonen con la loro capacità auto-organizzante. Infine abbiamo speso un po‟ di parole anche nel fare una analogia tra le reti neurali e la logica fuzzy. Queste due tecnologie abbiamo visto essere integrabili e in grado di dare entrambi risultati “sfumati”. In questa parte invece faremo un discorso meno tecnico che verte a definire i criteri di progettazione e di scelta che oramai dovrebbero essere divenuti intuibili. Faremo riferimento in particolare alle reti MLP, essendo quelle più utilizzate in ambito pratico. Definibili in punti, i seguenti sono gli argomenti da trattare: 1. 2. 3. 4.

Scelta dell‟applicazione. Analisi. Pianificazione delle risorse. Definizione di un criterio per le performance della rete che si desidera.

Matteo Tosato - Introduzione alle reti neurali artificiali


71 5. 6. 7. 8.

Progetto della rete neurale. Preparazione degli esempi. Processo di apprendimento. Valutazione finale ed applicazione.

1) Le reti neurali sono convenienti quando l‟algoritmo non si conosce, oppure quando pensare ad una soluzione algoritmica tradizionale è troppo complesso e dispendioso. Quindi non vengono applicate quando esistono già algoritmi noti. Ad esempio, negli esempi fatti per l‟OR ed XOR, la rete neurale non era affatto necessaria dato che la procedura per eseguire un OR oppure un OR esclusivo è ben nota. Più utile invece nel caso del riconoscimento dei profili altimetrici. Riepilogando, una rete neurale è conveniente quando l‟applicazione è “a intensità dei dati”. Piuttosto che a “intensità di calcoli” e i dati sono prevalentemente qualitativi, imprecisi e disturbati. Ai risultati di una rete neurale è consentito essere imprecisi ed in un certo range. L‟applicazione implementata a reti neurali non può essere “mission critical”. Dato che queste possono dare risultati imprecisi e parziali. Non devono avere potere decisionale. Una rete fornisce un dato qualitativo, un andamento previsto, oppure un dato certo in percentuale. Ciò nonostante una rete può svolgere compiti impossibili per programmi sequenziali. Come per esempio imparare a fare nuovi tipi di operazioni per cui non erano state programmate. Adattarsi alla nuova situazione, correggere gli errori, ricostruire un pattern danneggiato senza sapere la sua forma originale. Ciò che fa il cervello umano. 2) Come abbiamo visto, una applicazione solitamente non è interamente realizzabile con un rete neurale. Une applicazione viene solitamente organizzata con moduli in cascata. Una parte chiamata “ragionatore” può essere costituita da un algoritmo esperto fuzzy, e da una parte a rete neurale che da al sistema la facoltà di “percepire” informazioni nuove dall‟ambiente. Consideriamo un esempio reale; il “Phonetic Typewriter” è un sistema ideato dallo stesso Kohonen. Si tratta di una macchina da scrivere comandata a voce. Essa comprende 3 moduli in cascata; il primo si basa su algoritmi standard ed esegue la conversione analogico / digitale e poi l‟analisi di Fourier del segnale vocale in input (FFT = Fast Fourier Transform); il secondo modulo è una rete autoorganizzante di Kohonen, questo ha il compito di classificare i fenomeni in input. Il terzo ed ultimo modulo raffina i risultati ottenuti dalla SOM, ovvero verifica i risultati dal punto di vista grammaticale. L‟accoppiamento in cascata di un elemento “ragionatore” ed una rete neurale si presta molto bene nelle applicazioni dove sono necessarie due parti di sistema, una per dedurre, da un suono o immagine, ed una per ottenere un dato preciso basato su questa deduzione. Questa composizione a cascata può essere estesa anche agli stessi moduli di reti neurali, molte volte composti da più reti in cascata. Come abbiamo già detto, output di alcune reti possono divenire input di altre. 3) Questo punto parla unicamente delle risorse umane vere e proprie, nel caso lavoriamo in un team. In questo momento non ci interessa. 4) Anche il quarto punto lo possiamo tralasciare, riguarda la definizione di un criterio di performance. Nel senso che la rete neurale da creare deve apportare un reale beneficio. Questo deve essere noto a priori. Lo tralascio perché mi sembra troppo ovvio, ritengo che da capire il discorso spese/guadagno sia semplice. 5) Il progetto della rete è invece parte fondamentale. Richiede innanzi tutto la definizione degli input e degli output e degli attributi degli input. Se venissero trascurati questi fattori durante la progettazione di una rete neurale, si può ottenere un risultato insufficiente. Ovvero la rete non funzionerà. Una cosa molto importante da tenere sempre a mente è che per una rete, la presentazione e la qualità degli input durante il training è fondamentale. Molto spesso gli input hanno bisogno di essere “normalizzati” per essere adatti a divenire input della rete neurale. Gli input, se sono ad esempio simboli, possono essere codificati in numeri attraverso una semplice codifica binaria. Qui è bene precisare che non si tratta della consueta codifica Matteo Tosato - Introduzione alle reti neurali artificiali


72 binaria che siamo portati a pensare. La codifica seguente non andrebbe bene: questo perché la rete neurale tenderebbe a “raggruppare” i valori come simili più di quanto farebbe con ad esempio. In questo caso bisogna adottare un codifica detta “binaria estesa”. Ad esempio: In questo modo i valori sono “distanti” uguali. Nel caso delle reti possiamo pensare che si attivi solo un input, sempre in una diversa posizione per ogni simbolo. La decodifica può avvenire ad esempio nel modo seguente, noti e : V =

e

(

)(

)

Poi c‟è da pensare al numero degli strati nascosti da utilizzare nel caso delle reti MLP e dal numero di neuroni per ognuno di questi livelli. Per determinare il numero degli strati ci viene incontro un metodo abbastanza rigoroso. Ricordate quando abbiamo parlato della proiezione degli input nello spazio n-dimensionale? Solitamente quando la separabilità ha una iper-superficie convessa, basta un solo strato; in caso di separabilità con iper-superficie concava saranno necessari due strati. Tuttavia non esiste nessuna certezza matematica di questo, è comunque preferibile avere un solo strato di neuroni nascosti, dato che con il crescere di questi, il numero dei pesi sinaptici da aggiornare cresce anch‟esso, aumentando il costo in termini di risorse hardware necessarie. E‟ anche vero che esiste un metodo, applicabile all‟algoritmo di addestramento EBP che valuta durante questo, la possibilità, necessità di aumentare o diminuire il numero dei neuroni. Questo metodo prende il nome di “Structural Learning”. Esistono due varianti, nella si ha una rete inizialmente con poche neuroni e neuroni intermedi vengono aggiunti secondo necessità, questo metodo è utile anche per aggiornare una rete neurale esistente. La seconda variante elimina neuroni, partendo ovviamente da una situazione di eccesso. Per questa esistono poi molte sotto varianti che non vedremo, uno può anche, una volta nel contesto della sua applicazione, inventare nuovi sistemi di adattamento strutturale automatici. Un altro elemento da scegliere con attenzione riguarda il tipo di funzioni di trasferimento da utilizzare. Noi abbiamo in precedenza visto ed anche utilizzato le più diffuse. E‟ possibile comunque applicare qualsiasi tipo di funzione. L‟importante è che siano continue, monotone e derivabili. Abbiamo già visto che non possono essere tutte lineari. Come nel caso delle reti MLP. Negli strati intermedi le funzioni non hanno scopo di essere lineari, come abbiamo visto per la rete XOR. 6) Siamo arrivati alla progettazione del training set, o degli esempi. Quando l‟apprendimento della rete è supervisionato, abbiamo bisogno di preparare un efficiente training set. Gli esempi devono essere preparati tenendo conto di fattori precisi. Nel caso si parte da dati esistenti, prima di inserirli come input, dobbiamo eliminare gli estranei e omogenizzare quelli necessari all‟applicazione, controllando le omonime (dati diversi con lo stesso nome), e le sinonime (dati uguali ma con nome diverso), controllare la congruenza delle unità di misura e la compresenza di dati gerarchici. Inoltre il numero di esempi deve essere dimensionato alla complessità del problema. Esistono regole più o meno corrette per fare una stima del numero di esempi necessari. Una di questi è la seguente:

Un altro fattore estremamente importante è fornire alla rete neurale input rumorosi. Ovvero non del tutto esatti. Ad esempio se l‟output corretto della rete per un determinata iterazione del training set è 0.5, noi possiamo fornire alla rete come valore atteso anche 0.48. In questo modo la rete addestrata è in grado di fare delle generalizzazioni più efficaci. In questo procedimento non bisogna però esagerare, dipende da quanto la generalizzazione è importante per la nostra applicazione, una grande capacità di generalizzare è accompagnata naturalmente anche ad una minore precisione. 7) Abbiamo già accennato in precedenza ai coefficienti di apprendimento, che come nel caso dell‟esempio del capitolo sulle reti MLP essi sono determinanti. Nelle reti MLP viene applicato l‟algoritmo BP in una delle sue varianti, la scelta del valore per learning rate e momentum non sono dettate da metodi rigorosi, sono comunque valori solitamente bassi, inferiori allo 0.5. Esiste poi la possibilità di implementare il comportamento del learning rate come “adattativo”. Quando l‟algoritmo rimane bloccato in un minimo locale, il learning rate viene aumentato / diminuito per dare il giusto Matteo Tosato - Introduzione alle reti neurali artificiali


73 apporto di precisione alla rete senza prolungare eccessivamente la durata dell‟addestramento. In conclusione il problema / caratteristica delle reti neurali è che non ci sono metodi precisi ne di progettazione ne di validazione, ci sono reti neurali adatte solo a determinati compiti, non riutilizzabili, è bene salvare le varie configurazioni che durante l‟utilizzo nelle applicazioni vengono aggiornate o cambiate, questo per poter ritornare nello stato precedente nelle eventualità che anche i fattori esterni ritornino ad uno dei loro stati passati. Una buona idea è secondo me inserire tra le routine di chiamata delle librerie / applicazioni neurali anche funzione per il salvataggio e ripristino dei pesi, o anche del numero di neuroni correnti per ogni strato.

Il problema dei dati in ingresso Forse il compito dove le reti neurali trovano maggior successo è quello che concerne la visione artificiale. Esiste da più di una dozzina d‟anni una libreria open source dedicata alla visione. OpenCV. Questa libreria permette la realizzazione di programmi in grado di riconoscere oggetti tramite una periferica, tipo una telecamera. Nel campo della videosorveglianza queste vengono pesantemente utilizzate. Si pensi ad esempio al problema del riconoscimento di una figura umana in una scena dinamica. Un compito assolutamente impossibile per un normale programma sequenziale. Oppure un sistema di riconoscimento targhe, oppure ancora un sistema per riconoscere i visi all‟ingresso di un ufficio. Dato che sarebbe una pazzia partire a progettare un sistema del genere dal singolo neurone (anche se qui non nego di aver parlato con persone potenzialmente in grado di farlo) dobbiamo ricorrere a queste librerie, che più che fornirci il sistema neurale, ci danno dei moduli già composti per trattare i dati che otteniamo dalle periferiche. Infatti uno dei maggiori problemi che incontriamo quando lavoriamo con le reti neurali consiste nel modo in cui trattare i dati in ingresso alla rete. Rimanendo sull‟esempio della visione artificiale, possiamo pensare alla qualità delle immagini in arrivo da una telecamera, esse possono arrivare con molteplici problemi e situazioni variabili; immagini buie, sfocate, disturbate etc... A questo problema occorre rispondere con faticose operazioni di correzione e revisione delle immagini in arrivo. Data una immagine devono esserne evidenziate le caratteristiche salienti che ci interessano. Se il nostro compito è quello di individuare all‟interno di un video una figura umana dobbiamo cercare di mettere in evidenzia proprio le caratteristiche che ci interessano. Ad esempio i contorni delle figure saranno di maggior importanza rispetto alla qualità dei colori. A questo proposito esistono filtri che ricalcano complesse e dispendiose operazioni matematiche sulle immagini, che sono ovviamente trattate come matrici di pixel. Questo problema del trattamento dei dati in ingresso esiste anche in tutti gli altri ambiti di impiego. Un‟altra questione è relativa a come codificare i dati in input. Ne abbiamo già accennato; non sempre i dati da analizzare hanno un formato adatto per costituire l‟input di una rete neurale. Le normalizzazioni o lo streching di questi valori spesso non è sufficiente o è comunque impossibile da eseguire. Immaginiamo di dover classificare delle forme di figure piane. Quello che è necessario è trovare un codifica adatta. Oltre rappresentarne la caratteristica, occorre mantenere anche i fattori di proporzione che le figure hanno tra loro, se questo non viene garantito, i risultati

Matteo Tosato - Introduzione alle reti neurali artificiali


74 generati saranno scorretti. Consideriamo una rete con tre input e le seguenti codifiche dei dati;

001

010

011

001

010

100

L‟esempio sopra mostra sulla prima riga una codifica errata, mentre sulla seconda una alternativa migliore. La prima codifica indicherebbe alla rete neurale un valore più pesante per la figura a stella, quindi nella computazione totale, essa tenderebbe ad avere molto più peso rispetto le altre. La seconda codifica invece si limita a evidenziare la caratteristica senza influire sul peso di essa.

Esempi di applicazioni pratiche Intrusion detection Presentiamo ora una serie di concetti strettamente legati alle conoscenze nel campo delle telecomunicazioni, dei protocolli di rete e della sicurezza informatica. Cercherò comunque di indicare delle linee generali applicabili anche ad altri ambiti, ma è sicuramente ideale avere la conoscenza informatica di base almeno per quanto riguarda reti e naturalmente, programmazione. Sarebbe stato forse peggio trattare degli esempi come l‟applicazione delle reti neurali nell‟analisi finanziaria, lì occorrerebbe davvero studiare molti concetti che riguardano l‟economia e la statistica per poi applicare le reti nella progettazione di sistemi che effettuano delle previsioni su tassi e tendenze. Questo dimostra l‟incredibile varietà di campi disponibili in cui questa tecnologia può essere impiegata. Recenti successi sono stati ottenuti nel campo medico grazie a reti neurali per il riconoscimento di tumori nelle radiografie, piuttosto che reti neurali impiegate nel campo delle biotecnologie. Due parole su cosa significa analizzare il traffico di rete; internet e le reti più in generale si basano su una serie di protocolli. I “protocolli” sono una convenzione che i programmatori devono utilizzare nella scrittura delle parti dei loro programmi che devono comunicare con altri computer nella rete. In questo modo è possibile garantire una adeguata “compatibilità” tra tutti i tipi di sistemi che devono poter comunicare indipendentemente dal tipo di piattaforma. Prima di proseguire raccomando di approfondire questo aspetto se non lo si conosce di già. Di seguito lascio alcuni spunti introduttivi. Saremo malgrado costretti ad affrontare questioni molto tecniche e specifiche della progettazione di questi protocolli, dato che noi dovremo monitorarne il transito, occorre averne una profonda conoscenza. http://it.wikipedia.org/wiki/Protocollo_di_rete http://it.wikipedia.org/wiki/Modello_OSI Matteo Tosato - Introduzione alle reti neurali artificiali


75 Alcuni cenni sulla programmazione di rete: http://scuola.linux.it/docs/altre_scuole/planck/socket/progr-socket4.html Dunque il problema che ci troviamo a dover risolvere non è poi dei più semplici. I seguenti punti mostrano le più comuni situazioni in cui il traffico di rete è considerato certamente anomalo: -

Flusso in entrata di un elevato numero di richieste di connessione. Flusso di un elevato numero di reply ARP senza precedenti richieste. Pacchetti con MTU superiore alla norma. Flusso elevato di richieste di ping. Pacchetti TCP desincronizzati.

Da queste semplici osservazioni è piuttosto complicato tentare di riconoscere se si tratta di pacchetti legittimi, errori o tentativi di attacco. Un grande flusso di nuove connessioni in entrata non è detto rappresenti sempre il tentativo di provocare l‟interruzione dei servizi. Potrebbe essere che l‟host è in effetti in un momento di grande utilizzo da parte dei client, oppure si potrebbe trattare di tentativo di profiling verso l‟host. La valutazione di questi aspetti può essere fatta con regole piuttosto semplici, non ci occorre utilizzare metodi fuzzy. Il nostro obiettivo è capire, a prescindere dalle informazioni inviate (cose difficile da fare senza rischiare di bloccare accidentalmente connessioni legittime) se si tratta di una connessione instaurata da un‟attaccante o da un regolare client. Almeno per quanto riguarda tutto il traffico TCP, che è anche la tipologia di pacchetti più abbondante che transita sulle reti. Sono necessarie delle osservazioni fatte sulle tempistiche di invio – ricezione. Dobbiamo tener presente che nella maggior parte dei casi gli attacchi vengono effettuati attraverso host “intermediari”. Chiamati anche bot. L‟attaccante infatti, prima viola altri server meno controllati e ne acquisisce il controllo. Poi da questi lancia il suo reale attacco. In questo modo esso aumenta la sua speranza di rimanere nell‟anonimato. Più host intermediari usa più è probabile che la sua identità rimanga non conosciuta. Molti tipi di approcci sono stati utilizzati nel corso degli anni per sfruttare la differenza che si ha nelle connessioni di questi attaccanti e quelle degli utenti normali. L‟elemento che andremo a prendere in considerazione ora è il “Round trip time” (RTT). Di cosa si tratta? Per capirlo dobbiamo tenere ben presente il funzionamento del protocollo TCP. Questo implementa un sistema di controllo per l‟affidabilità della trasmissione dei dati. Esso si basa su una conferma di ricezione da parte del ricevente. Il protocollo contiene difatti un campo “acknowledgement” atto a contenere il numero di sequenza del prossimo pacchetto che egli si aspetta di ricevere. In questo modo, i due host sono sincronizzati. E accetteranno pacchetti solo se essi risultano attinenti al range (la finestra di TCP) aspettato. Si veda un adeguato approfondimento all‟argomento. L‟RTT corrisponde al tempo di andata e ritorno di un pacchetto dati da un client al server. Ogni host tiene in memoria un tempo RTT previsto entro il quale l‟altro capo della connessione dovrà rispondere. Se la conferma di ricezione non arriva nel tempo previsto l‟host ritrasmetterà le informazioni. L‟RTT previsto viene calcolato nel modo seguente:

( )

(

(

))

((

)

)

corrisponde al tempo RTT appena campionato. Tcp è così in grado di adattarsi alla larghezza di banda e al traffico di rete, ed elimina il problema dei percorsi differenti che vari pacchetti TCP della medesima connessione possono prendere. Quando un pacchetto viene perso, non potendo il destinatario riceverlo esso non invierà conferma e il timeout scadrà, a questo punto il mittente rispedisce i dati, quando arriverà la conferma di ricezione, il mittente non ha modo di sapere se questa Matteo Tosato - Introduzione alle reti neurali artificiali


76 riferisce al primo o al secondo pacchetto inviato, comunque incrementerà l‟RTT perché considererà il primo pacchetto spedito. Tale fenomeno viene chiamato “ambiguità della conferma di ricezione” e le conferme di ricezione di TCP sono dette “ambigue”. Volendo si potrebbe fare in modo che TCP calcoli l‟RTT non dal pacchetto originale ma dalla ritrasmissione, ma in questo modo si avrebbe un problema ancora maggiore dato che l‟RTT finirebbe per diventare troppo breve a tal punto che TCP invierebbe ogni pacchetto almeno due volte generando il doppio del traffico. Quindi nonostante il problema dell‟ambiguità, TCP tende a tenere un tempo RTT “pessimista” come valore di timeout. Il seguente grafico mostra l‟andamento dell‟RTT durante un tipica connessione tra due host di una rete LAN:

Questo grafico non mostra il valore timeout RTT di TCP, mostra il valore RTT campionato sulla base dei pacchetti ricevuti. Vediamo di capire come possiamo utilizzare questi dati per i nostri scopi. Il calcolo del valore RTT permette di stimare il numero di host (quindi di elaborazioni) presenti sul tratto “downstream” della connessione. Determinando una certa “lunghezza” di connessione, possiamo capire se si tratta di una intrusione o meno. Il nostro “sensore” viene posizionato nel modo seguente:

Matteo Tosato - Introduzione alle reti neurali artificiali


77 Attacker

Normal client

C1

Sensor

Ci

Host1

Cn

Cn-1

Host2

Host3

Victim

Figura 19: Schema attacco informatico mediante host intermediari

Possiamo considerare ogni pacchetto TCP “outcoming” e il rispettivo “echo”, quindi calcolare la differenza temporale RTT del pacchetto in andata e ritorno. Questo tempo può essere influenzato da molteplici fattori. Dobbiamo considerare il tempo di propagazione ( ), quindi dipendente dalla distanza fisica, il tempo impiegato dal calcolatore per processare il pacchetto ( ), il tempo di accodamento che il pacchetto può avere nello stack TCP/IP ( ), e il tempo di trasmissione ( ). Quindi possiamo riassumere il calcolo del RTT nel modo seguente:

(

)

Generalmente è piuttosto costante per molti pacchetti. Quindi raggruppando il calcolo può essere inteso anche come:

Dove è il ritardo non variabile o costante e è il ritardo variabile definito da ( ). Se ci sono K connessioni, (o k hosts) tra l‟attaccante e la vittima, allora il valore rappresentato da rappresenta la parte di variazione di tutti i ritardi lungo la catena di connessioni tra questi hosts:

∑ Per poter calcolare questo valore dobbiamo iterare su ogni pacchetto in arrivo e se si tratta di un pacchetto echo, confrontare questo con il relativo pacchetto di invio, se non si tratta di un echo ma di un pacchetto outcoming, lo si accoda per essere “matchato” dopo. Lo pseudo-codice: (Han-Ching Wu And Shou-Hsuan Stephen Huang, Department of Computer Science, University of Houston) Initialize send queue; while(there are some packets) { read next packet P; { if P is a send packet { calculate TimeGap; if TimeGap > threshold reset Send queue; else insert packet P to Send queue; Matteo Tosato - Introduzione alle reti neurali artificiali


78 } else if P is an echo packet { if (P.size is large) do nothing; else { S = dequeue(Send); if((S.ack = P.seq) and (S.seq < P.ack)) { Match P and S; Compute RTT between P and S; } } } } } Sicuramente è una osservazione acuta il fatto di specificare che non tutti i valori RTT della connessione saranno uguali. Questo dipende dalla fluttuazione del traffico di rete. E‟ anche vero però che analizzando intervalli ristretti di tempo questo problema scompare e tutti gli RTT saranno più o meno simili. Sulla base di questo è possibile riconoscere, o meglio definire vari livelli del valore RTT e di conseguenza, stimare il numero di “stepping-stones” (gli host presenti lungo la connessione). I vantaggi di usare l‟intelligenza artificiale (NN, neural networks) invece di algoritmi esperti fuzzy sono innanzi tutto una notevole flessibilità; questa è importante perché le situazioni nelle quali un host si può trovare sono molto varie. Le nn possiedono anche la capacità di analizzare dati non-lineari con multi-variabilità. Non dovremo considerare connessione per connessione ma solo i pacchetti in se. Quando vedremo una serie di valori RTT assolutamente fuori scala sapremo che quella connessione è ipoteticamente una intrusione che sta utilizzando vari host intermediari per nascondere la sua identità. Un messaggio sarà reso all‟amministratore che effettuerà il dovuto controllo approfondito. Date le diverse situazioni possibili in cui gli host si possono trovare, la rete neurale è la soluzione migliore circa l‟adattabilità del sistema. Vediamo per step le operazioni da compiere: 1 Per prima cosa occorre creare il sistema per il capture dei pacchetti ed il filtraggio. Tramite una call-back i pacchetti TCP send o echo devono essere inseriti in una coda pronti per essere processati. 2 Poi, secondo l‟algoritmo presentato prima, occorre calcolare il valore RTT campionato, dobbiamo poi raggruppare questi in righe di n valori, dove n corrisponde al numero di neuroni della rete neurale nel layer di input. Se per esempio abbiamo 500 valori RTT e 10 neuroni in ingresso, organizzeremo gli RTT in 50 righe, ovvero 50 set per la rete. Ognuna di queste righe viene valutata dalla rete. 3 Una volta che la rete ha elaborato i set in input è in grado di valutare il numero di “stepping-stone” presenti e confrontando questo valore con una media su tutti i pacchetti è in grado di individuare le connessioni che stanno molto probabilmente utilizzando sistemi intermediari per accedere. Poi queste possono essere ulteriormente filtrate in base a criteri che eliminano evidenti falsi allarmi. Ma la cosa interessante che occorre focalizzare è l‟estrema adattabilità del sistema senza effettuare la ricompilazione, sarà sufficiente addestrare la rete in modo diverso, per avere un comportamento differente. Il tipo di rete neurale che può essere impiegato non è restrittivo al patto che sia una feed-forward, dato il compito di “classificazione” che gli spetta. I neuroni nascosti e il neurone di uscita devono avere una funzione non lineare. Può andare bene la Matteo Tosato - Introduzione alle reti neurali artificiali


79 tangente, la sigmoide o anche la funzione seno. Un esempio di configurazione con 10 neuroni di input: x0

x1

x2

Y(Ʃ(x*w))

x3

x4

Y(Ʃ(x*w))

x5 Y(Ʃ(x*w))

NetOut = Y(Ʃ(Y(Ʃ(x*w1)*w2)))

X6

X7 Y(Ʃ(x*w)) X8

X9

Figura 20: Rete MLP per la valutazione degli RTTs

Scendiamo nel dettaglio della progettazione di un sistema di questo tipo. Nella maggior parte dei casi, le spiegazioni teoriche di una cosa nascondono una miriade di questioni pratiche che solitamente vengono tralasciate. Di seguito vediamo come realizzare i vari moduli del nostro programma. Diamo per assunto che la cosa può essere fatta sia come programma munito di interfaccia utente grafica, (come ho fatto io per provare il sistema), o come modulo kernel. Detto questo cambia poco per i moduli principali eccetto forse il modulo di cattura che a livello kernel viene fatto in modo differente. Appunto il processo di cattura dei pacchetti è la prima cosa che dobbiamo affrontare. Un sistema di cattura è di fatto un driver in grado di passarci, tramite una funzione callback, ogni pacchetto che transita sulla scheda di rete o attraverso il nostro host in generale. Le modalità di esecuzione sono generalmente due, normale e promiscua. In modalità normale il traffico acquisito sarà relativo ad una particolare scheda di rete, tutti gli altri pacchetti che non sono indirizzati o non escono dall‟interfaccia non sono considerati. La modalità promiscua invece opera nel modo inverso, tutti i pacchetti vengono acquisiti indipendentemente dalla loro origine o dalla loro destinazione. Il pacchetto ci viene consegnato nella sua integrità, ovvero avremo un puntatore che punta al primo byte dello strato di collegamento, generalmente ethernet.

Ethernet

IP

Data ...

Anche se lavoriamo a livello kernel avremo lo stesso risultato, ma non lo faremo utilizzando dei driver di terze parti, nel caso di linux occorrerà utilizzare l‟interfaccia “netfilter” per ricevere in modo seriale assieme agli altri moduli di Matteo Tosato - Introduzione alle reti neurali artificiali


80 rete i puntatori ai pacchetti da analizzare. Inoltre lavorando a questo livello è possibile bloccare i pacchetti prima che essi transitino nella scheda. Per esempio, se il nostro modulo viene assemblato assieme ad un canonico firewall potremmo scegliere se fornire un semplice sistema di segnalazione o permettere al nostro modulo di intervenire in “real-time” sul traffico. Ho utilizzato i driver libpcap per realizzare il modulo. Ho inserito tutte le procedure in una classe appoggiandomi al framework Qt. Le funzioni native pcap da utilizzare sono in ordine: pcap_findalldevs(), per acquisire la lista di interfacce di rete disponibili. pcap_lookupnet(), per acquisire informazioni addizionali sull‟interfaccia. pcap_open_live(), per ottenere un handle di sessione. pcap_datalink(), per verificare il livello di collegamento. pcap_next_ex(), restituisce il prossimo pacchetto presente in stack. Tutte le funzioni sono descritte all‟interno della documentazione che viene fornita all‟interno dei sorgenti, oppure direttamente sul WEB all‟indirizzo seguente: http://www.winpcap.org/docs/docs_412/html/main.html Ogni pacchetto acquisito dovrà essere analizzato dall‟algoritmo proposto prima. Ricordo che comunque l‟analisi che andremo a fare è bene farla non su tutte le porte contemporaneamente, ovvero, è necessario filtrare i pacchetti in modo da non considerare tutto il traffico delle altre applicazioni che non siano i vari client di accesso remoto utilizzati dagli attaccanti, questi sono generalmente telnet, Open-ssh, Putty e pochi altri. E‟ assolutamente inutile acquisire tutto il traffico TCP riferito alla porta 80. Produrrebbe solo dei risultati confusionari dato che il traffico WEB produce migliaia di pacchetti al minuto. Ed è anche inutile perché non è possibile utilizzare quella porta da lato client se impegnata già a fare un altro tipo di servizio, sia da parte dell‟utente legittimo che dall‟attaccante. A meno che l‟exploit si trova nel web server, ma anche in questo caso, gli exploit aprono connessioni non di certo dalla stessa porta di servizio. L‟algoritmo lo abbiamo visto, qui vediamo come implementarlo in modo pratico. Innanzi tutto vediamo di definire che cosa si intende per “pacchetto Send e Echo”. Un pacchetto Send è definito come segmento in uscita dall‟interfaccia di rete dell‟host monitorato, o come in questo caso, dall‟host su cui il nostro programma stà girando, esso contiene dati. Questo pacchetto ha il flag ACK e PSH settato. Non ci interesseremo a quale connessione appartiene, abbiamo detto che non ci importa di avere anche questa variabile. Ci limiteremo a “match-are” tutti i pacchetti send ed echo. Recuperiamo il flusso di una comune sessione TCP nella quale due host si sono scambiati un certo numero di dati. Vi evidenzio i pacchetti Send ed Echo con i colori rosso e blu rispettivamente. |Time | |11,995 | |12,090 | |12,091 | |12,091 | |12,191 | |12,193

| 192.168.0.2 | | 91.203.96.252 | SYN | |(6206) ------------------> (443) | SYN, ACK | |(6206) <------------------ (443) | ACK | |(6206) ------------------> (443) | PSH, ACK - Len: 180 |(6206) ------------------> (443) | ACK | |(6206) <------------------ (443) | ACK - Len: 1360

Matteo Tosato - Introduzione alle reti neurali artificiali

| | |Seq | |Seq | |Seq | |Seq | |Seq | |Seq

= 0 = 0 Ack = 1 = 1 Ack = 1 = 1 Ack = 1 = 1 Ack = 181 = 1 Ack = 181


81 | |12,196 | |12,196 | |12,295 | |12,295 | |12,295 | |12,328 | |12,469 | |12,469 | |12,565 | |12,565 | |12,572 | |12,722 | |12,722 | |12,827 | |12,837 | |12,838 | |12,838 | |12,838 | |12,838 | |12,938 | |13,033 |

|(6206) | |(6206) | |(6206) | |(6206) | |(6206) | |(6206) | |(6206) | |(6206) | |(6206) | |(6206) | |(6206) | |(6206) | |(6206) | |(6206) | |(6206) | |(6206) | |(6206) | |(6206) | |(6206) | |(6206) | |(6206) | |(6206)

<-----------------ACK - Len: 1360 <-----------------ACK | ------------------> ACK - Len: 1360 <-----------------PSH, ACK - Len: 509 <-----------------ACK | ------------------> PSH, ACK - Len: 267 ------------------> ACK | <-----------------PSH, ACK - Len: 59 ------------------> ACK | <-----------------PSH, ACK - Len: 59 <-----------------PSH, ACK - Len: 901 ------------------> ACK | <-----------------PSH, ACK - Len: 517 ------------------> ACK | <-----------------PSH, ACK - Len: 229 <-----------------PSH, ACK - Len: 261 <-----------------ACK | ------------------> FIN, ACK | <-----------------ACK | ------------------> FIN, ACK | ------------------> ACK | <------------------

(443) (443) (443) (443) (443) (443) (443) (443) (443) (443) (443) (443) (443) (443) (443) (443) (443) (443) (443) (443) (443) (443)

| |Seq | |Seq | |Seq | |Seq | |Seq | |Seq | |Seq | |Seq | |Seq | |Seq | |Seq | |Seq | |Seq | |Seq | |Seq | |Seq | |Seq | |Seq | |Seq | |Seq | |Seq |

= 1361 Ack = 181 = 181 Ack = 2721 = 2721 Ack = 181 = 4081 Ack = 181 = 181 Ack = 4590 = 181 Ack = 4590 = 4590 Ack = 448 = 448 Ack = 4590 = 4590 Ack = 507 = 4590 Ack = 507 = 507 Ack = 4649 = 4649 Ack = 1408 = 1408 Ack = 4649 = 4649 Ack = 1925 = 4649 Ack = 1925 = 4878 Ack = 1925 = 1925 Ack = 5139 = 5139 Ack = 1925 = 1925 Ack = 5140 = 1925 Ack = 5140 = 5140 Ack = 1926

Abbiamo 3 valori RTT campionati. 0.204 s., 0.237 e 0.265. Nell‟algoritmo utilizziamo la regola: “if((S.ack = P.seq) and (S.seq < P.ack))” questa ci impedisce di sbagliare la corrispondenza dei due pacchetti. Riprendendo direttamente il mio codice avrò alcune routine che provvedono al riconoscimento degli strati e all‟accesso a questi tramite un sistema di puntatori e casting. Fino ad arrivare al riconoscimento del pacchetto Send, ovvero: if(ip->saddr == localip && (tcp->flags & 0x10) && (tcp->flags & 0x18)) { I flag che vado a controllare sono l‟ACK e PSH e naturalmente l‟indirizzo di origine, che deve essere il mio. Queste regole classificano il pacchetto come “Send”. A questo Matteo Tosato - Introduzione alle reti neurali artificiali


82 punto può essere inserito in un struttura FIFO come una coda. Infatti è il primo pacchetto send a dover essere abbinato al primo pacchetto echo che rispetta le seguenti regole: }else if((ip->daddr == localip) && (tcp->flags & 0x10) && (tcp->flags & 0x18)) { ... if((s_tcp->ack_n == tcp->seq_n) && (s_tcp->seq_n < tcp->ack_n)) { Soltanto a questo punto possiamo calcolare il valore RTT della coppia di pacchetti. I risultati dei campionamenti devono essere organizzati in “set” per la rete neurale. Ovvero dato il numero N di neuroni che abbiamo scelto per lo strato di input della rete dovremo organizzare i valori in array di N elementi e sottoporli alla rete. Se mi è concesso di generalizzare la struttura della rete, possiamo riassumere con questo schema: RTT1

RTT2 . . .

f

RTTN

Il valore di uscita della rete da un‟idea del numero di “stepping-stones” rilevati dalla serie di RTTs. Ci sono comunque delle note da fare su questo sistema. Non rappresenta una soluzione completamente sicura e applicabile in ogni circostanza. Immaginiamo un server che offre un servizio di accesso remoto con SSH, (anche se questo è abbastanza improbabile come servizio) ed esso sia pesantemente utilizzato. E‟ possibile che un attaccante si mischi nella folla sfruttando tutto il timeout di TCP prima di inviare il pacchetto successivo. Questo potrebbe indurre al fallimento il nostro sistema, dato che considererebbe i tempi RTT alti rilevati come “eccezioni” dovute ad anomalie; e proprio il punto di forza delle reti neurali rischierebbe di essere causa del fallimento del sistema di rilevamento. Pertanto in questi casi occorrerebbe anche tenere come riferimento la connessione in se e andare a analizzare gli RTT campionati solo relativi ad essa, questo per ogni connessione. Questo complica le cose e più che altro, aumenta notevolmente il costo in potenza di calcolo necessario per il sistema. Generalmente possiamo tenere monitorate le porte di servizio. Ad esempio, abbiamo un server web che può essere gestito da remoto tramite SSH, (questa è una situazione piuttosto comune per la maggior parte dei server di servizi internet) quindi il numero di connessioni previste sulla porta 22 sarà pari ad uno. Così, oltre a stabilire un numero massimo di sessioni SSH, il sistema sarebbe in grado di valutare se l‟accesso è tenuto da un client standard, oppure se esso sta utilizzando una lunga catena di connessioni per nascondere la sua identità. In questo ultimo caso la connessione può essere interrotta. L‟attaccante sarebbe così obbligato ad utilizzare una connessine diretta per l‟accesso e così facendo, sarebbe velocemente smascherato tramite i sistemi per la localizzazione dell‟IP utilizzabili dagli stessi provider proprietari delle linee. Dal punto di vista dell‟utente è sufficiente segnalare l‟IP registrato nei log che risulta autore di azioni sospette per far scattare tutta la questione.

Matteo Tosato - Introduzione alle reti neurali artificiali


83 Un sistema del genere è comunque supplementare ai sistemi anti-intrusione tradizionali. Si tratta di tecniche nuove e piuttosto diverse dalle solite ma non le sostituiscono, aggiungono solamente un certo grado di intelligenza. I seguenti sono i tempi RTT che ho rilevato per connessioni dirette in Rete LAN, ipotizzando di utilizzare una rete neurale a 6 neuroni di input, avremmo: RTT set n°1 0.140795s. 0.700669s. RTT set n°2 0.092423s. 0.092714s. RTT set n°3 0.099924s. 0.099725s. RTT set n°4 0.095503s. 0.099248s. RTT set n°5 0.097204s. 0.096408s. RTT set n°6 0.282706s. 0.099976s. RTT set n°7 0.099749s. 0.099919s.

0.093393s. 0.123039s. 0.121076s. 0.10092s. 0.100824s. 0.094254s. 0.09343s. 0.764306s. 0.092682s. 0.100902s. 0.100548s. 0.492595s. 0.095705s. 0.095733s. 0.095373s. 0.094035s. 0.099615s. 0.799309s. 0.651773s. 0.095147s. 0.119879s. 0.199972s. 0.200492s. 0.100925s. 0.101313s. 0.100984s. 0.100547s. 0.099287s.

I valori sono tutti più o meno simili e, salvo casi particolari, non sono superiori ai 200 ms. (una media di 100 ms.) Per questo motivo sarebbe meglio utilizzare più neuroni di input, magari tra 15 e 20. Per le connessioni provenienti da internet avremo valori leggermente più alti. Non c‟è un sistema univoco per effettuare la giusta valutazione (ovvero il tipo di addestramento da fare), perché le situazione può essere davvero varia. Per questo motivo la rete neurale risulta molto comoda, è sufficiente un riaddestramento per modificare il criterio di valutazione. Potremmo anche insegnare alla rete ad identificare solo le connessioni fidate. Nel programma dovrò pensare ad un pannello dove questi parametri possono essere settati. Dobbiamo rendere il sistema il più personalizzabile possibile. Ad esempio:

La schema di funzionamento del mio programma esemplificativo è infine il seguente:

Matteo Tosato - Introduzione alle reti neurali artificiali


84 Main IA config dialog

Capture module config

FIFO

Packet analyzer and dump

User Interface

IA pre-processor General statistics

~

~

~

Packet capture

Packets

FIFO

Puntatori pacchetti

Il modulo IA è quello che gestisce la rete neurale e si preoccupa di eseguire il rilevamento dei valori RTT anomali. In più si preoccupa di loggare tutta l‟attività su file. I thread principali sono 3. E‟ bene infatti sfruttare, la dove possibile, le architetture multi-core, dato che solo l‟elaborazione della rete neurale può arrivare ad essere dispendiosa se sono presenti molti neuroni nel livello nascosto.

Previsione di fenomeni complessi Le reti neurali possono essere utilizzate anche per prevedere il valore di una (previsione uni-variata) o più (previsione multi-variata) variabili sulla base di dati storici. Questa capacità è sfruttata molto nell‟ambito finanziario e statistico. Data una funzione ( ), solitamente ne si esegue l‟analisi per mezzo della scomposizione in componenti armoniche ideata dal celebre Fourier. Con le reti neurali è possibile fare a meno di questa tecnica effettuando un addestramento mirato a prevedere (o classificare se il nostro obiettivo non è la previsione), il comportamento della variabile nel tempo futuro. Nel caso della previsione multi-variata anche con serie di input differenti, è l‟insieme di valori delle sequenze storiche di tutte le variabili di input che concorre alla determinazione dell‟output di ognuna delle variabili su cui si vuole fare la previsione. Per questo motivo è possibile avere bisogno di un numero enorme di neuroni di input, rendendo difficile tutta l‟applicazione. Si preferisce, in questi casi, utilizzare le reti neurali ricorrenti. Queste, come abbiamo visto, hanno connessioni anche tra neuroni dello stesso livello, quindi la loro memoria sarà estesa anche agli input precedenti. Supponiamo di dover prevedere il comportamento di una singola variabile. L‟addestramento consisterà nell‟inserire in input alla rete una serie di valori assunti da x. In output la serie di valori che x assumerà. E così via per più serie, finché il Matteo Tosato - Introduzione alle reti neurali artificiali


85 target di precisione della rete non viene raggiunto. A questo punto la rete sarà in grado di prevedere valori successivi che x assumerà. Dobbiamo anche considerare che la rete non imparerà solamente la relazione che esiste tra i valori di input e quelli di uscita, ma anche quella esistente tra input consecutivi. e . Questo è un indice della derivata in quel punto della funzione. Di seguito lo schema ipotetico di una rete per la previsione uni-variata: x100 x101 x102 x103 x114 x104 x115 x105

x107

Rete neurale

x116 x106

x108

x117 x118 x119

x109 x120 x110 x111 x112 x113

Un davvero banale esempio può essere fatto con un segnale periodico. Naturalmente una cosa di questo tipo a senso solo per il fine didattico. Ma nella realtà possiamo applicare questa tecnica a qualsiasi segnale variabile difficilmente prevedibile. I maggiori tipi di analisi che vengono fatti attualmente, oltre essere complessi da un punto di vista realizzativo, incappano molto spesso in errori, dato che sistemi di analisi che si basano su algoritmi sequenziali non hanno la capacità di generalizzazione di una struttura neurale. Quindi all‟arrivo di dati in ingresso affetti da un disturbo molto spesso questi sistemi non sono in grado di funzionare correttamente. Un esempio di risultato potrebbe essere il seguente:

Matteo Tosato - Introduzione alle reti neurali artificiali


86 7 6 5 4 3 2 1 0 0

10

20

30 Futuro

40

50

60

Sorico

Il numero di neuroni in input, in uscita e la struttura del training set sono variabili che possono essere decise seguendo l‟equazione di convenienza seguente: (

)

Un altro aspetto importante è la normalizzazione. Non è infatti detto che i valori da analizzare rimangano all‟interno di un range adatto alla nostra rete. Per questo motivo deve essere fatta, anche in questo caso, una operazione di stretching sui valori.

Analisi di segnale Tipicamente l‟analisi di una qualsiasi segnale aperiodico viene fatta attraverso la trasformata di Fourier per suddividere la forma d‟onda in varie componenti. Attraverso l‟analisi in frequenza è possibile fare delle considerazioni sulle caratteristiche che ci interessano. Anche se, la rete neurale non avrà bisogno necessariamente di scomporre il segnale in più componenti, essa sarà comunque in grado di estrapolare le caratteristiche che desideriamo dal segnale proprio come lo possiamo fare noi con la nostra testa. Tutto dipende dall‟addestramento a cui la sottoponiamo preventivamente. Quando noi osserviamo la forma d‟onda e tiriamo le conclusioni su “che cosa rappresentava” il segnale in quel momento, facciamo anche noi delle associazioni come le fa la rete neurale. Una rete neurale per quanto semplice, ricalca quello che può fare il cervello. La lettura di un testo è un‟utile analogia, quando leggiamo non andiamo a valutare ogni lettera, ogni sillaba, suono, punteggiatura, sintassi presente etc… ma sappiamo già che suoni emettere per esprimere ciò che c‟è scritto. Questo perché siamo “abituati” a farlo, la conoscenza necessaria è già diffusa tra le connessioni neurali. (connessionismo) Lo stesso discorso vale per i sistemi basati su intelligenza artificiale, non abbiamo più bisogno di implementare enormi e complessi sistemi di analisi, che in molti casi sono anche irrealizzabili. Ciò non obbliga comunque a bypassare Fourier, in certi casi rimane molto utile isolare le caratteristiche del segnale per poi, in seguito, analizzarle con una rete. Le varie componenti ricavate della trasformata possono divenire input di una rete neurale feedforward o di altra configurazione. Molte volte è sufficiente un percettrone per avere già una certa possibilità di classificazione. Matteo Tosato - Introduzione alle reti neurali artificiali


87 Un esempio può essere il seguente, dato il segnale di origine:

Con l‟ausilio della trasformata di Fourier lo si scompone nelle sue componenti,

La FFT ha il compito di amplificare e di isolarne “le caratteristiche”. Ogni caratteristica viene normalizzata e inserita nello strato di input.

Componenti

Neuroni di Input

Neuroni nascosti

Neurone di output

Segnale AD converter + freq. divisor

FFT

. . .

. . . i

. . .

. . .

y

h

k

Figura 21: Analisi di segnale tramite rete feed-forward

I due blocchi grigi, che rappresentano il convertitore analogico-digitale e il modulo per la trasformata di Fourier veloce, possono anche essere realizzati via hardware. Come anche la rete stessa del resto. Lo schema illustrato si comporta da analizzatore del segnale. Stabilisce che cosa il segnale rappresenta in base all‟addestramento cui è stata sottoposta. Matteo Tosato - Introduzione alle reti neurali artificiali


88 Solitamente si insegna a riconoscere certi pattern che rappresentano fenomeni esterni. Immaginiamo ad esempio, un dispositivo ricevitore con antenna. La cosa più complicata da fare non è tanto realizzare il sistema, ma scegliere gli esempi da sottoporre alla rete.

Reti di reti neurali Utilizzando un approccio ingegneristico, possiamo pensare ad una rete neurale come un blocco. Il famoso “black-block”. Con input ed output. Quando il funzionamento di un blocco è stato puntualizzato, possiamo utilizzarlo dimenticandoci delle sue caratteristiche interne, per produrre altri blocchi. Questo è quello che viene solitamente fatto nell‟ingegneria, partendo dal blocco più piccolo e crescendo verso quello più grande. Allora nulla ci vieta di estendere questo approccio anche alle reti neurali. Quello che otteniamo è la dispersione ulteriore del funzionamento. Incrementando le capacità di generalizzazione del sistema. Recenti studi hanno mostrato come questo approccio risulta valido anche nello studio di patologie come la schizofrenia. Un modello di questo tipo è il seguente: Block 3 Block 1

Block 2

...

...

...

Dove il blocco 1 è una ulteriore “fuzzificazione” di 3 input originali. Pertanto il blocco 2 si ritroverà a lavorare con una classe e altri input. Nessuna complicazione dal punto di vista computazionale, dato che il blocco 2, utilizza la classe in uscita dal blocco 1 al pari degli altri input. Matteo Tosato - Introduzione alle reti neurali artificiali


89 Le cose possono divenire anche molto complicate e si possono mischiare anche più tipi di configurazioni. Reti che utilizzano al loro interno reti auto-associative come memorie, oppure reti auto-organizzanti che hanno input generati da altre reti, e via dicendo...

...

...

...

Fuzzy - system

In base ai tipi di dati di cui si dispone si decide quali raggruppare in gruppi e processare separatamente, oppure quali di questi vanno prima trattati da una ulteriore rete, oppure quali devono essere normalizzati. Non ci sono regole per decidere quale configurazione sia la migliore per ogni problematica, molte decisioni vanno prese in base all‟esperienza che si accumula durante i test.

Conclusione e ringraziamenti Naturalmente le possibilità delle reti non si fermano a quello che abbiamo visto, esistono molte altre varianti che meritano di essere prese in considerazione. In questo testo ho cercato solo di fare una adeguata introduzione per i tipi e per le applicazioni più diffuse oggi. Di seguito, nei riferimenti, elenco le risorse utilizzate per lo studio e dove è possibile trovare altre informazioni e tutti gli approfondimenti necessari. Le reti neurali sono oggetti utili ed interessanti anche dal punto di vista delle neuroscienze e sono il punto di partenza per la realizzazione di una intelligenza non biologica. (vedi il progetto “Blue Brain”). Ci sono anche importanti implicazioni nella filosofia. Sul tema della coscienza e della fenomenologia. Ringraziamenti particolari: Matteo Tosato - Introduzione alle reti neurali artificiali


90 -

Flavio Bernardotti, fonte di informazioni e risorse. Andres Reyes, introduzione storica.

Appendice A – basi matematiche

Quando si lavora con le reti neurali è utile usare alcuni costrutti matematici. Questi sono quelli di norma utilizzati nella risoluzione di molti problemi in ambiti scientifici. Nelle ANN è infatti possibile incappare in questioni, che dal punto di vista matematico, possono divenire anche abbastanza complesse. In configurazioni semplici, dove i neuroni avranno al massimo funzioni di trasferimento binarie non avremo molta necessità di ricorrere alla matematica. Diversa è la questione quando utilizziamo algoritmi più complessi con back-propagation o una delle sue varianti. Inoltre, alcune operazioni sui pesi possono divenire molto più veloci quando utilizziamo alcune delle regole offerte dall‟algebra lineare per quanto riguarda le operazioni sulle matrici. Abbiamo visto infatti che il miglior modo di lavorare con le reti, è di definirle come matrici, ciò rende meno dispendioso il processo dal punto di vista dell‟impiego di tempo CPU e memoria, rispetto all‟approccio ad oggetti. Partendo proprio da questo ultimo punto, vediamo alcuni concetti di algebra lineare che possono risultare molto utili nel lavoro con le reti. - Cenni sui vettori Ogni parte della rete può in effetti essere intesa come un vettore di elementi, negli esempi riportati nella prima parte, ho sempre preferito utilizzare proprio degli array C per contenere i valori potenziali dei neuroni, il valore dei pesi, etc... tutti questi oggetti possono essere considerati come vettori e matrici. Possiamo descrivere un vettore nel seguente modo:

(

)

Una prima cosa che si può vedere riguardo al semplice vettore è la trasposizione. Il seguente è il vettore trasposto di x:

(

)

Gli elementi del vettore sono chiamati “componenti”. Un vettore può essere rappresentato graficamente come una freccia in uno spazio di n dimensioni, dove n è la dimensione del vettore. Due vettori possono essere moltiplicati, moltiplicando fra loro ogni elemento. Molto semplicemente nel modo seguente:

(

)

(

)

(

)

Matteo Tosato - Introduzione alle reti neurali artificiali


91 E‟ chiaro che graficamente questa operazione corrisponde ad un allungamento del vettore, in questo caso doppio. Oppure in caso fosse stato un vettore composto da componenti il cui valore era 0,5 sarebbe stata una riduzione. Stessa cosa vale per l‟addizione, con la differenza che dal punto di vista geometrico questa corrisponde alla diagonale del parallelogramma con due lati individuati dai due vettori. - Prodotto interno Un caso più interessante è invece il prodotto interno. Questo corrisponde esattamente al calcolo del potenziale del neurone.

∑( (

) (

)

)

- Norma La norma di un vettore corrisponde alla sua lunghezza.

‖ ‖

La normalizzazione di un vettore consiste nel dividere il vettore per la propria norma. Cosa significa? A volte gli input di una rete neurale possono arrivare da dispositivi, che per loro natura, possono essere affetti da oscillazioni. Pensate ad esempio a dei sensori esterni che hanno una qualche funzione di trasduzione, questi possono fornire input scorretti come intensità che potrebbero portare la rete neurale a errori. Per questo motivo gli input vengono in certi casi normalizzati. Questo processo avviene dividendo ogni componente per la norma vettoriale:

√∑ Il seguente grafico mostra il processo di normalizzazione su due input, i quali vengono normalizzati per rientrare tra i valori 0 e 1:

Matteo Tosato - Introduzione alle reti neurali artificiali


92 2.0 x1

1.5

1.0 x1 x2

0.5 x2

0.5

1.0

1.5

2.0

Nel campo delle reti, la norma viene utilizzata per calcolare la distanza di due vettori. Ad esempio la distanza del vettore degli input al vettore dei pesi, oppure il vettore di output rispetto quello della risposta desiderata. Per questo si ricorre alla norma della differenza:

√(

)

(

)

(

)

Questo calcolo nel caso di vettori binari, ovvero con componenti * +, da in output la distanza di Hamming tra i due vettori. La distanza di Hamming può essere utile in alcuni casi. Essa è definita come il numero di componenti corrispondenti differenti tra due vettori. Invece per un solo vettore binario tale distanza può essere intesa come la distanza tra questo e il vettore nullo, un vettore ove tutti i componenti sono posti a 0. - Disuguaglianza di Cauchy-Schwartz Questa dice che il valore assoluto del prodotto interno di due vettori X,W è minore o uguale al prodotto tra le norme dei due vettori.

|

|

|| || || ||

Quindi esisterà un angolo

tra i due vettori:

|| || || || Quindi il prodotto interno dei due vettori è anche definibile con:

|| || || || Questo ci indica che il prodotto interno dei due vettori sarà proporzionale al coseno dell‟angolo che viene a formarsi. Siccome:

Matteo Tosato - Introduzione alle reti neurali artificiali


93 Il prodotto interno, quindi la risposta del neurone, sarà tanto maggiore quanto minore è la distanza tra il vettore di input e il vettore dei pesi. - Cenni sulle matrici Una matrice è definibile come una raccolta di M righe per N colonne di numeri reali.

[

]

Solitamente i suoi componenti si indicano con in modo da identificare un componente nella matrice utilizzando due indici, rispettivamente per la riga e la colonna. L‟addizione tra due matrici è possibile solo se le due hanno numero di righe e colonne uguali. Invece è possibile moltiplicare una matrice per un vettore. Ad esempio data la matrice W seguente:

[

]

E un vettore X:

[

]

Che deve avere stesso numero di componenti del numero di colonne della matrice W, è possibile definire un nuovo vettore Wx moltiplicando ogni componente della matrice con il componente del vettore corrispondente di colonna. Questo per ogni riga:

[

] [

( [ (

]

) )

( (

) )

(

) (

)

]

0

1

Se consideriamo che la matrice W può corrispondere benissimo a due vettori di pesi sinaptici, quindi per due neuroni, e il vettore X al vettore di input, questo calcolo consente di calcolare in un solo passo tutti i nuovi valori per i neuroni di output. Infatti abbiamo due prodotti interni nel vettore finale Wx. La moltiplicazione tra due matrici A*B è possibile solo se la matrice A ha numero di colonne uguale alle righe della matrice B, in questo caso il prodotto finale ij corrisponde al prodotto del vettore della riga i della prima matrice con il vettore della colonna j della seconda matrice. Ad esempio:

0

[

,( ,(

1 [ ) )

( (

) )

( (

] ))-

,( ,(

)

( )

) (

( )

(

)] )-

0

Naturalmente la moltiplicazione tra matrici non è commutativa, ovvero perché il risultato dei due prodotti sarebbe molto diverso.

Matteo Tosato - Introduzione alle reti neurali artificiali

1 ,


94 La matrice unitaria I è una matrice quadrata in cui ciascun componente ij è dato dal delta di “Kronecker” ( )

( )

{

Per cui:

[

]

Il prodotto tra una matrice A e la matrice I corrispondente restituisce sempre la matrice originaria:

Come per il vettore trasposto anche per la matrice è possibile fare lo stesso ragionamento.

[

] e

0

1

E‟ facilmente intuibile che è possibile calcolare anche i prodotti però sono diversi.

I risultati

- Metodo di eliminazione di Gauss (rango della matrice) Il metodo di eliminazione di Gauss permette di ottenere una matrice equivalente che possiede un numero crescente di zero iniziali sulle righe.

[

]

[

]

Dove il simbolo * rappresenta qualsiasi valore risultato da altre operazioni. Vi sono solo tre operazioni che è possibile fare sulla matrice: 1. La moltiplicazione di una riga per uno scalare diverso da 0, ad esempio:

2. Scambio di righe:

3. Addizione del multiplo di una riga a un‟altra riga:

Operando in serie in questo modo e ripetendo da capo il procedimento si arriva ad ottenere una matrice che non può essere più ridotta. Arrivati a questo punto il numero delle righe che non hanno componenti uguali a zero viene detto rango della matrice. Questo procedimento possiede un grosso numero di applicazioni, tra cui la soluzione di un sistema di equazioni lineari omogenee a N incognite disposte in forma matriciale. Un esempio: Matteo Tosato - Introduzione alle reti neurali artificiali


95

[

]

[

]

[

]

[

]

[

]

- Derivata Solitamente quando si lavora con le reti neurali si può arrivare ad utilizzare concetti di analisi matematica abbastanza ricorrentemente. La derivata in primis è molto utilizzata, dato che è necessaria per l‟algoritmo di apprendimento supervisionato backpropagation “EBP”. Qui vediamo di riassumere brevemente che cosa è la derivata e come si calcola. Dal punto di vista matematico la derivata si definisce come limite del rapporto incrementale fra la variazione dei valori della funzione e quella della variabile indipendente, al tendere a zero di quest‟ultima. Ammettiamo di voler descrivere il movimento di un soggetto che si muove lungo una guida rettilinea; diciamo la posizione (rispetto ad un punto fissato) dell‟auto al tempo t. Il tachimetro dell‟auto segna, al tempo , una certa velocità, che chiamiamo ( ). La velocità media nell‟intervallo [ - è data dal rapporto fra lo spazio percorso ed il tempo impiegato a percorrerlo, quindi: ,

-

( )

( )

Quindi la velocità al tempo 0 segnata sul tachimetro è il limite della velocità media quando l‟ampiezza dell‟intervallo tende a zero: ( )

( )

( )

Graficamente la derivata è una secante di una qualsiasi funzione,

( ) Il tutto si può sintetizzare come segue: abbiamo una funzione , ed un punto fissato ( ). Consideriamo una piccola variazione e definiamo il rapporto incrementale della funzione nel punto con incremento h come:

Matteo Tosato - Introduzione alle reti neurali artificiali


96 ( )

( )

(

)

( )

Il rapporto incrementale rappresenta una velocità media, il coefficiente angolare di una retta secante, un tasso medio di crescita, e così via. Ci interessa studiare il limite per h che tende a zero: ( )

(

)

Se il limite esiste

( )

è derivabile in

.

La derivata consente di trovare nella funzione eventuali minimi, Noi nell‟algoritmo Error back-propagation avremo bisogno di calcolarci la derivata della funzione di trasferimento che sceglieremo di utilizzare, dato che la impiegheremo per calcolare l‟errore del singolo nodo dello strato di output e nascosto. E‟ importante anche il concetto di gradiente. Il gradiente di una funzione f(x) vettoriale (ovvero che produce un numero in uscita) può essere inteso come un vettore composto dalle derivate dei suoi componenti. Lo abbiamo incontrato nell‟algoritmo EBP. - Integrale Potremmo anche incappare nel calcolo integrale. Ma a parte il suo utilizzo nelle reti neurali, la definizione di integrale è bene saperla, dato il suo impiego nella maggior parte dei campi scientifici. Si pensi solo all‟analisi di Fourier. Il concetto di integrale è piuttosto semplice in se. Si consideri la relazione tra velocità e tempo. Se poi il moto è anche uniformemente accelerato avremo un grafico come il seguente: Velocità-tempo 5

Velocità

4 3 2 1 0 0

1

2

3

Tempo

Trovare lo spazio percorso non è complicato dato che , ci basta trovare l‟area del trapezio che si forma. Ma come possiamo fare in caso avessimo un moto non uniforme?

Matteo Tosato - Introduzione alle reti neurali artificiali


97 Velocità-tempo 4 Velocità

3 2 1 0 0

1

2

3

4

Tempo

In questo caso calcolare l‟area che c‟è tra la funzione l‟asse x non può essere fatta con una semplice moltiplicazione, si può pensare allora di costruire nel grafico una serie di rettangoli aventi per base un segmento ricavato sull‟asse delle x e come valore massimo un valore del tratto di funzione y rispettivo. In questo modo tentiamo di avvicinarci all‟area reale facendo la somma di tutte le aree dei rettangoli. Graficamente; Velocità-tempo

10 Velocità

8 6 4 2 0 0

1

2

3

4

5

6

7

8

9

10

Tempo

Naturalmente non riusciamo a riprodurre esattamente l‟area. Possiamo solo avere una delle due situazioni:

(

) (

)

) (

)

oppure,

(

Dove ( ) definisce un segmento sull‟asse delle x, ovvero la base del rettangolo; f() identifica il punto minore della funzione nell‟intervallo; F() identifica invece il maggiore. Il trucco per trovare l‟area giusta sta nel scegliere dei rettangoli dalla base molto piccola. Se facciamo tendere il numero di rettangoli ad infinito otteniamo l‟area corretta. Quindi:

∑ (

) (

)

∑ (

Matteo Tosato - Introduzione alle reti neurali artificiali

) (

)


98 La notazione degli integrali definisce con la „s‟ medioevale la sommatoria e con il termine dx, l‟infinitesimo, ovvero il segmento che costituisce la base dei rettangoli.

∑ (

) (

)

( )

∑ (

) (

)

Detto questo è facile definire la funzione integrale e quindi l‟integrale definito. L‟integrale è detto definito quando sarà considerato entro un certo range, di conseguenza se rendiamo variabile uno degli estremi abbiamo un funzione. Al variare dell‟estremo x, il valore integrale si avvicina o si allontana dall‟ipotetica area vista prima.

( )

( )

Questa è una funzione crescente, dato che x è un punto sull‟asse compreso fra a e b. Il teorema fondamentale del calcolo integrale dice che la derivata della funzione è uguale alla funzione di partenza. Indicando la derivata rispetto ad x con D avremo:

( )

,∫

( )

-

( )

Appendice B – Riferimenti -

Analysis and Applications of Artificial Neural Networks 1Ed, Prentice Hall

-

Process Neural Networks, Xingui He e Shaohua Xu, advanced topics in science and technologiy in cina

-

Veelenturf L.P.J. Analysis and Applications of Artificial Neural Networks (1Ed, Prentice Hall, 1995)

-

Artificial Intelligence Structures and Strategies for Complex Problem Solving Third Edition, George F. Luger, William A. Stubblefield

-

A Direct Adaptive Method for Faster Backpropagation Learning: The RPROP Algorithm , Martin Riedmiller and Heinrich Braun, Institut fur Logik, Komplexitat und Deduktionssyteme.

-

Novel Algorithms and Techniques in Telecommunications, Automation and Industrial Electronics

-

Novel Algorithms and Techniques in Telecommunications and Networking

-

Pratical Neural Network Recipes in C++

-

Reti neurali e fuzzy logic su personal computer – Luca Marchese

-

An Improved Backpropagation Method with Adaptive Learning Rate V.P. Plagianakos, D.G. Sotiropoulos, and M.N. Vrahatis University of Patras, Department of Mathematics, Division of Computational Mathematics & Informatics

-

Neural Networks, (Neural Networks & Artificial Intelligence), Università Italiana Cracking – Italian University of Cracking

-

http://it.wikipedia.org/wiki/Rete_neurale

Matteo Tosato - Introduzione alle reti neurali artificiali


99

-

http://www.willamette.edu/~gorr/classes/cs449/intro.html

-

http://www.semeion.it/

-

http://www.synaptics.org/synaptics/research.htm

-

http://www.ra.cs.uni-tuebingen.de/SNNS/ (Software)

-

http://www.heatonresearch.com/encog (Framework, Java e C#)

-

http://www.bernardotti.it/portal/ (Forum CV e AI)

-

AI framework in C#, Aneuro32

Appendice C – Aneuro32 Di seguito presento alcuni esempi di semplici programmi in C# che utilizzano il framework „aneuro32‟ (https://github.com/Matteo87/Aneuro32) che ho sviluppato a fronte di integrare varie configurazioni di reti neurali artificiali e altri paradigmi del natural computing in un unico SDK. Addestramento M-Adeline tramite algoritmo Windrow-Hoff per operatore AND: using using using using

System; System.Collections.Generic; System.Linq; System.Text;

using using using using

aneuro32.Nn.Structure.FeedForward; aneuro32.Functions; aneuro32.Learning.Supervised.Propagation; aneuro32.Misc;

namespace M_Adeline_testApp { class Program { static void Main(string[] args) { // input set double[][] input = { new double[] {0,0}, new double[] {0,1}, new double[] {1,0}, new double[] {1,1} }; // target set double[][] target = { new double[] {0}, new double[] {0}, new double[] {0}, new double[] {1} }; // Creating neural network: MAdeline MyNet = new MAdeline(2); // Add bias: MyNet.AddBiasWeight(); // Connects units: MyNet.Build(); // Creating training object: WindrowHoff trainingAlg = new WindrowHoff( ref MyNet, // Network input, // input set target, // target set activation_mode.sigmoid, // Transfer function 0.001, // Target MSE Matteo Tosato - Introduzione alle reti neurali artificiali


100 0.5 );

// Learning rate

bool retVal; // Training loop: do { retVal = trainingAlg.iteration(); if (retVal != true) { Console.WriteLine("Training error: " + trainingAlg.NetworkError); break; } } while (!trainingAlg.isTrained);

// Check for error

// Testing network: int j = 0; Console.WriteLine("Net test:"); foreach (double[] _in in input) { MyNet.SetInput(input[j]); MyNet.Exec(); Console.WriteLine("\nPattern n° "+j); Console.WriteLine("Input: " + input[j][0] + " " + input[j][1]); Console.WriteLine("Output: " + MyNet.output.OutputValue.ToString("0.00") + " Ideal: " + target[j][0]); j++; } Console.WriteLine("\nNet final error: "+trainingAlg.CurrentMeanSquareError.ToString("0.######")); Console.WriteLine("Total epoch: "+trainingAlg.CurrentEpoch); List<double> weights = MyNet.SaveWeights(); Console.WriteLine("\nNet final weights: "); foreach (double d in weights) { Console.WriteLine(d.ToString("0.######")); } Console.ReadLine(); } } }

Ouput: Net test: Pattern n° 0 Input: 0 0 Output: 0,00 Ideal: 0 Pattern n° 1 Input: 0 1 Output: 0,02 Ideal: 0 Pattern n° 2 Input: 1 0 Output: 0,02 Ideal: 0 Pattern n° 3 Input: 1 1 Output: 0,97 Ideal: 1 Net final error: 0,001 Total epoch: 8217 Net final weights: 6,305019 6,304662 -9,533159

Matteo Tosato - Introduzione alle reti neurali artificiali


101 Addestramento MLP tramite algoritmo Error back-propagation con learning rate adattativo per operatore XOR:

using using using using using

System; System.Collections.Generic; System.Linq; System.Text; System.IO;

using aneuro32; using aneuro32.Nn.Structure.FeedForward; using aneuro32.Learning.Supervised.Propagation; namespace DebugProp0 { class Program { static void Main() { double[][] xorInput = { new double[] {0.0,0.0}, new double[] {0.0,1.0}, new double[] {1.0,0.0}, new double[] {1.0,1.0} }; double[][] xorOutput = { new double[] {0.0}, new double[] {1.0}, new double[] {1.0}, new double[] {0.0} }; IFFNeuralNetwork MyNet; PropagationBase MyTrainingAlg; TextWriter outStrm = Console.Out; TextReader inStrm = Console.In; // Build ANN MyNet = new ffnetwork(); // Adding Layers MyNet.AddNewLayer(2); MyNet.AddNewLayer(4); MyNet.AddNewLayer(1); // Adding bias neurons MyNet.AddBiasWeight(); // Connects all MyNet.Build(); // Training instance MyTrainingAlg = new ShiffmanVariantBackpropagation( ref MyNet, xorInput, xorOutput ); // Training process ... do { if (MyTrainingAlg.iteration() != true) break; } while (MyTrainingAlg.isTrained != true); // Exit training, check result and run an example outStrm.WriteLine("Testing... "); outStrm.WriteLine(); double[] res; int j = 0; foreach (double[] pattern in xorInput) { outStrm.WriteLine(); outStrm.WriteLine("Pattern 1째: " + pattern[0] + " " + pattern[1]); MyNet.SetInput(pattern); MyNet.Exec(); res = MyNet.GetOutput(); outStrm.WriteLine("NetOutput: " + res[0] + " Ideal output: " + xorOutput[j++][0]); } outStrm.WriteLine("Total Epochs: " + MyTrainingAlg.CurrentEpoch + ", Final error: " + MyTrainingAlg.GetMeanSquareError()); inStrm.ReadLine(); // Wait for user input } } } Matteo Tosato - Introduzione alle reti neurali artificiali


102

Output: Testing... Pattern 1째: 0 0 NetOutput: 0,0215273578662015 Ideal output: 0 Pattern 1째: 0 1 NetOutput: 0,977569795574149 Ideal output: 1 Pattern 1째: 1 0 NetOutput: 0,977554230396672 Ideal output: 1 Pattern 1째: 1 1 NetOutput: 0,0229820784372725 Ideal output: 0 Total Epochs: 5003, Final error: 0,000999927837006829

Matteo Tosato - Introduzione alle reti neurali artificiali


Turn static files into dynamic content formats.

Create a flipbook
Issuu converts static files into: digital portfolios, online yearbooks, online catalogs, digital photo albums and more. Sign up and create your flipbook.