Vai al contenuto

Fine-tuning di un LLM sulle tue annotazioni

Addestra un LLM con SFT o DPO, un modello di ricompensa o un modello di ricompensa di processo sulle etichette del tuo studio, valuta ciascuno su item esclusi dall'addestramento e pubblica su Hub.

Novità nella v2.14.0

Questo tutorial addestra quattro tipi di modello linguistico sulle risposte che raccoglie uno studio Potato. Il trainer llm-sft esegue il fine-tuning di un LLM su risposte scritte o su tracce di agent corrette, llm-dpo adatta un LLM a scelte a coppie, reward-model addestra un modello di ricompensa scalare sui confronti e prm-reward addestra un modello di ricompensa di processo sulle valutazioni dei singoli passi. La pagina è scritta per i gruppi che raccolgono già in Potato dati di preferenza o di tracce e vogliono un primo modello da quelle etichette senza esportare i dati in uno script di addestramento separato. Ogni sezione indica lo schema che produce i dati, riporta una configurazione che supera il validatore di Potato, elenca i parametri che vale la pena cambiare e spiega che cosa misura il punteggio held-out e se le predizioni tornano agli annotatori come pre-etichette. La panoramica sull'addestramento di modelli tratta la pagina Training, e Addestra il tuo primo modello percorre l'intero ciclo con un classificatore che non richiede GPU.

Trainer per schema di annotazione

Ogni trainer di questa pagina impara da un solo schema di annotazione per esecuzione, e la pagina Training propone un trainer solo per gli schemi da cui il trainer può imparare. La tabella seguente elenca gli schemi da cui impara ciascun trainer:

TrainerSchemaChe cosa impara il modelloMetrica principale held-outMostrato agli annotatori
llm-sfttexta scrivere la risposta scritta dagli annotatoriF1 sui tokensì, la risposta generata riempie la casella di testo
llm-sfttrajectory_edita riscrivere i passi di un agent nei passi corretticloser_to_correctionno, solo archiviata e valutata
llm-dpopairwise in modalità binarya preferire la risposta scelta dagli annotatoriaccuratezza, pareggi esclusisì, viene selezionata la scheda della risposta preferita
llm-dpotrajectory_edita preferire i passi corretti a quelli originalicloser_to_correctionno, solo archiviata e valutata
reward-modelpairwise (binary e scale), ranking, bwsun punteggio per ogni rispostaaccuratezza, accuratezza di direzione, tau di Kendall o accuratezza best-worstsì, come risposta dello schema stesso
prm-rewardprocess_rewarduna valutazione corretto, neutro o errato per ogni passostep_macro_f1proposta tramite il pulsante "AI pre-label steps"

Prima di iniziare

Gli extra di addestramento di Potato installano PyTorch, Transformers e PEFT per i quattro trainer:

bash
pip install "potato-annotation[train-llm]"
pip install "potato-annotation[train-text]"

Il primo extra copre llm-sft e llm-dpo, il secondo copre reward-model e prm-reward. pip install "potato-annotation[train]" installa tutti gli extra di addestramento in una volta. Un trainer di cui mancano i pacchetti compare nella pagina Training come "not installed", con il comando che installa i pacchetti.

Ogni trainer rifiuta di eseguire l'addestramento con meno di 20 item nella partizione train (il parametro min_instances). La suddivisione predefinita assegna il 70% degli item etichettati a train, il 15% a val e il 15% a test, quindi conta su circa 30 item etichettati prima della prima esecuzione. Gli item val e test sono esclusi dall'addestramento e valutano il modello.

llm-sft e llm-dpo partono da Qwen/Qwen2.5-0.5B-Instruct, reward-model da distilbert/distilroberta-base e prm-reward da answerdotai/ModernBERT-base. I trainer per LLM sono pensati per girare su una GPU. Quando la macchina che esegue Potato non ha una GPU, Addestramento remoto invia le esecuzioni a un server GPU via ssh o a un cluster SLURM.

Uno studio che raccoglie preferenze e riscritture

