Reti neurali attraverso algoritmi genetici in C++. Parte I

19 November 2008 da Francesco

PREFAZIONE

Le reti neurali sono un argomento molto ostico per molti all’inizio.
Anche io per riuscire ad implementare la mia prima rete neurale funzionante ci ho impiegato molto tempo ma ho soprattutto dovuto provare e riprovare più volte passando per diversi fallimenti. Mi sono accorto che molti dei tutorial che si trovano in giro tralasciano alcuni aspetti o li spiegano in maniera poco chiara, e tra l’altro i tutorial in lingua italiana sull’argomento sono anche pochi.
Con questo tutorial cercherò di creare un documento che comprenda almeno le basi per rendere chiunque lo legga, ed abbia alcuni prerequisiti elencati in seguito, capace di implementare una rete neurale.

Nota sul codice.

Prima di iniziare va scritta una piccola nota sul codice: il codice che troverete nell’archivio che vi sarà dato alla fine di questa guida, non è esattamente lo stesso che trovate scritto in questa serie di tutorial. Questo perchè ho fatto in seguito delle modifiche (minori) che comunque non cambiano in sostanza la struttura del codice, ma aggiungono solo qualche funzionalità alla rete neurale. Non dovrebbe essere difficile seguirlo lo stesso.

PREREQUISITI

Ho cercato di rendere questa guida comprensibile alla maggior parte delle persone (anche a quelle che sull’argomento reti neurali sono poco informate) ma restano sempre degli argomenti che vengono dati per scontati per la comprensione del testo.
Sono:

L’ultimo punto non è strettamente necessario, visto che tenterò di spiegare almeno quello che serve sapere sugli algoritmi genetici per implementare la nostra rete, ma se avete gia’ una conoscenza dell’argomento di certo avrete meno difficoltà a comprendere alcuni concetti. Alla fine del tutorial sono consigliati una serie di link utili la cui lettura e caldamente consigliata.

PARTE GENERALE

Introduzione.

Questa Parte generale del tutorial contiene alcuni elementi di base come il funzionamento di un neurone biologico, la struttura di un neurone artificiale (cioè quello che noi implementeremo) e la struttura di una rete neurale artificiale. Ho inserito anche una piccola parte sugli algoritmi genetici che non tratta esaustivamente l’argomento ma dovrebbe dare almeno l’infarinatura necessaria a comprendere per quale motivo li useremo e come li useremo. Comunque, come ho scritto sopra, consiglio lo stesso la lettura di altri tutorial più approfonditi se avete difficoltà a comprendere quello che dico sugli algoritmi genetici. Avrei tralasciato volentieri questa parte, perchè penso che molti di voi già la conoscano se hanno letto almeno qualche altro documento sull’argomento reti neurali. Purtroppo mi tocca scriverla lo stesso visto che è un requisito fondamentale per la
comprensione delle parti successive e questo tutorial mira ad essere il più chiaro possibile anche per chi è agli inizi. Comunque, se conoscete già questi argomenti potete tranquillamente saltare tutta la Parte generale del tutorial e passare a leggere direttamente il resto.

Come funziona un neurone biologico.

I neuroni sono le cellule che costituiscono la nostra mente e che trasmettono i segnali nervosi (sia tra di loro che verso le altre parti del corpo). Ogni uomo posside circa 100 miliardi di neuroni (o almeno dovrebbe) variamente collegati tra di loro attraverso una struttura che prende il nome di sinapsi.
Ogni neurone possiede un lungo prolungamento chiamato assone e diverse ramificazioni più piccole che prendono il nome di dendriti. Attraverso i dendriti (che sono collegati agli assoni di altri neuroni) la cellula riceve i segnali che gli vengono inviati, mentre attraverso l’assone viene mandato un nuovo segnale.
Se non avete ben chiaro cio’ che abbiamo detto fino ad ora, l’immagine seguente dovrebbe chiarvi le idee:

Schema di un neurone biologico

Schema di un neurone biologico

(Nella figura sono presenti parti del neurone che non ho nominato, come per esempio la guaina mielinica, perche’ non sono utili al nostro scopo).
In generale, quando un neurone riceve un segnale, se esso supera una certa soglia (detta soglia di attivazione), propaga quel segnale attraverso il suo assone agli altri neuroni, altrimenti resta inattivo.

Come funziona una rete neurale.

