Guida DPO

Guida DPO

Guida DPO (Direct Preference Optimization)

Negli ultimi due anni la DPO (Direct Preference Optimization) si è affermata come una delle tecniche più usate per allineare i Large Language Model (LLM) alle preferenze umane, sostituendo in molti casi la più complessa pipeline RLHF (Reinforcement Learning from Human Feedback). In questa guida vediamo cos’è, come funziona matematicamente, come si struttura un dataset e come si implementa in pratica con la libreria Hugging Face TRL.

Indice dei contenuti – Guida DPO

1. Cos’è la DPO: definizione

2. Come funziona: dalla RLHF alla loss diretta

3. La funzione di perdita della DPO: i vantaggi matematici

4. Principali casi d’uso

5. Come strutturare un dataset per la DPO

6. La DPO come problema di classificazione binaria

7. Esempio pratico con Hugging Face TRL

8. Best practice: pro e contro

9. Conclusioni

1. Cos’è la DPO: definizione

La Direct Preference Optimization (DPO) è una tecnica di fine-tuning per modelli linguistici che allinea le risposte di un LLM alle preferenze umane ottimizzando direttamente una funzione di perdita basata su coppie di risposte (una preferita, una scartata), senza passare attraverso un modello di reward separato né attraverso algoritmi di reinforcement learning come PPO.

È stata introdotta nel paper “Direct Preference Optimization: Your Language Model is Secretly a Reward Model” (Rafailov et al., 2023), la cui intuizione centrale è che la stessa politica (il modello linguistico) può essere riparametrizzata per rappresentare implicitamente sia la politica sia il reward model, evitando così una fase di reinforcement learning separata.

In termini semplici: invece di addestrare un reward model e poi usare RL per massimizzarlo (come nella RLHF classica), la DPO trasforma il problema di allineamento in un problema di classificazione supervisionata su coppie di risposte.

2. Come funziona: dalla RLHF alla loss diretta

La pipeline RLHF tradizionale prevede tre fasi: supervised fine-tuning (SFT), addestramento di un reward model sui dati di preferenza, e infine ottimizzazione della policy con un algoritmo RL (tipicamente PPO) — potente ma instabile, costoso e complesso da tunare.

La DPO comprime le ultime due fasi in una sola: a partire da un modello SFT già addestrato, ottimizza direttamente sui dati di preferenza (prompt, risposta preferita, risposta scartata) con una loss derivata analiticamente dall’obiettivo RLHF, senza campionare risposte durante il training né usare un reward model esplicito.

3. La funzione di perdita della DPO: i vantaggi matematici

La loss della DPO si basa sul modello Bradley-Terry per le preferenze e si scrive come:

L_DPO(θ) = -E[(x, y_w, y_l)] [    log σ( β · ( log πθ(y_w|x) – log π_ref(y_w|x) )         – β · ( log πθ(y_l|x) – log π_ref(y_l|x) ) )]

dove:

  • π_θ è il modello in addestramento (policy)
  • π_ref è il modello di riferimento (di solito il modello SFT, con i pesi congelati)
  • y_w è la risposta preferita (“winner”), y_l è la risposta scartata (“loser”)
  • β è un parametro di temperatura che controlla quanto ci si allontana dal modello di riferimento
  • σ è la funzione sigmoide

Il vantaggio matematico è che questa loss è derivata in forma chiusa dall’obiettivo di RL vincolato con penalità KL, sostituendo il reward implicito r(x,y) con la differenza di log-probabilità tra policy e modello di riferimento. Questo elimina reward model separato, sampling online delle risposte (richiesto da PPO) e gradienti ad alta varianza tipici del RL, con un training più stabile e leggero, pur convergendo verso lo stesso ottimo teorico della RLHF.

Un piccolo esempio Python che implementa la loss “from scratch” con PyTorch, utile per capire la meccanica prima di affidarsi a TRL:

