È da più di un anno che parliamo tutti di intelligenza artificiale, modelli, embeddings, e reti neurali. L’AI generativa ci ha colti tutti di sorpresa, soprattutto per l’invasione di campo nella sfera creativa che abbiamo sempre ritenuto essere limitata all’umanità.
Sono stati commentati i modelli, confrontati, e ci siamo meravigliati alla loro rapida evoluzione, arrivando addirittura a generare il numero corretto di dita nelle immagini raffiguranti le persone. Cerchiamo di capire grazie al modello degli autoencoder come questo processo generativo non sia magico, ma abbia una logica più semplice di quanto si possa pensare.
L’architettura encoder-decoder: come funziona
Le deep neural network sono sostanzialmente strutture dati che rappresentano un funzionamento ispirato ai neuroni reali. La rete neurale è normalmente costituita da più “strati” di neuroni, e i neuroni di ciascuno strato sono connessi a quelli del successivo. Ciascuna connessione può essere più o meno forte e un peso ne rappresenta l’entità.
Durante la fase di apprendimento l’algoritmo (back propagation) “aggiusta” i pesi cercando di minimizzare l’errore tra il valore generato dalla rete e il risultato atteso.
Gli autoencoder sono una classe di reti neurali orientati all’elaborazione di immagini con una caratteristica centrale: la rete neurale viene considerata non come una scatola nera ma bensì due: l’encoder responsabile per elaborare l’input e convertirlo in un formato intermedio in uno spazio vettoriale (chiamato latent space); il decoder prende in input la rappresentazione nel latent space di un’immagine e la converte in un’immagine di output.
Lo schema di un autoencoder è riportato nella seguente figura:
Ciascun livello della rete neurale ha un numero di parametri che dipende dalla sua geometria, ad esempio uno strato convoluzionale (ovvero capace di analizzare la struttura spaziale dei pixel di un’immagine) per un’immagine di 16×16 pixel con 32 canali per un kernel di 3×3 ha 320 parametri che saranno appresi dal processo di apprendimento. Il fantomatico numero di parametri altro non è che la somma del numero di parametri di ciascun strato della rete.
Un esempio di encoder potrebbe essere strutturato come segue:
Tipo di strato | Forma dell’output | #parametri |
InputLayer | (32,32, 1) | 0 |
Conv2D | (16,16,32) | 320 |
Conv2D | (8, 8, 64) | 18.496 |
Conv2D | (4, 4, 128) | 73.856 |
Flatten | (2048) | 0 |
Dense | (2) | 4.098 |
Non entriamo nel dettaglio della sua geometria, ma questo encoder è fatto da 6 strati, il primo si limita a rappresentare l’input, poi vi sono tre strati convolutivi, uno strato serve ad “appiattire” i dati rendendoli una lista di numeri invece di una tabella, e infine l’ultimo strato ha come output 2 numeri: il valore nello spazio latente dell’encoder.
Poiché questo particolare encoder prende in ingresso immagini di 32×32 pixel in scala di grigio il primo strato ha quella forma. La rete riduce il numero dei pixel (prima 16, poi 8, ed infine 4) aumentando i canali (prima 32, poi 64 ed infine 128).
Il decoder si limita ad invertire il processo e partendo da due numeri che rappresentano un’immagine nel latent space in un’immagine.
Addestrare un autoencoder: il processo passo per passo
Per generare informazioni, in questo caso delle immagini, addestriamo la rete neurale in un modo molto particolare: chiediamo che l’input dell’encoder sia uguale all’output del decoder. Come può la rete imparare da questo processo? L’obiettivo nascosto di questo processo consiste nel costruire una rappresentazione astratta degli oggetti rappresentati dalle immagini nel latent space.
Ovviamente non si tratta di un processo esatto come si nota in figura: l’output non è esattamente uguale all’input ma comunque gli assomiglia. In figura possiamo vedere il valore del latent space associato a ciascuna immagine.
Se disegniamo i valori dello spazio latente in un grafico (sono due valori) e li coloriamo per la classe di appartenenza (0=T-Shirt, 1=Pantaloni, 2=Pullover, 3=vestito, 4=giacca, 5=sandali, 6=camicia, 7=scarpe da tennis, 8=borsa, 9=stivali) scopriremo che oggetti che classifichiamo in modo analogo sono vicini nel latent space. Le t-shirt, ad esempio, corrispondono alla striscia viola nel grafico.
È come se la rete apprendesse le similitudini e le astraesse nel latent space.
La generazione di immagini con l’Autoencoder
La generazione di nuove immagini consiste nel selezionare un valore nuovo (ovverosia non elaborato durante l’apprendimento) nel latent space e convertirlo in immagine utilizzando il decoder. Se scegliamo un punto vicino alle t-shirt otterremo che l’encoder generi una t-shirt nuova.
Il processo generativo è quindi molto meno creativo o “intelligente” di quanto possiamo pensare, sicuramente è affascinante che la rete apprenda che le t-shirt corrispondano a punti vicini nel latent space, ma la generazione di informazione è fortemente legata a come l’addestramento sia stato capace di estrarre le caratteristiche dell’immagine che potranno poi essere utilizzate per individuare lo spazio nel latent space che perturbato porterà alla generazione di immagini simili a quella richiesta.
Conclusioni
Gli autoencoder non sono tra le forme più complesse di AI generativa, ma hanno questa caratteristica di rendere ragionevolmente semplice la comprensione del processo di generazione dell’informazione. I modelli reali sono ben più complessi ed usano altre architetture di rete, ma il processo generativo resta comunque un processo legato ai parametri appresi dalla rete durante l’addestramento. E come si può osservare il processo di generazione è meno “magico” di quanto possa sembrare quando si capisce il principio di funzionamento.
Resta il fatto che comprendere modelli come quelli degli LLM richiede molto studio e molta esperienza, ma spero che d’ora in avanti l’idea che la generazione di informazione da parte di una AI appaia un po’ meno misteriosa di quanto non lo fosse prima.