La prima configurazione pone due domande su ogni richiesta di assistenza clienti. Gli annotatori scelgono la migliore tra due risposte e scrivono la risposta che un annotatore vorrebbe ricevere dall'assistente. La scelta addestra llm-dpo e reward-model, e la risposta scritta addestra llm-sft. Ogni riga di data/answers.jsonl contiene una richiesta e le due risposte:

json
{"id": "q001", "prompt": "How do I reset my router to factory settings?", "responses": ["Hold the reset button on the back for 10 seconds until the lights blink, then wait two minutes for the router to restart.", "Routers can be reset. Check the manual."]}

La configurazione mostra la richiesta come testo dell'item e le due risposte come schede A e B:

yaml
annotation_task_name: "Support answers: preferences and rewrites"
task_dir: .
data_files:
  - data/answers.jsonl
item_properties:
  id_key: id
  text_key: prompt
output_annotation_dir: annotation_output
user_config:
  allow_all_users: true
 
annotation_schemes:
  - annotation_type: pairwise
    name: preferred
    description: "Which response answers the question better?"
    mode: binary
    items_key: responses
    allow_tie: true
    tie_label: "No preference"
 
  - annotation_type: text
    name: rewrite
    description: "Write the answer you would want the assistant to give."
    multiline: true
    rows: 4
 
model_training:
  enabled: true
  writeback:
    schemas: [preferred]
    min_confidence: 0.7

text_key: prompt conta due volte, perché la richiesta è ciò che leggono gli annotatori e ciò che llm-sft mette dopo la domanda. items_key: responses indica l'elenco da cui nascono le schede pairwise, nell'ordine dei dati, quindi A è la prima risposta. writeback.schemas elenca solo preferred, perciò gli annotatori vedono la scelta del modello e non una riscrittura generata. Le pre-etichette ancorano gli annotatori, quindi aggiungi rewrite all'elenco solo quando il punteggio held-out dell'esecuzione llm-sft giustifica la visualizzazione del testo del modello. min_confidence: 0.7 tiene fuori dalla pagina le predizioni di cui il modello è meno sicuro, pur archiviandole. Controlla la configurazione con il validatore di Potato prima di avviare il server:

bash
python -m potato.validate_cli config.yaml --strict

Avvia Potato, etichetta almeno 30 richieste e apri /admin/training come amministratore. Le esecuzioni seguenti possono partire anche dall'API, con la admin_api_key della configurazione in $KEY.

Fine-tuning supervisionato con llm-sft

llm-sft trasforma ogni item etichettato in un esempio di addestramento. La richiesta è la description dello schema (la domanda che gli annotatori hanno visto) seguita dal testo dell'item, e il bersaglio è la risposta scritta dall'annotatore. La perdita copre solo i token della risposta, così il modello impara a rispondere e non a ripetere la richiesta. Per impostazione predefinita si addestra un adattatore LoRA invece di tutti i pesi.

Scegli llm-sft e lo schema rewrite nella pagina Training e fai clic su Train now, oppure avvia la stessa esecuzione dall'API:

bash
curl -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
  http://localhost:8000/admin/training/api/runs \
  -d '{"trainer": "llm-sft", "schemas": ["rewrite"], "params": {"max_new_tokens": 256}}'

La maggior parte delle esecuzioni cambia solo i parametri di questa tabella:

ParametroPredefinitoSignificato
model_nameQwen/Qwen2.5-0.5B-InstructUn modello linguistico causale su Hugging Face Hub, oppure una directory locale.
loratruefalse addestra tutti i pesi e richiede una memoria parecchie volte maggiore. {quantize: 4bit} carica il modello di base a 4 bit (vedi QLoRA).
max_length1024Token per esempio. Una richiesta lunga perde token dall'inizio, e la risposta viene conservata.
max_new_tokens256La risposta più lunga che il modello genera.
instructionla description dello schemaTesto messo prima del testo di ogni item.
system_promptnessunoUn messaggio di sistema per i modelli con un template di chat.
gradient_checkpointingfalseRisparmia memoria delle attivazioni al costo di circa un terzo di tempo in più.
learning_rate2e-4 con LoRA, 1e-5 senza

