La retropropagazione è una tecnica di machine learning essenziale per l'ottimizzazione delle reti neurali. Facilita l'uso di algoritmi di discesa del gradiente per aggiornare i pesi della rete, che è il modo in cui i modelli di deep learning alla base dell'intelligenza artificiale (AI) moderna "apprendono".
Abbreviazione di "propagazione all'indietro dell'errore", la retropropagazione è un metodo elegante per calcolare in che modo le modifiche a uno qualsiasi dei pesi o dei bias di una rete neurale influiranno sull'accuratezza delle previsioni del modello. È essenziale per l'uso dell'apprendimento supervisionato, dell'apprendimento semi-supervisionato o dell'apprendimento auto-supervisionato per addestrare le reti neurali.
Sebbene gli equivalenti e i predecessori della retropropagazione siano stati proposti indipendentemente in diversi contesti risalenti agli anni '60, David E. Rumelhart, Geoffrey Hinton e Ronald J. Williams sono stati i a pubblicare primi l'algoritmo di apprendimento formale. Il loro articolo del 1986, "Learning representations by back-propagating errors", forniva la derivazione dell'algoritmo di retropropagazione così come utilizzato e compreso in un moderno contesto di machine learning.
La logica della retropropagazione è che gli strati di neuroni nelle reti neurali sono essenzialmente una serie di funzioni matematiche annidate. Durante l'allenamento, queste equazioni interconnesse sono annidate in un'altra funzione: una "funzione di perdita" che misura la differenza (o "perdita") tra l'output desiderato (o "verità di base") per un dato input e l'output effettivo delle reti neurali.
Possiamo quindi utilizzare la "regola della catena", un principio di calcolo che risale al XVII secolo , per calcolare la velocità con cui ogni neurone contribuisce alla perdita complessiva. In tal modo, possiamo calcolare l'impatto delle modifiche a qualsiasi variabile, ovvero a qualsiasi peso o bias, nelle equazioni che quei neuroni rappresentano.
Matematicamente parlando, la retropropagazione lavora a ritroso dall'output per calcolare in modo efficiente il "gradiente" della funzione di perdita: un vettore di derivate per ogni equazione nella rete. Questo gradiente indica agli algoritmi di ottimizzazione come "discesa del gradiente" quali equazioni regolare e in che direzione, per ridurre le perdite.
Questi tre processi intrecciati - una funzione di perdita che traccia l'errore del modello su diversi input, la propagazione a ritroso di quell'errore per vedere come le diverse parti della rete contribuiscono all'errore e gli algoritmi di discesa del gradiente che regolano i pesi dei modelli di conseguenza - costituiscono il modo in cui i modelli di deep learning "imparano". Per questo motivo, la retropropagazione è fondamentale per l'addestramento dei modelli di reti neurali, dai più semplici percettori multistrato alle complesse architetture di reti neurali profonde utilizzate per l'AI generativa.
Newsletter di settore
Resta al passo con le tendenze più importanti e interessanti del settore relative ad AI, automazione, dati e altro con la newsletter Think. Leggi l'Informativa sulla privacy IBM.
L'abbonamento sarà fornito in lingua inglese. Troverai un link per annullare l'iscrizione in tutte le newsletter. Puoi gestire i tuoi abbonamenti o annullarli qui. Per ulteriori informazioni, consulta l'Informativa sulla privacy IBM.
Poiché il processo di retropropagazione è fondamentale per il modo in cui le reti neurali vengono addestrate, una spiegazione utile del processo richiede una comprensione pratica di come le reti neurali fanno previsioni.
È soprattutto importante comprendere lo scopo e il contesto di "pesi" e "bias": i parametri del modello regolabili che vengono ottimizzati tramite la retropropagazione e la discesa del gradiente.
Lo scopo delle reti neurali è imitare approssimativamente la struttura del cervello umano. Sono composti da molti nodi (o neuroni) interconnessi, disposti in strati. Le reti neurali fanno previsioni una volta che i dati di input originali hanno effettuato un "passaggio in avanti" attraverso l'intera rete.
I neuroni nello "strato di input" ricevono dati di input, solitamente sotto forma di incorporamento vettoriale, con ogni neurone di input che riceve una singola caratteristica del vettore di input. Ad esempio, un modello che funziona con immagini in scala di grigi da 10x10 pixel avrà in genere 100 neuroni nel suo strato di input, con ogni neurone di input corrispondente a un singolo pixel. Le reti neurali quindi richiedono tipicamente input di dimensioni fisse, anche se tecniche come il pooling o la normalizzazione possono fornire una certa flessibilità.
In una rete neurale feedforward standard, ogni neurone nello strato di input è connesso a ciascuno dei neuroni nello strato successivo, i quali sono a loro volta connessi ai neuroni nel successivo strato, e così via fino allo strato di output dove vengono fatte le previsioni finali. Gli strati intermedi tra quello di input e quello di output, chiamati livelli nascosti della rete, sono quelli in cui avviene la maggior parte dell' "apprendimento".
Sebbene alcune architetture di reti neurali specializzate, come una combinazione di modelli esperti o reti neurali convoluzionali, comportino variazioni, aggiunte o eccezioni a questa semplice disposizione, tutte le reti neurali utilizzano questa struttura centrale.
Sebbene ogni neurone riceva input da ciascun nodo del livello precedente, non tutti questi input hanno la stessa importanza. Ad ogni connessione tra due neuroni viene assegnato un "peso" unico: un moltiplicatore che aumenta o diminuisce il contributo di un neurone a un neurone nello strato successivo.
A ogni singolo neurone può essere assegnato anche un "bias": un valore costante aggiunto alla somma degli input ponderati dei neuroni nello strato precedente.
L'obiettivo finale della retropropagazione e della discesa del gradiente è calcolare i pesi e le distorsioni che forniranno le migliori previsioni del modello. Ai neuroni corrispondenti a caratteristiche dei dati che sono significativamente correlate a previsioni accurate viene assegnato un peso maggiore, mentre ad altre connessioni può essere assegnato un peso prossimo allo zero.
Le moderne reti neurali profonde, spesso con dozzine di livelli nascosti ciascuno contenente molti neuroni, potrebbero comprendere migliaia, milioni o, nel caso della maggior parte dei modelli linguistici di grandi dimensioni (LLM), miliardi di parametri regolabili.
Ogni neurone è configurato per eseguire un'operazione matematica, chiamata "funzione di attivazione", sulla somma degli input pesati in modo variabile che riceve dai nodi nello strato precedente. Le funzioni di attivazione introducono la "non linearità", che consente al modello di acquisire pattern complessi nei dati di input e produrre gradienti che possono essere ottimizzati. L'utilizzo delle sole funzioni di attivazione lineare fa collassare le reti neurali in un modello di regressione lineare .
Le funzioni di attivazione comuni nelle reti neurali includono:
Consideriamo un'ipotetica unità z nascosta, con una funzione di attivazione tanh e un termine di bias t, nel secondo strato di una rete neurale con 3 nodi di input, a, b e c, nel suo strato di input. Ciascuna delle connessioni tra i nodi di input e il nodo z ha un peso univoco, w. Possiamo descrivere il valore di output che il nodo z passerà ai neuroni nel livello successivo con l'equazione semplificata z = tanh(waz*a + wbz*b + wcz*c + t).
Il neurone z è collegato ai neuroni nello strato successivo. Quell'equazione per z fa quindi parte delle funzioni di attivazione nel livello successivo e, per estensione, anche è parte di ogni funzione di attivazione per qualsiasi neurone in qualsiasi livello successivo.
Come vedremo nelle sezioni seguenti, la retropropagazione è un algoritmo straordinariamente veloce ed efficiente per districare l'enorme rete di variabili ed equazioni interconnesse in una rete neurale.
Per illustrare l'efficienza della retropropagazione, Michael Nielsen la confronta con un approccio alternativo semplice e intuitivo al calcolo del gradiente della funzione di perdita di una rete neurale nel suo libro di testo online, "Neural Networks and Deep Learning".
Come spiega Nielsen, si può facilmente stimare l'impatto delle modifiche a qualsiasi peso specifico wj nella rete semplicemente completando un passaggio in avanti per due valori leggermente diversi di wj, mantenendo invariati tutti gli altri parametri e confrontando la perdita risultante per ogni passaggio. Formalizzando quel processo in una semplice equazione e implementando alcune righe di codice in Python, si può automatizzare quel processo per ogni peso nella rete.
Immagina però che nel tuo modello ci siano 1 milione di pesi, il che sarebbe un numero piuttosto modesto per un moderno modello di deep learning. Per calcolare l'intero gradiente, devi completare 1.000.001 passaggi in avanti attraverso la rete: 1 per stabilire una linea di riferimento e poi un altro passaggio per valutare le modifiche a ciascuno dei milioni di pesi.
Con la retropropagazione si può raggiungere lo stesso obiettivo in 2 passaggi: 1 passaggio in avanti e 1 passaggio all'indietro.
Per semplificare la spiegazione del funzionamento della retropropagazione, sarà utile rivedere brevemente alcuni concetti e termini matematici fondamentali.
La regola della catena è essenziale per calcolare le derivate delle funzioni di attivazione nelle reti neurali, che sono composte dagli output delle funzioni di attivazione di altri neuroni negli strati precedenti.
Sebbene la logica alla base della retropropagazione sia relativamente semplice, la matematica e la notazione possono diventare molto complesse, specialmente per chi non ha familiarità con il calcolo delle variabili.
Partendo dall'output del modello, la retropropagazione applica la regola della catena per calcolare l'influenza delle modifiche a ogni singolo parametro delle reti neurali sull'errore complessivo delle previsioni del modello.
A livello teorico, lo scopo della retropropagazione è addestrare una rete neurale a fare previsioni migliori attraverso l'apprendimento supervisionato. A livello più pratico, l'obiettivo della retropropagazione è determinare in che modo i pesi e le distorsioni del modello devono essere regolati per ridurre al minimo gli errori misurati da una "funzione di perdita".
A livello tecnico e matematico, l'obiettivo della retropropagazione è quello di calcolare il gradiente della funzione di perdita rispetto a ciascuno dei singoli parametri della rete neurale. In termini più semplici, la retropropagazione utilizza la regola della catena per calcolare la velocità con cui la perdita cambia in risposta a qualsiasi modifica di un peso specifico (o bias) nella rete.
In generale, l'addestramento delle reti neurali con la retropropagazione prevede i seguenti passaggi:
Le reti neurali producono previsioni tramite propagazione in avanti. La propagazione in avanti è essenzialmente una lunga serie di equazioni nidificate, con gli output delle funzioni di attivazione da un livello di neuroni che fungono da input per le funzioni di attivazione dei neuroni nel livello successivo.
L'addestramento dei modelli inizia in genere con un'inizializzazione casuale dei pesi e dei bias. Gli iperparametri del modello, come il numero di strati nascosti, il numero di nodi in ogni strato e le funzioni di attivazione per neuroni specifici, sono configurati manualmente e non sono soggetti ad addestramento.
In ogni passaggio in avanti viene prelevato un input dal set di dati di addestramento. I nodi del livello di input ricevono il vettore di input e ciascuno passa il proprio valore, moltiplicato per un peso iniziale casuale, ai nodi del primo layer nascosto. Le unità nascoste prendono la somma ponderata di questi valori di output come input per una funzione di attivazione, il cui valore di output (condizionato da un peso iniziale casuale) funge da input per i neuroni nello strato successivo. Questo processo continua fino allo strato di output, dove avviene la previsione finale.
Consideriamo questo esempio semplificato di una rete neurale che classifica gli input in una delle 5 categorie:
In una rete ben addestrata, questo modello produrrà costantemente un valore di probabilità elevato per la classificazione corretta e produrrà valori di bassa probabilità per le altre classificazioni errate. Tuttavia, questa rete neurale non è ancora stata addestrata. A questo punto, i suoi pesi e i suoi bias hanno valori iniziali casuali, quindi le sue previsioni sono generalmente imprecise.
Dopo ogni passaggio in avanti, una "funzione di perdita" misura la differenza (o "perdita") tra l'output previsto del modello per un determinato input e le previsioni corrette (o "ground truth") per quell'input. In altre parole, misura la differenza tra l'output effettivo del modello e quello desiderato.
Nell'apprendimento supervisionato, che utilizza dati etichettati, la verità di base è fornita dalle annotazioni manuali. Nell'apprendimento auto-supervisionato, che maschera o trasforma parti di campioni di dati non etichettati e incarica i modelli ricostruendoli, mentre il campione originale stesso funge da ground truth.
L'obiettivo di questa funzione di perdita è quantificare l'imprecisione in modo che rifletta in modo appropriato sia la natura che l'entità dell'errore dell'output del modello per ogni input. Diverse formule matematiche per calcolare la perdita sono più adatte a compiti specifici: ad esempio, le varianti dell'errore quadratico medio funzionano bene per i problemi di regressione, mentre le varianti della perdita di entropia incrociata funzionano bene per la classificazione.
Poiché la funzione di perdita prende come input l'output di una rete neurale, e quell'output della rete neurale è una funzione composita che comprende molte funzioni di attivazione annidate dei singoli neuroni, derivare la funzione di perdita implica derivare l'intera rete. A tale scopo, la retropropagazione utilizza la regola della catena.
"Funzione di perdita", "funzione di costo" o "funzione di errore"?
Vale la pena notare rapidamente che in alcuni contesti, i termini funzione di costo o funzione di errore sono usati al posto di funzione di perdita, con "costo" o "errore" che sostituiscono "perdita".
Sebbene alcuni testi sull'apprendimento automatico assegnino una sfumatura unica a ciascun termine, in genere sono intercambiabili.1 Una funzione obiettivo è un termine più ampio per qualsiasi funzione di valutazione che vogliamo minimizzare o massimizzare. La funzione di perdita, la funzione di costo o la funzione di errore si riferiscono specificamente ai termini che vogliamo minimizzare.
A partire dal livello finale, un passaggio "all'indietro" differenzia la funzione di perdita per calcolare in che modo ogni singolo parametro della rete contribuisce all'errore complessivo per un singolo input.
Tornando al nostro esempio precedente del modello classificatore, inizieremmo con i 5 neuroni dello strato finale, che chiameremo livello L. Il valore softmax di ciascun neurone di output rappresenta la probabilità, su 1, che un input appartenga alla loro categoria. In un modello perfettamente addestrato, il neurone che rappresenta la classificazione corretta avrebbe un valore di output vicino a 1 e gli altri neuroni avrebbero un valore di output vicino allo 0.
Per il momento ci concentreremo sull'unità di output che rappresenta la previsione corretta, che chiameremo Lc. La funzione di attivazione di Lc è una funzione composita, contenente le numerose funzioni di attivazione annidate dell'intera rete neurale dal livello di input a quello di output. Ridurre al minimo la funzione di perdita comporterebbe l'esecuzione in tutta la rete di regolazioni che portino l'uscita della funzione di attivazione di Lc più vicina a 1.
Per fare ciò, avremo bisogno di sapere in che modo qualsiasi modifica nei livelli precedenti cambierà l'output di Lc. In altre parole, avremo bisogno di trovare le derivate parziali della funzione di attivazione di Lc.
L'output della funzione di attivazione di Lc dipende dai contributi che riceve dai neuroni del penultimo strato, che chiameremo strato L-1. Un modo per modificare l'output di Lc è modificare i pesi tra i neuroni in L-1 e Lc. Calcolando la derivata parziale di ogni peso L-1 rispetto agli altri pesi, possiamo vedere come l'aumento o la diminuzione di ognuno di essi porterà l'output di Lc più vicino (o più lontano) a 1.
Ma questo non è l'unico modo per modificare l'output di Lc. I contributi che Lc riceve dai neuroni L-1 sono determinati non solo dai pesi applicati ai valori di output di L-1, ma dai valori di output effettivi (pre-peso) stessi. I valori di output dei neuroni L-1 , a loro volta, sono influenzati dai pesi applicati agli input che ricevono da L-2. Quindi possiamo differenziare le funzioni di attivazione in L-1 per trovare le derivate parziali dei pesi applicati ai contributi di L-2. Queste derivate parziali ci mostrano come qualsiasi variazione di un peso L-2 influenzerà gli output in L-1, che successivamente influenzeranno il valore di output di Lc e quindi la funzione di perdita.
Con la stessa logica, potremmo anche influenzare i valori di output che i neuroni L-1 ricevono dai neuroni L-2 regolando i contributi che i neuroni L-2 ricevono dai neuroni in L-3. Quindi troviamo le derivate parziali in L-3, e così via, ripetendo ricorsivamente questo processo fino a raggiungere lo strato di input. Quando abbiamo finito, abbiamo il gradiente della funzione di perdita: un vettore della sua derivata parziale per ogni parametro di peso e bias della rete.
Ora abbiamo completato un passaggio in avanti e un passaggio all'indietro per un singolo esempio di addestramento. Tuttavia, il nostro obiettivo è addestrare il modello affinché si generalizzi bene a nuovi input. A tale scopo, è necessario eseguire l'addestramento su un numero elevato di campioni che riflettano la diversità e la gamma di input su cui il modello avrà il compito di effettuare previsioni dopo l'addestramento.
Ora che abbiamo i gradienti della funzione di perdita rispetto a ciascun parametro di peso e distorsione nella rete, possiamo ridurre al minimo la funzione di perdita e quindi ottimizzare il modello, utilizzando la discesa del gradiente per aggiornarne i parametri.
Scendendo verso il basso, il gradiente della funzione di perdita diminuirà la perdita. Poiché il gradiente che abbiamo calcolato durante la retropropagazione contiene le derivate parziali per ogni parametro del modello, sappiamo in quale direzione "spostare" ciascuno dei nostri parametri per ridurre le perdite.
Ogni passaggio riflette l'apprendimento del modello dai suoi dati di addestramento. Il nostro obiettivo è aggiornare i pesi in modo iterativo fino a raggiungere il gradiente minimo. Lo scopo degli algoritmi di discesa del gradiente è trovare le regolazioni specifiche dei parametri che ci sposteranno verso il basso nel gradiente nel modo più efficiente.
La dimensione di ogni passaggio è un iperparametro regolabile, chiamato tasso di apprendimento. La scelta del giusto tasso di apprendimento è importante per un addestramento efficiente ed efficace.
Ricordiamo che le funzioni di attivazione in una rete neurale sono non lineari. Alcuni gradienti possono avere una forma approssimativamente a U: muovendosi in una direzione si scende in basso lungo il gradiente, ma continuando a muoversi in quella direzione alla fine si risale lungo il gradiente.
Un basso tasso di apprendimento ci assicura di andare sempre nella giusta direzione, ma calcolare così tante modifiche richiede tempo e denaro dal punto di vista computazionale. Un alto tasso di apprendimento è efficiente dal punto di vista computazionale, ma rischia di superare il minimo.
Un'altra considerazione da fare nella discesa del gradiente è la frequenza con cui aggiornare i pesi. Un'opzione consiste nel calcolare i gradienti per ogni esempio nel set di dati di addestramento, quindi prendere una media di tali gradienti e utilizzarla per aggiornare i parametri. Il processo viene ripetuto iterativamente in una serie di epoche di addestramento fino a quando il tasso di errore si stabilizza. Questo metodo è la discesa del gradiente batch.
Quando il set di dati di addestramento è molto ampio, come in genere nel deep learning, la discesa del gradiente batch comporta tempi di elaborazione proibitivi. Il calcolo dei gradienti per milioni di esempi per ogni iterazione di aggiornamenti del peso diventa inefficiente. Nella discesa stocastica del gradiente (SGD), ogni epoca utilizza un singolo esempio di addestramento per ogni passo. Sebbene la perdita possa variare da un'epoca all'altra, converge rapidamente al minimo nel corso di molti aggiornamenti.
La discesa del gradiente mini-batch rappresenta un approccio intermedio. Gli esempi di addestramento vengono campionati casualmente in batch di dimensioni fisse e i loro gradienti vengono quindi calcolati e mediati. Ciò riduce i requisiti di memorizzazione rispetto alla discesa del gradiente batch riducendo al contempo l'instabilità relativa della SGD.
Addestra, convalida, adatta e implementa le funzionalità di AI generativa, foundation model e machine learning con IBM watsonx.ai, uno studio aziendale di nuova generazione per builder AI. Crea applicazioni AI in tempi ridotti e con una minima quantità di dati.
Metti l'AI al servizio della tua azienda grazie all'esperienza leader di settore e alla gamma di soluzioni di IBM nel campo dell'AI.
Reinventa i flussi di lavoro e le operazioni critiche aggiungendo l'AI per massimizzare le esperienze, il processo decisionale in tempo reale e il valore di business.
¹ "Deep Learning", Goodfellow et al, MIT Press, 2016.
IBM web domains
ibm.com, ibm.org, ibm-zcouncil.com, insights-on-business.com, jazz.net, mobilebusinessinsights.com, promontory.com, proveit.com, ptech.org, s81c.com, securityintelligence.com, skillsbuild.org, softlayer.com, storagecommunity.org, think-exchange.com, thoughtsoncloud.com, alphaevents.webcasts.com, ibm-cloud.github.io, ibmbigdatahub.com, bluemix.net, mybluemix.net, ibm.net, ibmcloud.com, galasa.dev, blueworkslive.com, swiss-quantum.ch, blueworkslive.com, cloudant.com, ibm.ie, ibm.fr, ibm.com.br, ibm.co, ibm.ca, community.watsonanalytics.com, datapower.com, skills.yourlearning.ibm.com, bluewolf.com, carbondesignsystem.com, openliberty.io