Un neurone artificiale tenta di emulare il comportamento di quello biologico, anche se in maniera molto semplificata.
Ogni neurone (d’ora in poi con il termine neurone mi riferirò esclusivamente a quelli artificiali) ha una serie di collegamenti pesati in ingresso e fornisce un output in base agli input ricevuti dai collegamenti. Per collegamento pesato si intende un collegamento al quale è anche associato un peso,
che rappresenta, in poche parole, la forza del collegamento e quindi quanto esso potrà influenzare l’output del neurone.
Il peso non è altro che un numero, generalmente compreso tra 0 e 1 (o tra 1e 1).
L’informazione trasportata dal collegamento (parliamo sempre di un numero) viene moltiplicata per il peso e quindi varia al variare del peso (resta invariata quando esso è uguale a 1, diventa nulla quando è 0 e diventa l’opposto quando è 1).
Come vedremo, sarà proprio il valore dei pesi a determinare l’esattezza dei risultati della nostra rete ed è appunto variando i pesi tramite speciali algoritmi che si tenta di raggiungere una configurazione ottimale. Ricordo che i pesi subito dopo l’inizializzazione della rete hanno valori casuali.
Il neurone, dopo aver ricevuto gli input pesati da tutti i collegamenti in ingresso li somma. La sommatoria degli input pesati viene poi passata ad una funzione chiamata funzione di attivazione che fa propagare il segnale ai neuroni successivi.
Spesso, e questo comportamento differisce da quello del neurone biologico, non viene stabilita una soglia minima di attivazione al di sotto della quale il neurone non invia nessuno stimolo, ma questo dipende dalla funzione di attivazione scelta e lo vedremo più tardi.
Le reti che andremo ad implementare in questo tutorial sono definite reti feedforward perchè l’impulso si propaga sempre nella stessa direzione; esistono molti altri tipi di reti e ognuna è più adatta ad alcuni compiti particolari, ma non ne parleremo in questo tutorial.
Una rete neurale è formata appunto da un certo numero di neuroni, chiaramente molto minore di quello del nostro cervello: in genere alcune decine di neuroni sono in grado di compiere compiti anche piuttosto complessi.
I neuroni di una rete sono organizzati in diversi livelli, o strati, e ognuno di essi ha un compito preciso. Il primo strato è quello di input: contiene i neuroni che ricevono l’input e non hanno collegamenti pesati; non essendo infatti i dati che ricevono provenienti da nessun altro neurone essi immagazzinano semplicemente il dato così come è e lo trasmettono al livello successivo.
Lo strato di input può essere collegato direttamente a quello di output oppure tra di essi si può trovare un certo numero di strati nascosti (hidden layers): per gli scopi di questo tutorial useremo una rete con un solo strato nascosto il quale è più che sufficiente per svolgere i compiti che ci interessano.

Abbiamo visto la struttura di una rete neurale, ma come fa essa ad apprendere? Va detto che esistono due tipi di apprendimento: quello supervisionato (che tratteremo in questo tutorial) e quello non supervisionato.

Usando il primo tipo di apprendimento la rete necessita di una prima fase di addestramento durante la quale le vengono forniti alcuni esempi comprendenti sia l’input che l’output desiderato. La rete viene eseguita con questi input di cui si conosce già in precedenza il risultato e tramite alcuni algoritmi vengono modificati i pesi della rete per fornire risultati sempre più accurati. Quando la fase di allenamento è finita (cioè si e’ raggiunta una soglia minima di errore stabilita in precedenza) la rete è in grando non solo di dare un risultato esatto se gli forniamo gli stessi dati che gli
abbiamo fornito nel training set (gli esempi) ma anche con dati in input che non ha mai visto. Questo avviene perchè la rete non associa un singolo input all’output corrispondente ma scopre la relazione che li lega. Potremmo per esempio allenare una rete neurale ad imparare a fare la somma di due numeri fornendo un training set del genere:

Primo input Secondo input Output desiderato
0.1 0.2 0.3
0.1 0.3 0.4
0.1 0.4 0.5
0.1 0.5 0.6
0.1 0.6 0.7
0.1 0.7 0.8
etc… etc… etc..

Un training set deve essere il più omogeneo possibile, e deve descrivere una grande varietà di esempi. Un training set in cui il primo input è sempre 0.1 non è per niente buono per ottenere dei risultati soddisfacenti. Più è vasto il training set, inoltre, più sarà alta la capacità di generalizzazione della rete, cioè di dare risultati corretti anche quando i dati in input sono per lei sconosciuti, cioè non facevano parte del training set.
Modificando in maniera corretta i pesi della rete essa riconosce la relazione tra gli input e l’output (e cioè, nel nostro caso, che l’output deve essere la somma degli input) e dà un risultato corretto. Ricordatevi comunque che l’applicazione delle reti neurali avviene in campi in cui è più importante la flessibilità e la capacità di generalizzazione della stessa, piuttosto che ottenere risultati precisissimi: quello che voglio dire è che una rete neurale è molto più utile per la sua flessibilità che per la sua precisione, e che per fare la somma di dui numeri è molto meglio usare una calcolatrice. Una rete
addestrata a tale scopo, infatti, dà in genere, risultati approssimati come :
0.5 + 0.5 = 0.99
0.1 + 0.9 = 1.02
o simili.
Chiusa questa piccola parentesi torniamo ai tipi di apprendimento. Il secondo tipo di apprendimento (quello non supervisionato) serve a creare reti
neurali che classificano i dati che gli forniamo in input in diverse categorie (senza fornigli nessun output desiderato, da qui il nome) in base ai loro elementi in comune, ma non verra’ trattato in questo tutorial.
Come avrete capito, la parte più importante per l’apprendimento della rete neurale è la modifica dei pesi fra le connessioni dei neuroni ed è proprio qui che agiscono gli algoritmi di apprendimento. Esistono diversi algoritmi: uno dei piu’ famosi è quello di retro propagazione dell’errore (error back propagation) che mi limiterò a citare solamente visto che noi faremo uso di algoritmi genetici per fare evolvere la nostra rete.

La prima parte del tutorial finisce qui. A presto.

Condividi l'articolo!
  • Digg
  • del.icio.us
  • Facebook
  • Diggita
  • StumbleUpon
  • Technorati
  • Twitter

Tags: , , ,
Pubblicato in Informatica | Commenti (0)

No comments yet

Leave a Reply