import torchimport torch.nn.functional as F def dpo_loss(policy_chosen_logps, policy_rejected_logps,             ref_chosen_logps, ref_rejected_logps, beta=0.1):    """Calcola la loss DPO per un batch di coppie preferenza."""    pi_logratios = policy_chosen_logps - policy_rejected_logps    ref_logratios = ref_chosen_logps - ref_rejected_logps    logits = beta * (pi_logratios - ref_logratios)    loss = -F.logsigmoid(logits).mean()    return loss

4. Principali casi d’uso

La DPO viene oggi impiegata soprattutto per:

  • Allineamento a valori e stile: rendere le risposte di un chatbot più utili, oneste e sicure (è stata usata, ad esempio, per il post-training di Llama 3).
  • Riduzione delle allucinazioni: nei modelli multimodali (vision-language model), la DPO su coppie di risposte con e senza allucinazioni migliora la fedeltà alle immagini di input.
  • Personalizzazione del tono aziendale: adattare un modello generalista al linguaggio, al tono e alle policy di un’azienda o di un prodotto specifico.
  • Miglioramento di modelli di codice: preferire soluzioni più efficienti, leggibili o sicure rispetto ad alternative funzionanti ma peggiori.
  • Sicurezza e moderazione: penalizzare risposte dannose o non conformi rispetto ad alternative sicure, senza costruire un reward model dedicato.

5. Come strutturare un dataset per la DPO

Un dataset per la DPO richiede, per ogni esempio, tre elementi minimi: il prompt, la risposta preferita (chosen) e la risposta scartata (rejected). Il formato standard supportato da TRL è il seguente:

from datasets import Dataset data = {    "prompt": [        "Spiega cos'è una API REST in una frase.",        "Scrivi una funzione Python per invertire una stringa."    ],    "chosen": [        "Una API REST è un'interfaccia che espone risorse via HTTP usando verbi come GET, POST, PUT, DELETE.",        "def inverti(s):\n    return s[::-1]"    ],    "rejected": [        "È una cosa che i programmatori usano per far comunicare i programmi.",        "def inverti(s):\n    r = ''\n    for c in s:\n        r = c + r\n    return r"    ],} dataset = Dataset.from_dict(data)

Alcune raccomandazioni pratiche, confermate anche dalla documentazione ufficiale di Hugging Face:

  • privilegiare prompt espliciti separati dalle risposte, invece di dataset con “prompt implicito” ricavato dal solo confronto tra chosen e rejected;
  • garantire coerenza e qualità delle annotazioni: linee guida chiare per chi etichetta le coppie riducono il rumore;
  • filtrare dataset di grandi dimensioni (es. UltraFeedback, Anthropic HH-RLHF) mantenendo solo gli esempi rilevanti per il proprio caso d’uso;
  • monitorare la convergenza della loss e validare su un held-out set durante il training.

6. La DPO come problema di classificazione binaria

Un aspetto centrale, spesso sottovalutato, è che la DPO trasforma l’allineamento in un problema di classificazione binaria: per ogni coppia (chosen, rejected) il modello deve imparare ad assegnare una probabilità più alta alla risposta preferita rispetto a quella scartata, esattamente come un classificatore che deve distinguere la classe “positiva” dalla classe “negativa”.

La sigmoide nella formula della loss gioca lo stesso ruolo che ha in una regressione logistica: trasforma una differenza di punteggi (log-probabilità pesate da β) in una probabilità compresa tra 0 e 1, che rappresenta quanto il modello è “sicuro” che y_w sia preferibile a y_l. Minimizzare la binary cross-entropy su queste coppie equivale ad addestrare un classificatore implicito delle preferenze, con la differenza che l’output non è una singola etichetta ma l’intero modello linguistico generativo.

Questa lettura è utile per uno sviluppatore con esperienza di machine learning classico: gli stessi concetti di accuracy sulle coppie di validazione, margine di decisione e overfitting su classi sbilanciate si applicano anche qui, e possono essere monitorati con metriche standard durante il training.

7. Esempio pratico con Hugging Face TRL

