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:
| Trainer | Schema | Che cosa impara il modello | Metrica principale held-out | Mostrato agli annotatori |
|---|---|---|---|---|
llm-sft | text | a scrivere la risposta scritta dagli annotatori | F1 sui token | sì, la risposta generata riempie la casella di testo |
llm-sft | trajectory_edit | a riscrivere i passi di un agent nei passi corretti | closer_to_correction | no, solo archiviata e valutata |
llm-dpo | pairwise in modalità binary | a preferire la risposta scelta dagli annotatori | accuratezza, pareggi esclusi | sì, viene selezionata la scheda della risposta preferita |
llm-dpo | trajectory_edit | a preferire i passi corretti a quelli originali | closer_to_correction | no, solo archiviata e valutata |
reward-model | pairwise (binary e scale), ranking, bws | un punteggio per ogni risposta | accuratezza, accuratezza di direzione, tau di Kendall o accuratezza best-worst | sì, come risposta dello schema stesso |
prm-reward | process_reward | una valutazione corretto, neutro o errato per ogni passo | step_macro_f1 | proposta 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:
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:
{"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:
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.7text_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:
python -m potato.validate_cli config.yaml --strictAvvia 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:
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:
| Parametro | Predefinito | Significato |
|---|---|---|
model_name | Qwen/Qwen2.5-0.5B-Instruct | Un modello linguistico causale su Hugging Face Hub, oppure una directory locale. |
lora | true | false addestra tutti i pesi e richiede una memoria parecchie volte maggiore. {quantize: 4bit} carica il modello di base a 4 bit (vedi QLoRA). |
max_length | 1024 | Token per esempio. Una richiesta lunga perde token dall'inizio, e la risposta viene conservata. |
max_new_tokens | 256 | La risposta più lunga che il modello genera. |
instruction | la description dello schema | Testo messo prima del testo di ogni item. |
system_prompt | nessuno | Un messaggio di sistema per i modelli con un template di chat. |
gradient_checkpointing | false | Risparmia memoria delle attivazioni al costo di circa un terzo di tempo in più. |
learning_rate | 2e-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.
| Parametro | Predefinito | Significato |
|---|---|---|
model_name | Qwen/Qwen2.5-0.5B-Instruct | Un modello linguistico causale. |
beta | 0.1 | Quanto il modello può allontanarsi dal riferimento. Un valore più basso gli lascia spostarsi di più. |
epochs | 2 | |
learning_rate | 5e-5 con LoRA, 5e-7 senza | |
max_length | 1024 | Token per richiesta e risposta. |
instruction | nessuno | Testo 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:
| Schema | Segnale di addestramento | Metrica principale held-out |
|---|---|---|
pairwise, binary | La risposta scelta batte l'altra. I pareggi sono saltati. | accuratezza |
pairwise, scale | Vince il lato verso cui pende il cursore, con un peso dato da quanto pende. Il punto medio è saltato. | accuratezza di direzione |
ranking | L'intero ordine, con la perdita di Plackett-Luce, oppure ogni coppia dell'ordine con ranking_loss: pairs. | tau di Kendall |
bws | L'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.
| Parametro | Predefinito | Significato |
|---|---|---|
model_name | distilbert/distilroberta-base | Un encoder, oppure un LLM con lora. |
ranking_loss | plackett_luce | Solo per gli schemi ranking: plackett_luce o pairs. |
max_length | 512 | Token per testo valutato, richiesta e risposta insieme. |
learning_rate | 2e-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:
{"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:
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: truemode: 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:
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-sftprende 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 comeStep Nseguito dai campi del passo (action: ...,thought: ...), e la risposta finale segue l'ultimo passo quando lo schema hafinal_answer_key.llm-dpoprende 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.
| Parametro | Predefinito | Significato |
|---|---|---|
model_name | answerdotai/ModernBERT-base | Un encoder con un tokenizer veloce. |
max_length | 1024 | Token per traccia. |
epochs | 6 | |
batch_size | 8 | Tracce per passo di addestramento. |
learning_rate | 5e-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:
model_training:
enabled: true
trainer_params:
llm-sft:
model_name: Qwen/Qwen2.5-7B-Instruct
lora: {quantize: 4bit}
gradient_checkpointing: trueQLoRA 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:
export HF_TOKEN=hf_your_write_token
potato start config.yamlIl 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:
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
- Configurazione dell'addestramento di modelli: ogni chiave di
model_training, i punteggi held-out, il write-back e le route. - Trainer: tutti i 38 trainer, raggruppati per compito.
- Addestramento remoto: server GPU via ssh, cluster SLURM e l'agent che esegue il polling.
- Annotazione delle traiettorie di agent: raccogliere dati di tracce in Potato.
- Sezioni di riferimento upstream per
llm-sft,llm-dpo, le tracce di agent,prm-reward,reward-model, QLoRA e la pubblicazione su Hub.
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