Una piccola introduzione al Deep Learning
La teoria dietro il cosiddetto perceptron, fondamentalmente l'elemento chiave delle reti neurali artificiali e quindi di tutti i moderni modelli di deep learning o di "IA", è vecchia quasi quanto il transistor. Quindi, se questi algoritmi sono vecchi come i computer stessi, perché c'è voluto più di mezzo secolo prima di essere posti al centro dell'attenzione della scienza e della tecnologia? La risposta è: è complicato. Letteralmente. Eseguire i modelli esistenti è relativamente facile, ma addestrarli non lo è. Soprattutto non se si hanno in mente applicazioni nel mondo reale. Ma prima di poter analizzare alcune delle scoperte che ci hanno portato dove siamo oggi, facciamo un passo indietro e capiamo come funzionano effettivamente le reti neurali artificiali. Il primo passo quando si ricrea il cervello in un computer è ovviamente quello di guardare come il cervello fa i suoi calcoli. Qui sotto c'è uno schizzo di un neurone, il componente di calcolo di base del sistema nervoso di quasi tutti gli animali.
Ci sono anche altri tipi di cellule che si trovano nel cervello, ma i neuroni sono responsabili dei processi di comunicazione elettrochimica che possono non solo trasmettere ma anche manipolare le informazioni. Quando viene attivato, un neurone spara un impulso elettrico lungo l'assone a tutti i neuroni collegati. La forza del segnale ricevuto alla fine è determinata dalle connessioni sinaptiche tra i singoli neuroni. Quando il segnale totale in arrivo supera una soglia di eccitazione, anche il neurone ricevente si attiva. In modo semplificato, possiamo scomporre il tutto nelle sue componenti usando una struttura a grafo di nodi e spigoli con pesi per ogni connessione. In una rete completamente connessa, ogni neurone in uno strato è connesso ad ogni neurone nello strato successivo.
Dato un insieme di valori numerici in ingresso, possiamo formalmente calcolare il valore del primo neurone nello strato centrale a sinistra come la somma di tutti gli ingressi moltiplicata per il peso della rispettiva connessione come
Se raggruppiamo tutti i pesi di tutti i neuroni in una matrice, possiamo scrivere i valori del secondo strato come un vettore:
Poi applichiamo una funzione di attivazione f che imita il processo in un neurone in cui l'uscita è attiva solo quando l'ingresso raggiunge una certa soglia. L'output di questa rete per un dato input x è quindi
Una scelta comune per la funzione di attivazione è un cosiddetto sigmoide funzione,
mostrato nel grafico qui sotto. La forma della funzione e il suo comportamento continuo differiscono da qualcosa che si troverebbe nei sistemi biologici, ma risulta essere sufficiente.
Aggiungendo un ulteriore neurone di bias con un valore fisso di uno agli strati di ingresso e nascosto, il grafico di attivazione dei singoli neuroni può essere spostato a sinistra e a destra, mentre i pesi degli altri ingressi lo scalano verso l'alto e verso il basso. In combinazione con gli altri neuroni, è facile creare funzioni arbitrarie-dimensionali di qualsiasi forma continua sintonizzando le voci codificate nei pesi delle due matrici. Questa semplice configurazione potrebbe non sembrare molto, ma l'architettura feedforward multistrato è in realtà capace di approssimare qualsiasi funzione continua su domini compatti. Fondamentalmente questo significa che qualsiasi algoritmo che prende un input e produce un output potrebbe essere realizzato come una rete neurale. La grande domanda è: come otteniamo i pesi? Per reti incredibilmente piccole, potrebbe anche essere possibile sintonizzarle a mano, ma questo diventa inattuabile molto velocemente. Invece, le reti sono addestrate su insiemi di dati esistenti e veritieri, facendo corrispondere input dati a output pre-etichettati. In questo modo, si può calcolare l'"errore" o la differenza tra l'output del modello e l'output desiderato e poi usare il processo di backpropagation per aggiornare iterativamente i parametri del modello. La backpropagation fa uso della differenziazione automatica e della discesa del gradiente per abbassare leggermente l'errore ad ogni passo dell'iterazione. La discesa del gradiente funziona guardando la funzione che ha generato l'errore (cioè l'intero grafico computazionale del modello) e prendendo le sue derivate parziali rispetto ai parametri allenabili. Cambiando i parametri nella direzione della loro derivata che abbassa l'errore totale, il modello può essere sintonizzato iterativamente per produrre l'output desiderato. Normalmente, calcolare le derivate di complicate funzioni annidate come quella descritta sopra può essere molto noioso, ma se si conosce la derivata della funzione di attivazione può essere facilmente automatizzato. Ancora meglio, poiché essenzialmente tutti i chip dei computer calcolano scomponendo le funzioni in semplici operazioni di addizione e moltiplicazione, il calcolo delle derivate di queste funzioni annidate può essere fatto con una semplice applicazione della regola della catena.
Col tempo divenne chiaro che i problemi realistici avrebbero richiesto reti estremamente grandi. Il primo problema sta nel fatto che per reti così grandi, le derivate di una normale funzione di attivazione di tipo sigmoide tendono a svanire. Questo può essere facilmente visto guardando il grafico sopra e immaginando molti neuroni che contribuiscono all'input totale che apparirebbe sull'asse delle x. Andando troppo a sinistra o a destra del grafico, la funzione cambia molto poco, rendendo difficile aggiornare i parametri della rete in modo ragionevole. Questo problema è stato risolto allontanandosi ulteriormente dalle ispirazioni biologiche originali e introducendo invece l'unità lineare rettificata, o ReLU, come funzione di attivazione.
Questa funzione è estremamente semplice - è solo x per x>0 e 0. Tuttavia, mantiene abbastanza non linearità per catturare sufficientemente il comportamento complesso all'interno delle reti neurali e ha il vantaggio aggiunto che il suo gradiente non svanisce - almeno non per grandi valori di x. È anche molto più facile da calcolare rispetto alle complicate funzioni sigmoidi non lineari, rendendo i modelli molto più performanti. Oggi ci sono molte varianti di ReLU, ma è diventata una pietra miliare dei modelli di Deep Learning ed è usata praticamente ovunque, anche nei modelli disponibili in Transkribus.
Un altro grande problema affrontato dalle reti più grandi è che con un numero crescente di parametri, l'overfitting diventa più prevalente. Per garantire che il modello non si limiti a replicare i dati di addestramento, ma impari anche a generalizzare da essi, i metodi di regolarizzazione si sono rivelati molto utili. Uno dei più affermati fa uso dei cosiddetti dropout layers. Durante l'addestramento, uno strato di dropout prende uno strato di neuroni nascosti e imposta l'output di neuroni scelti a caso a zero - indipendentemente dal loro input. Questo costringe la rete a diffondere qualsiasi informazione e impedisce la dipendenza da certe correlazioni che potrebbero essere prevalenti nei dati di addestramento. I livelli di dropout sono diventati un'altra pietra miliare del deep learning e vengono utilizzati anche per l'addestramento dei modelli in Transkribus.
Qualcosa che non abbiamo ancora menzionato, ma che è anche molto importante per i grandi modelli, è come impostare i parametri inizialmente. Dopo tutto, il processo di addestramento iterativo deve iniziare da qualche parte. Per piccole reti, di solito è sufficiente impostare i parametri in modo casuale secondo qualche distribuzione di probabilità. Questo significa però che gli strati consecutivi si aspettano queste distribuzioni e un piccolo cambiamento dei parametri in uno strato iniziale può portare a enormi cambiamenti lungo la strada per i modelli con molti strati nascosti. Al fine di addestrare queste reti profonde, diventa necessario standardizzare gli ingressi per ogni strato. Questo viene fatto utilizzando un processo chiamato normalizzazione batch. Guarda l'uscita di uno strato specifico per un gruppo di ingressi diversi e poi ridimensiona l'uscita in modo che l'intero set mantenga determinate proprietà statistiche - di solito una media di zero e una varianza di uno. Proposta per la prima volta nel 2015, questa tecnica è ora prevalente in quasi tutte le reti neurali profonde, comprese quelle utilizzate da Transkribus.
Ci sono stati molti, molti altri miglioramenti nel campo delle reti neurali artificiali nel corso degli anni. Alcuni di essi erano piccoli, mentre altri sono stati dei veri e propri game changer come le reti convoluzionali, le reti ricorrenti e, più recentemente, i trasformatori (restate sintonizzati per saperne di più in futuri post sul blog). Combinati, hanno infine reso possibile l'addestramento di reti gigantesche che possono eseguire compiti incredibili. Insieme all'aumento di chip di calcolo parallelo incredibilmente potenti che sono stati originariamente progettati per i videogiochi (ma si sono rivelati perfetti anche per l'addestramento delle reti neurali), ora abbiamo finalmente i mezzi per sfruttare la rivoluzione dei big-data.