L'esecuzione conserva il checkpoint con la perdita val più bassa. Poiché l'esecuzione ha scelto il checkpoint su val, il punteggio held-out si legge su test. Il punteggio è l'F1 sui token, la sovrapposizione di parole tra la risposta generata e quella scritta, indipendentemente dall'ordine, con la corrispondenza esatta riportata accanto al punteggio. L'F1 sui token premia una risposta che usa le parole giuste nei posti sbagliati, quindi leggi insieme le due metriche.

Il modello genera una risposta in modo greedy per ogni item val e test. Con writeback.include_unlabeled: true, il modello risponde anche agli item che nessuno ha etichettato. La confidenza di una predizione è la probabilità media che il modello assegna ai token generati, e la coda di revisione del modello ordina per questa confidenza. Con rewrite sotto writeback.schemas, la risposta generata riempie la casella di testo quando un annotatore apre l'item.

Il testo libero si adatta male a più annotatori per item. L'impostazione aggregation riduce più risposte a una sola confrontandole parola per parola, e due risposte scritte raramente coincidono parola per parola. Per llm-sft su uno schema text, un solo autore per item evita il confronto parola per parola.

Ottimizzazione delle preferenze con llm-dpo

La Direct Preference Optimization (DPO) addestra un LLM ad aumentare la verosimiglianza della risposta preferita rispetto all'altra, misurata rispetto a una copia di riferimento congelata del modello di partenza. llm-dpo costruisce un esempio da ogni item con risposta A o B. La richiesta è il campo prompt dell'item, oppure il primo tra question, instruction, query, input e context che l'item possiede. Gli item con risposta pareggio sono esclusi, perché un pareggio non indica nessuna delle due risposte come migliore.

Scegli llm-dpo e lo schema preferred, oppure invia la stessa richiesta di prima con "trainer": "llm-dpo" e "schemas": ["preferred"]. Con l'adattatore LoRA predefinito, il modello di riferimento è lo stesso modello con l'adattatore disattivato, quindi in memoria non c'è una seconda copia. lora: false addestra tutti i pesi e tiene una copia congelata come riferimento, e la copia congelata raddoppia la memoria.

ParametroPredefinitoSignificato
model_nameQwen/Qwen2.5-0.5B-InstructUn modello linguistico causale.
beta0.1Quanto il modello può allontanarsi dal riferimento. Un valore più basso gli lascia spostarsi di più.
epochs2
learning_rate5e-5 con LoRA, 5e-7 senza
max_length1024Token per richiesta e risposta.
instructionnessunoTesto messo prima della richiesta di ogni item.

L'esecuzione conserva il checkpoint con la perdita DPO più bassa su val, e il log dell'esecuzione riporta quanto spesso il modello preferisce su val la scelta degli annotatori. Il punteggio held-out è l'accuratezza su test, sugli item la cui risposta non è un pareggio, con l'F1 macro accanto all'accuratezza. Una predizione è la risposta che il modello addestrato preferisce, con la probabilità di quella preferenza come confidenza. Come pre-etichetta, la scheda della risposta preferita risulta selezionata quando l'annotatore apre l'item. Con randomize_order nello schema, la selezione segue la risposta ovunque l'annotatore la veda. Anche l'adattatore salvato è una policy e genera testo se caricato con PEFT. Per la raccolta stessa dei dati di preferenza, vedi Raccolta di dati RLHF e di preferenza e Confronto a coppie.

Un modello di ricompensa con reward-model

Un modello di ricompensa associa un numero a una risposta, per l'RLHF, per il campionamento best-of-n o per ordinare nuovi output. reward-model addestra un transformer con una sola uscita, in modo che la risposta preferita dagli annotatori ottenga un punteggio più alto. La perdita è quella di Bradley–Terry, -log sigmoid(r_winner - r_loser), la perdita per modelli di ricompensa di InstructGPT. Lo schema preferred visto sopra addestra reward-model senza modificare la configurazione. Ogni schema comparativo fornisce un segnale di addestramento diverso:

SchemaSegnale di addestramentoMetrica principale held-out
pairwise, binaryLa risposta scelta batte l'altra. I pareggi sono saltati.accuratezza
pairwise, scaleVince il lato verso cui pende il cursore, con un peso dato da quanto pende. Il punto medio è saltato.accuratezza di direzione
rankingL'intero ordine, con la perdita di Plackett-Luce, oppure ogni coppia dell'ordine con ranking_loss: pairs.tau di Kendall
bwsL'elemento migliore batte ogni altro elemento della tupla, e ogni altro elemento batte il peggiore.accuratezza best-worst

