E’ di alcuni mesi fa l’annuncio di Microsoft sulla comparsa di Skype Translator per la fine di quest’anno. Si trattera’ di un traduttore automatico che permettera’ di fare telefonate in video con l’aiuto di una traduzione simultanea sia in voce che con i sottotitoli.
La sorpresa e’ arrivata durante la Code Conference di S. Francisco dove il vice presidente di Skype, Gurdeep Pall, ha fatto un esperimento pratico. Parlando in inglese, ha conversato tramite Skype con una dipendente Microsoft di lingua tedesca e la loro discussione e’ stata tradotta in tempo reale. Non e’ ancora chiaro in quante lingue sara’ disponibile Skype Translator e se sara’ un servizio gratuito, ma l’idea di poter parlare, ad esempio, con una persona cinese senza barriere linguistiche, per lavoro o motivi personali e’ davvero una grande novita’. Si tratterebbe del superamento delle barriere linguistiche della Torre di Babele che fino ad ora non era stato possibile con i traduttori disponibili, come per esempio Google Translator, in quanto utili per una traduzione veloce ma non abbastanza precisi. Ma come e’ possibile tutto cio’?
Utilizzando le cosiddette “machine deep learning” sviluppate nell’ambito della ricerca sull’Intelligenza Artificiale. Per capire di cosa si tratta partiamo con l’unita’ piu’ semplice di una rete neurale: il percettrone (perceptron).
Supponiamo di avere 5 immagini di uomini e 5 immagini di qualsiasi altra cosa e di etichettare con 1 le immagini umane e con 0 le altre. L’obiettivo e’ di costruire un algoritmo che sia capace di imparare ad identificare le immagini degli umani una volta che gli vengono presentate nuove immagini mai viste prima (1 per gli umani e 0 per il resto). Questo esempio e’ del tutto generale. I dati potrebbero essere dei sintomi e le etichette le diverse malattie; i dati potrebbero essere le lettere scritte a mano e le etichette le lettere reali corrispondenti. Questo e’ quello che in definitiva fa un algoritmo come il percettrone.
Supponiamo di avere n punti in un piano, etichettati con 0 ed 1. Aggiungiamo un nuovo punto e cerchiamo di prevedere la sua etichetta. Come possiamo fare? Un possibile approccio puo’ essere quello di guardare i primi vicini ed assegnare l’etichetta al punto in base alla quantita’ di 1 e di 0 presenti nelle vicinanze. Per esempio, nell’immagine qui riportata assegneremmo il colore rosso al punto vuoto visto che i primi vicini sono in prevalenza rossi.
Un modo leggermente piu’ intelligente potrebbe essere quello di considerare una linea che meglio separa i dati etichettati e usarla come classificatore (classificatore lineare).
In questo caso, i dati in ingresso verrebbero rappresentati come vettori (x,y) e la funzione in uscita dovrebbe essere qualche cosa come “blu” se il punto cade al di sotto della linea e “rosso” se capita al di sopra. Matematicamente potremmo scrivere la funzione di trasferimento come:
f(x)=w·x+b
dove w e’ un peso opportuno e b un cosiddetto bias (offset verticale). Il risultato di questa funzione potrebbe essere portato in ingresso ad una funzione di attivazione per produrre l’opportuna etichetta. Nel nostro esempio, la funzione di attivazione potrebbe essere:
h(x)=”rosso” se y(x)>f(x)= w·x+b o h(x)=”blu” per gli altri casi.
Per determinare il peso w ottimale, l’algoritmo del percettrone in base ai dati presentati in ingresso sceglie i valori per minimizzare l’errore della risposta in uscita, definito come la differenza tra il valore desiderato e quello reale (oppure utilizzando il cosiddetto mean square error). Ovviamente il percettrone nella sua semplicita’ ha uno svantaggio: esso puo’ solo imparare a separare funzioni lineari. Consideriamo per esempio la funzione XOR, una funzione molto semplice che ritorna 0 se i due valori in ingresso sono uguali altrimenti ritorna 1. In questo caso un classificatore lineare non funzionerebbe correttamente come mostrato sotto.
Per superare questo problema bisogna ricorrere ad un percettrone “multistrato”, anche conosciuto come “feedforward neural network”: in effetti non faremo altro che mettere insieme piu’ percettroni connessi insieme a creare un meccanismo di apprendimento piu’ potente.
La rete neurale ha le seguenti proprieta’:
· Un input, un output e uno o piu’ strati “nascosti” (hidden). Una rete con piu’ strati nascosti viene chiamata Deep Neural Network. Nella figura sopra e’ mostrata una rete con 3 unita’ (anche chiamate neuroni o nodi) in ingresso, uno strato di output con 2 unita’ e due strati nascosti, con 4 unita’ il primo e 3 unita’ il secondo rispettivamente
· Ogni unita’ e’ un singolo percettrone
· Le unita’ dello strato di ingresso servono per trasmettere allo strato nascosto i dati di ingresso, mentre le unita’ nascoste fungono da unita’ di ingresso per lo strato di uscita.
· Ogni connessione tra due neuroni ha un certo peso w simile a quello del percettrone discusso sopra
· Ogni unita’ di uno strato n, in generale e’ connessa ad ogni unita’ dello strato precedente n-1 (anche se e’ possibile disconnettere due neuroni semplicemente settando a zero il proprio peso)
· Lo strato di ingresso serve solo per trasferire i dati di ingresso alla rete che li processera’. Per esempio, se il vettore in ingresso e’ (7,1,2) questo significa che il primo nodo in alto vedra’ il valore 7, quello centrale 1 e quello in basso il valore 2. Questi valori, vengono poi propagati in avanti verso le unita’ nascoste usando una somma pesata come ingresso alla funzione di trasferimento di ogni unita’, che a sua volta calcola il proprio output tramite la funzione di attivazione.
· Lo strato in uscita calcola i valori risultanti allo stesso modo dello strato nascosto. Il risultato e’ l’output finale della rete.
Cosa succede se per i nostri percettroni utilizziamo delle funzioni di attivazione lineari?
L’uscita finale della rete sara’ ancora una qualche funzione lineare degli ingressi, opportunamente aggiustati con tanti pesi diversi collezionati lungo la rete. In altre parole, una composizione lineare di tante funzioni lineari e’ ancora una funzione lineare. Quindi se utilizziamo delle funzioni di attivazione lineari, la rete neurale non sara’ piu’ potente di un singolo percettrone indipendentemente dal numero di strati utilizzati. Per questo motivo la maggior parte delle rete neurali utilizzano funzioni di attivazione non lineari. Le funzioni di attivazione piu’ utilizzate sono: logistica, tangente iperbolica, binaria e rectfier. Nella formula sottostante, k e’ la funzione di attivazione, wi i pesi e gi i vettori in ingresso.
Di seguito la schematizzazione di un singolo neurone con funzione di attivazione a soglia. Il neurone riceve in ingresso uno stimolo dato dalla somma del prodotto dei singoli ingressi per i pesi associati. Nel nostro caso U=1.5·0.5+1·3+2·(-1)=1.75. Se la somma supera una certa soglia (nel nostro esempio il valore theta) il neurone si attiva e in uscita si avra’ il valore U-ɵ=1.75-1=0.75
L’algoritmo piu’ comune per il learning (addestramento) di un percettrone a multistrato e’ conosciuto come backpropagation. La procedura di base e’ la seguente:
· Un campione per il training viene presentato in ingresso alla rete e propagato attraverso di essa.
· In uscita viene calcolato l’errore E=0.5(t-y)2 dove t e’ il valore di riferimento (target) e y e il valore reale calcolato dalla rete.
· L’errore E viene minimizzato usando un metodo chiamato stochastic gradient descent. Il valore ottimale di ogni peso viene determinato in modo da far raggiungere all’errore un minimo globale. Durante la fase di training, i pesi vengono cambiati di poco nella direzione del minimo globale, anche se questa non e’ un’attivita’ semplice visto che spesso i pesi raggiungono dei minimi locali come quelli mostrati di seguito.
L’algoritmo di backpropagation fornisce un metodo per aggiornare ogni peso tra due neuroni rispetto all’errore di uscita. Il peso di un dato nodo (per esempio il nodo i-esimo) viene aggiornato seguendo la semplice relazione:
dove E e’ l’errore di uscita della rete e wi e il peso in input al neurone i-esimo. Il parametro alfa controlla la velocita’ di discesa lungo il gradiente, e nei casi piu’ semplici e’ una costante. Si tratta di muoversi nella direzione del gradiente rispetto al peso wi. Dapprima vengono calcolati gli errori in uscita delle diverse unita’, e quindi propagati all’indietro nella rete in modo efficiente per cercare di raggiungere il minimo globale.
Lo strato nascosto (hidden layer) e’ dove la rete immagazzina la rappresentazione astratta dei dati di training, cosi come il cervello ha una rappresentazione interna del mondo reale. Secondo il teorema dell’approssimazione universale, un singolo strato nascosto con un numero finito di neuroni puo’ essere specializzato ad approssimare qualsiasi funzione. Comunque nella realta’ una rete neurale puo’ avere piu’ di uno strato nascosto per permettere astrazioni maggiori anche se ci sono degli effetti collaterali.
Man mano che si aggiungono strati nascosti, l’algoritmo di backpropagation diventa meno utile nel passare le informazioni agli strati precedenti. Si puo’ presentare inoltre, il cosiddetto problema di overfitting. Esso descrive il fenomeno dell’interpolazione dei dati di training in modo troppo “preciso” (lasciatemi passare il termine), e la rete finisce col perdere in flessibilita’. E’ come se la rete imparasse a memoria perdendo la capacita’ di generalizzare cioe’ la capacita’ di fornire una risposta corretta a nuovi ingressi (non presentati nella fase di addestramento della rete).
Altro problema che affligge tutti i sistemi di apprendimento supervisionato (cioe’ quelli con addestramento) e’ la cosiddetta maledizione della dimensionalita’, un progressivo decadimento delle prestazioni all’aumentare della dimensione dello spazio di ingresso. Questo avviene perche’ il numero di esempi necessari ad ottenere un campionamento sufficiente dello spazio di ingresso aumenta esponenzialmente con il numero delle dimensioni.
Per superare questi problemi sono stati sviluppati degli algoritmi particolari. Il primo e’ quello delle reti auto-associative progettate ed addestrate per trasformare un pattern in ingresso in se stesso, in modo che, in presenza di una versione degradata o incompleta di un pattern di ingresso, sia possibile ottenere il pattern originale. Una rete auto-associativa in generale e’ una rete neurale feedforward con 3 strati: un ingresso, un’uscita e uno strato nascosto. Concettualmente, la rete viene addestrata per ricreare in uscita i dati presentati in ingresso con lo strato nascosto che immagazzina i dati compressi cioe’ una rappresentazione compatta che cattura le caratteristiche fondamentali dei dati in ingresso. Una vera e propria riduzione della dimensione del campione in ingresso.
Il secondo algoritmo sviluppato e’ stato quello delle cosiddette Restricted Boltzmann machines (RBM) un tipo di rete neurale stocastica.
Questo tipo di rete e’ composto da uno strato di ingresso/uscita chiamato visibile e uno strato nascosto. Diversamente dalle reti feedforward, le connessioni tra lo strato visibile e quello nascosto sono non direzionali (cioe’ i dati possono viaggiare in entrambi le direzioni visibile-nascosto e nascosto-visibile) e le diverse unita’ neuronali possono essere completamente connesse (in questo caso si parla di macchina di Boltzmann) o parzialmente connesse (in questo caso si parla di macchina di Boltzmann ristretta). In generale una rete RBM ha le unita’ binarie (con stati 0 o 1) assegnate con una certa probabilita’ secondo una distribuzione di Bernoulli.
Queste reti erano note agli scienziati fin dagli anni 80 ma solo di recente hanno visto di nuovo un grosso interesse da parte della comunita’ scientifica in seguito all’introduzione di un nuovo algoritmo di training (addestramento) non supervisionato chiamato “contrastive divergence”.
Questo algoritmo e’ costituito da 3 fasi: quella positiva, quella negativa e quella dell’aggiornamento dei pesi.
Fase positiva
· Un campione v (si tratta di un vettore multidimensionale) e’ presentato in ingresso alla rete
· Il vettore v e’ propagato allo strato nascosto allo stesso modo delle reti feedforward; indichiamo il suo output con il vettore h.
Fase negativa
· Si propaga il vettore h indietro allo strato visibile ottenendo un nuovo vettore v’
· Il vettore v’ viene propagato indietro allo strato nascosto ottenendo un nuovo vettore h’
Aggiornamento dei pesi
w(t+1)=w(t)+a(vhT-v’h’T)
dove a rappresenta la velocita’ di apprendimento della rete e T indica il vettore trasposto (l’operazione di trasposizione trasforma una riga in colonna e viceversa). La fase positiva riflette la rappresentazione interna dei dati reali (h a partire da v). La fase negativa, invece rappresenta il tentativo della rete di ricreare i dati sulla base della rappresentazione interna (v’ a partire da h). Lo scopo principale dell’algoritmo e’ quello di generare dei dati il piu’ vicino possibile ai dati reali e questo e’ riflesso nella formula di aggiornamento dei pesi. In altre parole, la rete ha una qualche percezione di come i dati in ingresso possono essere rappresentati, e quindi prova a riprodurre i dati reali sulla base di questa percezione. Se la riproduzione non e’ abbastanza vicina alla realta’, la rete aggiorna i pesi e prova di nuovo.
Sia le reti auto-associative che le macchine di Boltzmann hanno la proprieta’ interessante di avere le unita’ nascoste che funzionano come dei veri e propri rivelatori di caratteristiche “feature detectors” e questo e’ alla base delle deep neural net come vedremo tra poco. Prima vediamo meglio la proprieta’ delle unita’ nascoste. Supponiamo di voler addestrare una rete neurale a distinguere immagini che rappresentano delle motociclette da quelle che rappresentano dei visi. Abbiamo molti pixels in ingresso e un output pari a 0 per le motociclette e un 1 per un viso. Se usassimo una rete neurale con un singolo strato e un peso per ogni pixel quello che la rete cercherebbe di fare e’ classificare ogni pixel come appartenente ad una moto o ad un viso. Ovviamente questo e’ impossibile in quanto un pixel nero potrebbe essere parte sia dell’immagine della moto che di un viso. Sicuramente sarebbe piu’ utile avere a disposizione un rivelatore di un “manubrio” o di una “ruota”. Sotto la spinta di queste motivazioni sono stati sviluppati dei metodi di “deep lerning” che mirano, tramite l’utilizzo di architetture profonde, all’apprendimento di gerarchie di “features”, con quelle ai livelli piu’ alti formate attraverso la composizione di quelle ai livelli piu’ bassi. Ispirandosi all’architettura profonda del cervello, i ricercatori nel campo delle reti neurali hanno provato ad addestrare reti multistrato sfruttando un algoritmo di apprendimento non supervisionato (le RBM/auto-associative con learning contrastive divergence) e addestrando singolarmente ogni strato nascosto per superare il problema dell’overfitting. Queste strutture stacked (impilate una nell’altra) sono molto potenti e producono risultati veramente impressionanti. Google, per esempio, ha utilizzato una rete profonda costituita da diverse reti auto-associative per imparare a distinguere un viso umano da quello di un gatto (link). Un esempio di rete profonda con blocchi auto-associativi e’ quello mostrato di seguito.
Lo strato nascosto I agisce come strato di ingresso per lo strato nascosto II e cosi via. Il training di una rete del genere procede come segue:
1. Viene effettuato l’addestramento del primo blocco auto-associativo (le connessioni rosse) usando il metodo di backpropagation con tutti i dati di training disponibili.
2. Si effettua l’addestramento del secondo blocco auto-associativo (connessioni verde). Il training inizia presentando i dati in ingresso allo strato rosso e propagato ai neuroni del secondo strato nascosto (quello verde). In seguito vengono aggiornati i pesi (input-hidden e hidden-output) del secondo strato nascosto (quello verde) utilizzando l’algoritmo backpropagation e tutti i dati di training gia’ utilizzati per addestrare il primo strato nascosto. Questa procedura viene ripetuta per tutti gli strati nascosti.
3. Gli steps precedenti vengono chiamati pre-training e servono per inizializzare i pesi della rete. Comunque a questo stadio non esiste ancora un mappatura tra lo strato di ingresso e quello di uscita. Per realizzarla, la rete viene addestrata come una normale feedforward usando l’algoritmo di backpropagation (questo step viene chiamato fine-tuning).
Come con le reti auto-associative, e’ possibile “impilare” anche delle macchine di Boltzmann (RBM) per realizzare delle reti deep conosciute come Deep Belief Netwroks (DBN). In questo caso lo strato nascosto della prima RBM funzionera’ da strato visibile per lo strato nascosto della seconda RBM e cosi via. Lo strato di ingresso della prima RBM e’ lo strato di ingresso dell’intera rete.
L’addestramento della rete procede in questo modo:
1. Viene effettuato il training della prima RBM utilizzando l’algoritmo “contrastive divergence” utilizzando tutti i dati di addestramento.
2. Parte l’addestramento della seconda RBM. Poiche’ lo strato visibile della seconda RBM e’ lo strato nascosto della prima RBM, l’addestramento inizia presentando i dati di training in ingresso alla prima RBM e da qui propagati. La stessa cosa verra’ ripetuta per tutti gli strati nascosti.
3. Allo stesso modo delle reti auto-associative stacked, dopo il pre-training va fatta la mappatura dello strato di ingresso e quello di uscita utilizzando l’algoritmo di backpropagation (fine-tuning).
Le deep neural networks hanno fatto “risorgere” l’interesse per l’intelligenza artificiale da parte degli scienziati del mondo accademico e di quelli di compagnie come Google, Facebook e Microsoft. Gli sforzi di questi teams sono impressionanti: basta ricordare i progressi fatti nell’ambito del riconoscimento vocale, del riconoscimento delle immagini e delle traduzioni automatiche, giusto per citarne alcuni. La mole di dati nonche’ la potenza di calcolo a disposizione oggi e’ di diversi ordini di grandezza superiore a quella di alcuni anni fa. Questo non puo’ che aiutare lo sviluppo di algoritmi di apprendimento sempre piu’ sofisticati. Di sicuro saremo testimoni di una svolta epocale nell’ambito dell’intelligenza artificiale. Non ci resta che attendere.