La libreria TRL (Transformers Reinforcement Learning) di Hugging Face fornisce DPOTrainer, che gestisce automaticamente tokenizzazione, calcolo delle log-probabilità di riferimento e ottimizzazione:

from trl import DPOConfig, DPOTrainerfrom transformers import AutoModelForCausalLM, AutoTokenizerfrom datasets import load_dataset # Modello e tokenizer (partire sempre da un modello già SFT)model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-0.5B-Instruct")tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-0.5B-Instruct") # Dataset di preferenza in formato chosen/rejecteddataset = load_dataset("trl-lib/ultrafeedback_binarized", split="train[:2000]") training_args = DPOConfig(    beta=0.1,                       # temperatura: quanto ci si allontana dal ref model    learning_rate=5e-7,             # LR basso per stabilità    max_prompt_length=512,    max_length=1024,    per_device_train_batch_size=4,    gradient_accumulation_steps=4,    num_train_epochs=1,    logging_steps=10,) trainer = DPOTrainer(    model=model,    args=training_args,    train_dataset=dataset,    processing_class=tokenizer,) trainer.train()

Pochi parametri richiedono attenzione particolare: beta (tipicamente tra 0.1 e 0.5) regola quanto il modello può discostarsi dal comportamento originale, mentre un learning rate troppo alto può portare a un collasso rapido della qualità delle risposte.

8. Best practice: pro e contro

Pro:

  • Training più semplice e stabile rispetto a PPO/RLHF, senza reward model separato.
  • Minori requisiti computazionali: niente sampling online né rollout.
  • Ottima integrazione con l’ecosistema Hugging Face (Transformers, PEFT, LoRA), utile per fine-tuning anche su GPU consumer con tecniche di quantizzazione.
  • Risultati competitivi, tanto da essere stata adottata nel post-training di modelli open come Llama 3.

Contro:

  • La qualità finale dipende in modo critico dalla qualità del dataset di preferenza: annotazioni rumorose degradano rapidamente il modello.
  • Richiede comunque un modello SFT di partenza ben addestrato: la DPO non sostituisce il fine-tuning supervisionato iniziale.
  • Meno flessibile della RLHF pura nel gestire segnali di reward complessi o multi-obiettivo (per questi scenari si guarda oggi anche a varianti come KTO o GRPO).
  • Rischio di overfitting sulle coppie di training se il dataset è piccolo o poco diversificato.

9. Conclusioni

La DPO ha reso l’allineamento dei modelli linguistici accessibile anche a team che non dispongono delle risorse computazionali dei grandi laboratori di ricerca, semplificando un processo che fino a poco tempo fa richiedeva pipeline RLHF complesse e costose. Ma, come per ogni tecnica nel campo dell’AI generativa, il panorama è in continua e rapidissima evoluzione: nuove varianti (KTO, ORPO, SimPO) e nuove librerie emergono a un ritmo che rende difficile restare aggiornati senza un percorso di formazione strutturato.

Per questo motivo, l’unico modo concreto per mantenere il proprio team di sviluppo allineato allo stato dell’arte è investire in formazione continua, sia sulle tecniche di fine-tuning e allineamento dei modelli, sia più in generale sull’AI generativa applicata allo sviluppo software.

Va inoltre ricordato che, per le aziende aderenti, esiste la possibilità di accedere a Fondimpresa per finanziare la formazione dei propri dipendenti attraverso il Conto Formazione o gli avvisi a catalogo attivi, riducendo o azzerando i costi diretti dei percorsi formativi.

Se vuoi valutare un percorso formativo su DPO, fine-tuning di LLM e AI generativa per il tuo team di sviluppo, contattaci per una consulenza e un preventivo personalizzato:

📧 info@innovaformazione.net

📞 347 1012275 — Dario Carrassi

Per altri articoli tecnici e di settore consigliamo di navigare sul nostro blog QUI.

(fonte) (fonte) (fonte)

Ti potrebbe interessare

Articoli correlati