Quando un item ha un campo di richiesta, ogni ricompensa è quella della risposta data la richiesta. Le opzioni di un ranking sono le etichette dello schema, ciascuna valutata dato il testo dell'item. Il server costruisce le tuple BWS da bws_config, quindi un'esecuzione dai dati del server stesso ha le tuple, e i file di dati non devono contenerle.

ParametroPredefinitoSignificato
model_namedistilbert/distilroberta-baseUn encoder, oppure un LLM con lora.
ranking_lossplackett_luceSolo per gli schemi ranking: plackett_luce o pairs.
max_length512Token per testo valutato, richiesta e risposta insieme.
learning_rate2e-5, oppure 2e-4 con LoRA

Le predizioni rispondono alla domanda dello schema stesso, quindi sono valutate e riscritte come quelle di qualsiasi altro schema. La pre-etichetta è la scheda selezionata per una scelta binaria, la posizione del cursore per una scala, l'ordine dell'elenco per un ranking, e il migliore e il peggiore selezionati per BWS. Ogni predizione archivia anche le rewards da cui deriva. Hanno significato solo le differenze tra ricompense. Confronto a coppie e Best-Worst Scaling confronta i formati comparativi per raccogliere i dati.

Uno studio che raccoglie valutazioni dei passi e correzioni

La seconda configurazione lavora su tracce di agent. Gli annotatori valutano ogni passo della traccia e poi correggono i passi errati. Le valutazioni dei passi addestrano prm-reward, e le correzioni addestrano llm-sft o llm-dpo. Ogni riga di data/traces.jsonl contiene il compito, i passi dell'agent e la risposta finale:

json
{"id": "traj_001", "task_description": "Find the current weather in San Francisco and report the temperature.", "steps": [{"thought": "I should look up the current weather.", "action": "web_search(queyr='San Francisco weather')"}, {"thought": "Open the first result.", "action": "open_url(results[0])"}, {"thought": "Read the temperature from the page.", "action": "extract_text('.temp')"}], "final_answer": "It is sunny."}

I trainer leggono il compito dal campo task_description, task o text dell'item, in quest'ordine:

yaml
annotation_task_name: "Agent traces: step ratings and corrections"
task_dir: .
data_files:
  - data/traces.jsonl
item_properties:
  id_key: id
  text_key: task_description
output_annotation_dir: annotation_output
user_config:
  allow_all_users: true
 
annotation_schemes:
  - annotation_type: process_reward
    name: step_quality
    description: "Rate each step of the agent's trace"
    steps_key: steps
    step_text_key: action
    mode: per_step
    allow_neutral: true
    ai_prelabel: true
 
  - annotation_type: trajectory_edit
    name: corrected_trace
    description: "Fix the wrong steps, then correct the final answer"
    steps_key: steps
    step_text_key: action
    editable_fields: [action]
    show_diff: true
    edit_final_answer: true
    final_answer_key: final_answer
 
model_training:
  enabled: true
  writeback:
    schemas: [step_quality]
    include_unlabeled: true

mode: per_step con allow_neutral: true dà le tre valutazioni che prm-reward impara. ai_prelabel: true aggiunge allo schema il pulsante "AI pre-label steps", e le valutazioni addestrate compaiono tramite il pulsante. writeback.schemas indica solo step_quality. Elencare lì corrected_trace impedisce l'avvio del server, e il validatore ne dà il motivo:

text
model_training.writeback.schemas names 'corrected_trace', a trajectory_edit scheme, which has no prelabel display: its predictions are stored and scored but annotators would never see them. Leave it out of writeback.schemas.

Riparare le tracce con llm-sft e llm-dpo

