Transcription
In questo video ti mostro come realmente si addestra un Large Language Model con codice alla mano. Senza troppe parole, direttamente andiamo al punto. Prendiamo il modello Llama 3.2 da 3 miliardi di parametri. Abbiamo già scaricato un dataset in locale con circa 200 esempi sul quale farlo addestrare e ti mostro tutto passo dopo passo.
Prima di iniziare, per le persone nuove che arrivano, mi presento. Mi chiamo Simone Rizzo e sono laureato magistrale in Intelligenza Artificiale. E questa è la mia società, Inferenia, dove quello che facciamo è potenziare il business dei nostri clienti con l'uso dell'intelligenza artificiale. Quindi, se sei interessato, link in bio.
Per prima cosa, andiamo su Google Colab, perché ci danno gratuitamente le schede video T4. Quindi utilizziamo questa scheda video per addestrare il nostro modello. Apriamo il primo blocco e dobbiamo partire con l'installazione dei pacchetti, in particolare `unsloth`, che ci permetterà di addestrare il modello in un modo più efficiente e veloce.
Una volta installati i package, andiamo a scaricare ed inizializzare il modello. In particolare, importiamo `torch`, importiamo `FastLanguageModel` e come modello, qua su `name`, gli passiamo `llama-3-8b-instruct`, che è proprio il modello che vogliamo utilizzare. Perché questo modello? Perché è leggero, efficiente e veloce, ed è uno degli ultimi usciti di casa Meta. Quindi, andiamo ed eseguiamo quest'altro blocco.
Tutto il codice che vedrai qui, te lo fornirò gratuitamente nella descrizione del video. Possiamo vedere che sono in totale 6.43 GB che adesso sta scaricando il server di Google Colab in locale sul suo spazio. Una volta che, dopo, andiamo ad aggiornare tutti i pesi del modello, facciamo il training, lo andremo ad esportare in un formato consono per `Ollama` e quindi lo andiamo a scaricare sul nostro PC locale ed eseguirlo su `Ollama`. Se non hai `Ollama` o non lo hai già installato, segui questo mio video guida che ti spiegherò tutto.
Ecco qui, ha terminato di scaricare il tokenizer e tutto il modello. Importante su questa classe qui di `FastLanguageModel.from_pretrained`, abbiamo un parametro che si chiama `load_in_4bit`, che adesso sta settato in `False`. Quindi, abbiamo scaricato tutti i pesi del modello a full length, quindi a `float16`. Mentre, se li volevamo quantizzare a 4 bit, bastava mettere `True`. Cos'è la quantizzazione? È un'approssimazione dei pesi in modo tale che il modello sia più leggero ed efficiente, al costo di perdere un po' di qualità. Ma siccome è un modello piccolo da 3 miliardi, l'ho settato a `False`.
Nel secondo blocco, non ti preoccupare, ci sono tantissimi parametri. Però, quello che facciamo adesso è impostiamo tutti i parametri necessari per `LoRA`. È una tecnica super popolare per addestrare i modelli in modo più efficiente, in cui tu non vai a spostare tutti i pesi del modello, ma aggiungi un piccolo numero di nuovi pesi, che sono quelli che vai ad addestrare e che guidano poi la generazione dell'output del modello. In poche parole, anziché addestrarlo tutto, addestri una piccola parte. Fatto ciò, continuiamo con i nostri blocchi.
E in questo blocco qui, in particolare, andiamo a provare il modello così, con la conoscenza che già ha. Ad esempio, parlami della funzione di Fibonacci e vediamo che conoscenza ha sulla funzione di Fibonacci. Ecco qua, ci risponde: "La funzione Fibonacci è una sequenza di numeri naturali che segue un pattern di ricorrenza, dove ogni numero è la somma dei due numeri precedenti e così via e così via". Poi ci spiega anche la formula.
Adesso andiamo alla parte core, cioè principale di tutto il progetto, di tutto il video, ovvero addestrare questo modello nel fare una cosa specifica sul dataset che abbiamo a disposizione. Adesso questo modello si dice pre-trained, ha già quindi una conoscenza di base su tante cose, ma non specifica di quello che vogliamo. Io ho realizzato questo dataset qui e ti spiego come è formato. Abbiamo una colonna `user` e una colonna `prompt`. Aspetta che te l'ho già aperto, praticamente. A sinistra abbiamo il prompt che mette in input l'utente e a destra abbiamo la risposta che dovrebbe darci lei, cioè il Large Language Model.
Il dataset è stato creato in modo tale che tu scrivi un prompt e lei ti riscrive il prompt in modo tale che sia perfetto per `Flux`. Cos'è `Flux`? `Flux` è questa AI qua, che è la migliore AI del momento per la generazione di immagini. Siccome per generare un'immagine noi dobbiamo scrivere il prompt, per scrivere questo prompt ci stanno tanti parametri, è molto specifico il prompt per un'AI generativa di immagini. Siccome io non conosco questa terminologia, faccio sì che lei capisca la mia richiesta e scrive al posto mio il prompt da dare a `Flux`. E quindi questo dataset fa proprio questa cosa qua.
Abbiamo a sinistra: "Fammi un prompt Flux per un cute furry monster in una foresta". E da lì in poi vediamo il prompt risultante che ha tutti dei parametri: `tro-ar 169`, `4k`, `photorealistic`, `high definition`, `movie scene`, che sono tutte terminologie, sintassi, appunto, dei modelli generativi di immagini. Quindi, il task è molto specifico di quello che vogliamo fare. E possiamo fare una prova in cui prendiamo un prompt di questi: "Flux prompt of a woman warrior in a futuristic desert, etc., etc." e facciamo la prova per vedere se il modello, con la conoscenza che ha di base, è in grado di realizzarci questo prompt. Vedi, vedi che non è in grado. Ci mette il titolo: "A female warrior navigates". Cioè, ci sta creando una storia, non ci sta creando un prompt con tutti quei tag `high quality`, eccetera. Vedi, non è in grado. Per questo, adesso, adesso ti mostro come, andandolo ad addestrare, dopo imparerà a creare questi prompt specifici per `Flux`.
Nel dubbio, ne facciamo pure un'altra. Quindi, "Flux prompt of a Viking with a blade". Voglio fare un'immagine di un vichingo con una spada. E vediamo che qui non ci sta creando un prompt, ci sta scrivendo una storia, perché il modello, a quanto pare, non è mai stato addestrato a generare altri prompt per altri modelli, ma semplicemente ha visto del testo e ti genera la storia. Perfetto.
Quindi, adesso, quello che facciamo è carichiamo il dataset prima e lo formattiamo nel formato corretto. Quindi, il dataset adesso lo carichiamo. "Flux training" lo carico qui. Caricato il dataset. Questo qui è il tokenizer della `chat_template`, che siccome il 3.2 utilizza lo stesso formato del 3.1, prendiamo quello del 3.1. Qui andiamo a leggere il dataset che si chiama `Flux training dataset`. Possiamo fare `df.head()` e poi vedere com'è formato. Si ha `user`, `prompt` e `prompt_risultante_del_modello`.
Cancello poi, elimino le righe nulle. E da qui in poi andiamo a formattare il formato del dataframe in Pandas nel formato con cui è consono per addestrare un modello, che è fatto in questo modo: è una lista di dizionari, dove abbiamo una chiave che è `conversation`, `conversazioni`. Dentro queste `conversazioni` si ha una lista di altri dizionari, dove si ha la chiave `content`, che è il contenuto, quindi proprio la stringa, e l'altra chiave che è `role`, il ruolo, cioè chi è che sta parlando, se è l'utente o lei. Quindi, questo può essere `assistant` se sta parlando lei, `user` sta parlando l'utente. E quindi, nelle colonne che ti ho mostrato prima, andrà formattato in `content` il testo, `role` `user`, e ci va messo questo testo qua. Mentre `content` la risposta dell'AI, `role` `assistant`, per formattare la risposta dell'agente AI.
Quindi, questo blocco adesso che lo eseguo, vi faccio vedere un risultato. Dataset, anzi, faccio vedere il dataset che è fatto così. Se settiamo `conversations[0]`, te ne faccio vedere una, che a sua volta è una lista formata da contenuto dell'utente, che è la richiesta dell'utente, contenuto dell'assistente AI, che è la risposta. Quindi, il dataset va formattato in questo modo per tutti gli esempi che abbiamo.
Adesso andiamo nella parte di addestramento del modello. Allora, per addestrarlo, io utilizzo `SFTTrainer`, che è una libreria super efficiente per effettuare l'addestramento. Importante su `dataset_text_field` settare `conversation`, poiché è la chiave che contiene tutta la lista delle conversazioni. Poi qui si ha una marea di parametri che possiamo settare, sono veramente tanti. E al momento ho settato questi, ma andrebbe fatta una `Grid Search`, che è la ricerca dei migliori iperparametri. Ad esempio, `max_steps` mettiamo 50 per non attendere troppo. `seed_output_dir` dove salvare l'optimizer. Comunque, settiamo adesso questo codice qui.
Ecco qua, ci ha settato tutto, tutti i dati che abbiamo nel dataset, perché qua gli abbiamo dovuto passare il modello, il tokenizer e il dataset. Quindi, questa classe qui ci setta e ci prepara tutto per l'addestramento. Successivamente, formattiamo con i tag che si utilizzano su Llama 3.2, perché Llama 3.2 è stato addestrato con questi tag e vanno rispettati. E il tag di inizio dell'header e tag di terminazione dell'header, con la richiesta dell'utente e quella della risposta dell'assistente. Formattiamo tutte le istruzioni in questo modo qui.
Possiamo vedere un attimo con la GPU come stiamo messi. Abbiamo 14 GB liberi. E con questo blocco di codici qui, avviamo il vero e proprio addestramento. Vai, iniziamo. Ci metterà un po' di tempo. Ecco qua, avremo poi una tabella risultante con tutti gli step e la `training loss`.
Allora, step 1, abbiamo `training loss` di 4.89. Cosa significa la `training loss`? Significa l'errore che il modello sta avendo sul dataset che gli abbiamo dato. Possiamo vedere che all'inizio è un errore alto e mano a mano che viene addestrato sui dati, si sta abbassando. Da 4.89, noi siamo arrivati a 2.298, 3.2. E questo ci fa capire che sta letteralmente imparando dai dati. Stiamo spostando i pesi del modello, che sarebbero i parametri che rappresentano le interconnessioni fra i neuroni della rete neurale del modello.
Terminato l'addestramento, possiamo vedere come all'ultimo step siamo arrivati addirittura a 0.89. Significa che il modello letteralmente ha imparato molto bene dal dataset, pur essendo un dataset relativamente molto, molto piccolo.
Andiamo adesso a fare l'inferenza sul nuovo modello addestrato per vedere come performa. "Flux prompt: Viking warrior with battle axe, shield, long hair, heavy armor, battle ready, strong facial expression." Possiamo vedere che sta funzionando. E facciamo un vero esempio. Andiamo su `Flux Pro Art`, settiamo il prompt e vediamo adesso qual è l'immagine di questo vichingo che ci ha consigliato il nostro modello LLM. Ed eccolo qua, bellissimo, veramente, veramente bello. Quindi, hai visto, da prima che ci creava una storia, adesso ci descrive come deve essere l'immagine di questo vichingo, perché è stato addestrato a realizzare delle descrizioni da dare poi in pasto alle AI che generano le immagini.
Facciamo un altro esempio. "Flux prompt: Battle mega robot in a futuristic city." Voglio fare questo robot qua. Vediamo cosa ci genera. "Large battle mega robot, mid-dark black blue, blue red environment, futuristic city building, and how much stuff it puts, cinematic 4K, TR h600." Ecco, ci ha messo pure tutti questi parametri qua. Vediamo un po' cosa ci esce. Ha fatto una bella descrizione. "Epic cinematic 4K." Eccolo qua, bellissimo, stupendo.
Facciamo un altro esempio. "Flux prompt of a girl with One Eye Open." Dovrebbe essere con la fotografia di una ragazza. "Girl with One Eye Open, photorealistic, detailed, AR 169." Ecco, vedi che ha imparato addirittura a mettere pure i tag di tutti quei parametri. "Getaway." Come ha detto. "Content warning: Flux." Vediamo un po' cosa ci genera. Ecco qua, bellissima pure questa. Cioè, immagini da paura ci sta generando `Flux`. Adesso questo modello ci sta piacendo come sta performando.
Lo vogliamo esportare in `gguf` per `Ollama`. Quindi, per prima cosa, lo possiamo esportare in vari modi. Lo possiamo esportare con tutti i pesi del modello in `float16`, che sarebbe quella la soluzione migliore. Possiamo quantizzare a 4 bit o ad 8 bit. Quando si quantizza 70 miliardi, 13 miliardi, 100 miliardi, 400 miliardi di parametri, conviene quantizzare. Altrimenti, come questo qui, da 3 miliardi, lo esporto così com'è. Quindi, eseguiamo questa parte di codice e dopo ti mostro come includerlo su `Ollama` ed eseguirlo in locale sul tuo PC.
Mentre qui si sta esportando il modello nel formato `gguf`, voglio spendere due parole sull'addestramento che abbiamo fatto. Allora, l'addestramento è stato fatto senza splittare il dataset in training, validation e test, poiché questo è solamente un video divulgativo, semplice, non volevo complicare ancora di più le cose. E anche perché il dataset utilizzato era veramente, veramente piccolo. Però, la cosa essenziale sarebbe stata quella di suddividere il dataset in tre parti: una per il training, una per il validation e una per il test. E tutti questi parametri qui, diciamo, andava fatta un'esplorazione dei migliori iperparametri per l'addestramento, cosa che non ho fatto, ancora una volta, perché è solamente un video divulgativo, dimostrativo di come è il processo per addestrare un modello.
Una volta terminato, ci troveremo un messaggio del genere, tutto printato, ma soprattutto avremo questa cartella che si chiama `model`, con all'interno il `model.safetensors`, `config`, tutti i pesi, tutte le cose di cui abbiamo bisogno per esportarlo. Adesso io lo voglio salvare in locale sul mio PC, quindi creo un blocco di codice, continuo, gli dico: "Creami il file zip per la cartella `/content/model`". Gli faccio girare un piccolo script per zippare la cartella `model`, così la posso scaricare facilmente. Qua sto utilizzando l'AI generativa di Google Colab. Beh, niente di più, niente di meno. Siccome ci stava mettendo tanto a zippare la cartella, l'ho stoppato, l'ho annullato e ho collegato al mio Google Drive direttamente. Vedi qua ci sta la cartella `Drive`, e adesso faccio un comando per spostare questi file dalla l'ambiente locale alla mia cartella Drive. Poi entrerò su Drive e me lo scarico.
Mo adesso, così in questo modo, sto facendo `move` della cartella `model` in locale all'interno di `MyDrive`. Allora, siamo riusciti a scaricare il tutto dentro la cartella `model`. Qua abbiamo tutti i vari pesi e parametri, soprattutto il più importante è il `model.safetensors` e il file del modello convertito in `F16.gguf`. Adesso andiamo su `Ollama`. Sta qui aperto. Basta aprire il terminale e facendo il comando `ollama list` o `ollama list`, potete vedere i modelli che avete già scaricato. E adesso quello che facciamo sarà `ollama create nome_del_modello RizzoModel_v3` e ci mettiamo il percorso alla cartella, alla cartella `model`, ma in particolare al file `model.safetensors`. Quindi, facciamo proprietà, dettagli, copiamo qua. Anzi, da qua copiamo qua tutto il percorso, mettiamo `/model.safetensors`. Il `model.safetensors`, se noi l'andiamo ad aprire, è un file di descrizione del modello e deve puntare il `from` al file `.gguf`. Quindi, se ci sta un altro percorso, cambiatelo. Il file `.gguf`. E adesso cliccando invio, trasferirà tutti i pesi del modello nel formato di `Ollama`.
`Ollama` è un runner, cioè è un software che esegue i modelli in locale. Adesso stiamo in locale sulla mia macchina, quindi bisogna attendere un attimo che trasferisce tutti i pesi del modello. Ok, ha fatto. Adesso, se facciamo `ollama list`, abbiamo `RizzoModel_v3`. E facendo `ollama run RizzoModel_v3`, possiamo eseguire in locale sulla mia macchina il Large Language Model che abbiamo fine-tuned, che abbiamo addestrato. Quindi, scriviamo: "Flux prompt: cyberpunk car in cyberpunk city". Vediamo cosa ci genera. "Cyberpunk 2077 car, futuristic, high quality, spectacular reflection, octa render, hard shadow, cinematic 8k, -ar 16:9". Ed ecco qui che lo stiamo eseguendo in locale.
Poi, se vogliamo utilizzare l'interfaccia grafica, apriamo `AnythingLLM`, che è semplicemente, diciamo, la chat, la parte frontend, che si collega poi ad `Ollama`. Quindi, andiamo su `Settings`, `LLM`, settiamo qui `Ollama`. Ecco qui, già ci trovi i modelli `RizzoModel_v3`. `Save changes`. Ecco qua, l'abbiamo salvato. Quindi, adesso, ritornando su `Back to workspace`, apriamo un'altra area. `Model`. Salva. Qui gli possiamo scrivere: "Flux prompt of a Viking girl with, non lo so, a dress, a con un'ascia". E adesso in locale eseguirà il nostro modello, il `RizzoModel_v3`, ci genererà il risultato. Quindi, stiamo utilizzando il modello in locale su `AnythingLLM` utilizzando `Ollama`. Eccolo qua. "Viking warrior girl, 9th century, underpainting, Unreal Engine, cinematic lighting, highly detailed environment design, concept art." Ok.
Copiamo il tutto. Adesso andiamo su `Flux Pro Art`, anzi, ce l'ho già qua aperto. `Generate`. E andiamo a vedere il risultato che abbiamo generato in locale sul nostro PC, con il nostro hardware, con il modello nostro addestrato sui nostri dati. Ed ecco qui il risultato. Che dire? Spettacolare. Che dire? Allora, io più di così non potevo fare. Ti ho mostrato tutto il codice. Questo codice lo metterò nella descrizione del video, così come anche il video di YouTube per installare `Ollama` e il video di YouTube per installare `AnythingLLM`, così hai tutte le guide, tutti i tutorial e tutto il materiale per poter ricreare questo esperimento. L'unica cosa che ti chiedo è, se il video ti è piaciuto, lascia un bellissimo like e iscriviti al canale per rimanere aggiornato sul mondo delle AI. Ciao.