Introduzione: Riutilizzare la Conoscenza dei Modelli
Il Transfer Learning avanzato e la chiave per sfruttare modelli pre-addestrati su enormi dataset senza necessitare delle risorse computazionali per il training da zero. Modelli come BERT e GPT sono stati addestrati su centinaia di miliardi di token, catturando una comprensione profonda del linguaggio che può essere trasferita a task specifici con pochi dati e risorse limitate.
In questo articolo esploreremo le strategie di fine-tuning moderne, il prompt engineering, il Retrieval-Augmented Generation (RAG) e l'ecosistema Hugging Face, con confronto tra modelli open-source come Llama, Mistral e Falcon.
Cosa Imparerai
- BERT: pre-training bidirezionale e fine-tuning per task di comprensione
- GPT: generazione auto-regressiva e in-context learning
- Strategie di fine-tuning: full, LoRA, adapters e QLoRA
- Prompt engineering: tecniche per ottenere output migliori
- RAG: combinare LLM con ricerca per risposte accurate
- Modelli open-source: Llama, Mistral, Falcon - quando usare quale
- Hugging Face Hub: l'ecosistema di modelli pre-addestrati
BERT: Comprensione Bidirezionale del Testo
BERT (Bidirectional Encoder Representations from Transformers) ha rivoluzionato il NLP dimostrando che il pre-training bidirezionale produce rappresentazioni del linguaggio straordinariamente ricche. Durante il pre-training, BERT usa due obiettivi:
- Masked Language Modeling (MLM): il 15% dei token viene mascherato e il modello deve predirli dal contesto bidirezionale
- Next Sentence Prediction (NSP): il modello predice se due frasi sono consecutive nel testo originale
Per il fine-tuning, basta aggiungere un layer di classificazione sopra l'output di BERT e addestrare il tutto su pochi migliaia di esempi etichettati:
from transformers import BertTokenizer, BertForSequenceClassification
from transformers import Trainer, TrainingArguments
import torch
# Caricare BERT per classificazione di sentiment
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertForSequenceClassification.from_pretrained(
'bert-base-uncased',
num_labels=2 # positivo/negativo
)
# Tokenizzare i dati
texts = ["This movie is great!", "Terrible waste of time."]
labels = [1, 0] # 1=positivo, 0=negativo
inputs = tokenizer(texts, padding=True, truncation=True,
max_length=128, return_tensors="pt")
inputs['labels'] = torch.tensor(labels)
# Forward pass
outputs = model(**inputs)
print(f"Loss: {outputs.loss:.4f}")
print(f"Logits: {outputs.logits}")
# Fine-tuning con Trainer API
training_args = TrainingArguments(
output_dir='./results',
num_train_epochs=3,
per_device_train_batch_size=16,
learning_rate=2e-5,
weight_decay=0.01,
warmup_steps=100,
evaluation_strategy="epoch"
)
Fine-Tuning Efficiente: LoRA e QLoRA
Il fine-tuning completo di modelli con miliardi di parametri richiede enormi risorse. Parameter-Efficient Fine-Tuning (PEFT) permette di adattare il modello modificando solo una piccola frazione dei parametri:
LoRA (Low-Rank Adaptation)
LoRA congela i pesi originali del modello e aggiunge matrici di basso rango addestrabili accanto ai layer di attention. Tipicamente modifica meno dell'1% dei parametri totali, raggiungendo performance comparabili al full fine-tuning.
QLoRA
QLoRA combina LoRA con quantizzazione a 4-bit, permettendo il fine-tuning di modelli da 65B parametri su una singola GPU con 48GB di VRAM. Usa il tipo di dato NF4 (NormalFloat 4-bit) e double quantization per massimizzare l'efficienza.
from peft import LoraConfig, get_peft_model, TaskType
from transformers import AutoModelForCausalLM, AutoTokenizer
# Configurazione LoRA
lora_config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
r=16, # Rango delle matrici LoRA
lora_alpha=32, # Scaling factor
lora_dropout=0.1,
target_modules=["q_proj", "v_proj", "k_proj", "o_proj"],
bias="none"
)
# Applicare LoRA al modello
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b-hf")
peft_model = get_peft_model(model, lora_config)
# Contare parametri addestrabili
trainable = sum(p.numel() for p in peft_model.parameters() if p.requires_grad)
total = sum(p.numel() for p in peft_model.parameters())
print(f"Trainable: {trainable:,} / {total:,} "
f"({100*trainable/total:.2f}%)")
# Output: ~0.5% dei parametri sono addestrabili
Prompt Engineering: L'Arte di Comunicare con i LLM
Il prompt engineering e la pratica di formulare istruzioni che guidano il modello verso l'output desiderato senza modificarne i pesi. Tecniche chiave includono: few-shot learning (fornire esempi nel prompt), chain-of-thought (chiedere al modello di ragionare passo per passo), role prompting (assegnare un ruolo specifico al modello) e structured output (richiedere formati specifici come JSON).
RAG: Retrieval-Augmented Generation
Il RAG combina la capacità generativa dei LLM con un sistema di ricerca per fornire risposte accurate basate su documenti specifici. Invece di affidarsi solo alla conoscenza memorizzata durante il pre-training, il modello riceve contesto rilevante recuperato da un database di documenti.
Il processo RAG si articola in tre fasi:
- Indexing: i documenti vengono suddivisi in chunk e trasformati in embedding vettoriali
- Retrieval: data una query, i chunk più simili vengono recuperati tramite similarity search
- Generation: i chunk recuperati vengono inseriti nel prompt come contesto per il LLM
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import FAISS
# 1. Splitting dei documenti
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
separators=["\n\n", "\n", ". ", " "]
)
chunks = text_splitter.split_text(document_text)
# 2. Creazione degli embedding e vector store
embeddings = HuggingFaceEmbeddings(
model_name="sentence-transformers/all-MiniLM-L6-v2"
)
vector_store = FAISS.from_texts(chunks, embeddings)
# 3. Retrieval e generazione
query = "Come funziona il transfer learning?"
relevant_docs = vector_store.similarity_search(query, k=3)
# Costruire il prompt con contesto
context = "\n".join([doc.page_content for doc in relevant_docs])
prompt = f"""Basandoti sul seguente contesto, rispondi alla domanda.
Contesto:
{context}
Domanda: {query}
Risposta:"""
Modelli Open-Source: Llama, Mistral, Falcon
L'ecosistema dei modelli open-source e esploso, offrendo alternative competitive ai modelli proprietari:
- Llama (Meta): famiglia di modelli da 7B a 70B parametri, eccellente per fine-tuning e deployment on-premise. Llama 3 raggiunge performance competitive con GPT-3.5
- Mistral: modelli efficienti con architettura innovative (Sliding Window Attention, Mixture of Experts). Mistral 7B supera Llama 2 13B su molti benchmark
- Falcon: addestrato su dataset di alta qualità (RefinedWeb), offre buone performance zero-shot
La scelta dipende dal caso d'uso: per generazione di testo generico, Llama 3 e spesso la scelta migliore; per efficienza su risorse limitate, Mistral 7B e ideale; per task specifici, il fine-tuning con LoRA su qualsiasi di questi modelli produce risultati eccellenti.
Hugging Face: L'Ecosistema Completo
Hugging Face e diventato il punto di riferimento per il deep learning NLP, offrendo un ecosistema completo:
- Model Hub: oltre 500.000 modelli pre-addestrati, scaricabili con una riga di codice
- Transformers Library: API unificate per tutti i modelli (BERT, GPT, T5, Llama, etc.)
- Datasets: migliaia di dataset per training e evaluation
- Trainer API: training loop ottimizzato con distributed training, mixed precision, gradient accumulation
- Spaces: hosting gratuito per demo e app ML
Prossimi Passi nella Serie
- Nel prossimo articolo esploreremo TinyML e Edge AI
- Vedremo come portare modelli deep learning su dispositivi embedded e smartphone
- Analizzeremo quantization, pruning e knowledge distillation per compressione dei modelli