La risposta a uno schema trajectory_edit è una coppia di tracce, i passi originali dell'agent e i passi dopo le correzioni dell'annotatore. Modifica delle traiettorie per SFT/DPO tratta l'editor, e l'articolo sulla modifica delle traiettorie spiega come le coppie diventano dati di addestramento. Entrambi i trainer escludono le tracce che nessuno ha modificato.

  • llm-sft prende il compito e i passi originali come richiesta e i passi corretti come bersaglio, così il modello impara a riparare una traccia. Ogni passo è scritto come Step N seguito dai campi del passo (action: ..., thought: ...), e la risposta finale segue l'ultimo passo quando lo schema ha final_answer_key.
  • llm-dpo prende il compito come richiesta, i passi corretti come risposta preferita e i passi originali come altra risposta. Una predizione dice se il modello preferisce la correzione (A) o l'originale (B), ed è fatta solo per le tracce escluse dall'addestramento che hanno una correzione.

Entrambe le esecuzioni riportano closer_to_correction come punteggio held-out. Per llm-dpo, il punteggio è la quota di tracce escluse dall'addestramento in cui il modello preferisce la correzione. Per llm-sft, il punteggio è la quota in cui i passi scritti sono più vicini alla correzione che all'originale secondo l'F1 sui token, con token_f1 rispetto alla correzione riportato accanto al punteggio.

Le predizioni di un'esecuzione trajectory_edit sono archiviate e valutate, e gli annotatori non le vedono mai. L'editor parte sempre dalla traccia originale dell'agent, quindi la pagina non ha un posto dove mostrare una riscrittura predetta. Leggi le predizioni dalla pagina dell'esecuzione o da artifacts/predictions.jsonl nella directory dell'esecuzione.

Valutazioni dei passi con prm-reward

Un modello di ricompensa di processo valuta i passi di una traccia e non solo la risposta finale, come in PRM800K. prm-reward fornisce l'intera traccia a un encoder transformer come un unico input, il compito seguito da Step 1, Step 2 e così via. L'encoder impara una valutazione a tre classi sull'ultimo token di ogni passo, così ogni valutazione è fatta con i passi precedenti in vista. I passi che nessuno ha valutato sono esclusi dalla perdita. Il modello predefinito, answerdotai/ModernBERT-base, è stato scelto per il suo contesto di 8.192 token.

ParametroPredefinitoSignificato
model_nameanswerdotai/ModernBERT-baseUn encoder con un tokenizer veloce.
max_length1024Token per traccia.
epochs6
batch_size8Tracce per passo di addestramento.
learning_rate5e-5, oppure 3e-4 con LoRA

Le tracce lunghe perdono gli ultimi passi. Una traccia più lunga di max_length token viene tagliata dalla fine, e i passi oltre il taglio non vengono né usati per l'addestramento né valutati. Il log dell'esecuzione conta i passi persi. Quando max_length supera ciò che il modello contiene, l'esecuzione abbassa max_length al limite del modello con un avviso, e il limite è di 512 token per la maggior parte degli encoder della famiglia BERT. Quando il log dell'esecuzione riporta passi persi, aumenta max_length e scegli un modello il cui contesto contenga le tracce più lunghe.

Il punteggio held-out è step_macro_f1, l'F1 macro sulle tre valutazioni di ogni passo valutato. Nella maggior parte delle tracce la maggior parte dei passi è corretta, quindi l'accuratezza lusingherebbe un modello che non dice mai "errato". L'esecuzione riporta anche step_accuracy e first_error_accuracy, la quota di tracce in cui il primo passo errato del modello è il primo passo errato degli annotatori.

Una predizione valuta ogni passo che rientra in max_length, con la probabilità di ogni valutazione come confidenza della valutazione e il passo meno sicuro come confidenza della traccia. Con lo schema in writeback.schemas e ai_prelabel: true, il pulsante "AI pre-label steps" propone le valutazioni addestrate perché l'annotatore le verifichi. Un LLM giudice configurato sotto ai_support non viene chiamato per un item che ha valutazioni addestrate. Annotazione della ricompensa di processo tratta lo schema, e la guida ai modelli di ricompensa di processo tratta i dati.

Adattare un modello più grande con QLoRA

QLoRA carica a 4 bit i pesi del modello di base e addestra un adattatore LoRA sopra. llm-sft, llm-dpo, hf-text-classification, vlm-sft e reward-model accettano lora: {quantize: 4bit}, mentre prm-reward rifiuta l'impostazione prima di caricare il modello. Pianifica la memoria dalla riga "Peak GPU memory" che ogni esecuzione transformer registra alla fine. La sezione QLoRA del riferimento di configurazione descrive come si caricano i pesi di base e l'opzione a 8 bit.

Aggiungi questo blocco alla configurazione precedente per impostare QLoRA come predefinito per llm-sft:

yaml
model_training:
  enabled: true
  trainer_params:
    llm-sft:
      model_name: Qwen/Qwen2.5-7B-Instruct
      lora: {quantize: 4bit}
      gradient_checkpointing: true

QLoRA richiede una GPU CUDA e una build di bitsandbytes per la CUDA di quella macchina, installata con pip install bitsandbytes. Senza una GPU funzionante o senza la build, l'esecuzione si ferma alla validazione prima di caricare il modello.

Pubblicare un'esecuzione su Hugging Face Hub

Un'esecuzione riuscita il cui modello si trova sul server Potato ha Publish to the Hugging Face Hub nella pagina dell'esecuzione. Il push carica il modello e una model card README.md scritta a partire dall'esecuzione, con il trainer, il modello di base e l'adattatore, gli schemi e le etichette, le dimensioni delle partizioni, il punteggio held-out e ciò su cui è stato misurato, e gli ID dell'esecuzione e del bundle. Un'esecuzione LoRA ha salvato solo l'adattatore, quindi il repository su Hub contiene l'adattatore, e la card indica il modello di base su cui caricarlo.

Il token resta fuori dal browser. Crea un token di scrittura nelle impostazioni di Hugging Face e mettilo nell'ambiente del server prima di avviare Potato:

bash
export HF_TOKEN=hf_your_write_token
potato start config.yaml

Il modulo indica la variabile d'ambiente, HF_TOKEN per impostazione predefinita, chiede due volte il nome del repository e lascia selezionato Private per impostazione predefinita. Pubblicazione su Hugging Face Hub elenca i file che vengono caricati, i controlli prima di un push e la licenza sulla model card. Lo stesso push dall'API:

bash
curl -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
  http://localhost:8000/admin/training/api/runs/RUN_ID/push-to-hub \
  -d '{"repo_id": "your-org/support-dpo", "confirm_repo_id": "your-org/support-dpo", "private": true, "licence": "apache-2.0"}'

La route restituisce 201 con il commit. Un modello rimasto su un host remoto con fetch_artifacts: false non può essere pubblicato dal server Potato, e la pagina dell'esecuzione indica dove si trova il modello. Per passare da un modello pubblicato a un ciclo di apprendimento attivo, continua con Il modello nel ciclo.

Ulteriori letture

Riferimenti

  • Dettmers, T., Pagnoni, A., Holtzman, A., and Zettlemoyer, L. (2023). QLoRA: Efficient Finetuning of Quantized LLMs. arXiv:2305.14314. arxiv.org/abs/2305.14314
  • Hu, E. J., Shen, Y., Wallis, P., Allen-Zhu, Z., Li, Y., Wang, S., Wang, L., and Chen, W. (2021). LoRA: Low-Rank Adaptation of Large Language Models. arXiv:2106.09685. arxiv.org/abs/2106.09685
  • Lightman, H., Kosaraju, V., Burda, Y., Edwards, H., Baker, B., Lee, T., Leike, J., Schulman, J., Sutskever, I., and Cobbe, K. (2023). Let's Verify Step by Step. arXiv:2305.20050. arxiv.org/abs/2305.20050
  • Ouyang, L., Wu, J., Jiang, X., Almeida, D., Wainwright, C. L., Mishkin, P., Zhang, C., Agarwal, S., Slama, K., Ray, A., Schulman, J., Hilton, J., Kelton, F., Miller, L., Simens, M., Askell, A., Welinder, P., Christiano, P., Leike, J., and Lowe, R. (2022). Training Language Models to Follow Instructions with Human Feedback. arXiv:2203.02155. arxiv.org/abs/2203.02155
  • Rafailov, R., Sharma, A., Mitchell, E., Ermon, S., Manning, C. D., and Finn, C. (2023). Direct Preference Optimization: Your Language Model is Secretly a Reward Model. arXiv:2305.18290. arxiv.org/abs/2305.18290