Ajuste fino de un LLM con tus anotaciones
Entrena un LLM con SFT o DPO, un modelo de recompensa o un modelo de recompensa por procesos con las etiquetas de tu estudio, puntúa cada uno sobre ítems reservados y publícalo en el Hub.
Novedad de la v2.14.0
Este tutorial entrena cuatro tipos de modelo de lenguaje con las respuestas que reúne un estudio de Potato. El entrenador llm-sft ajusta un LLM con respuestas escritas o con trazas de agente corregidas, llm-dpo ajusta un LLM con elecciones por pares, reward-model ajusta un modelo de recompensa escalar con comparaciones y prm-reward ajusta un modelo de recompensa por procesos con valoraciones de pasos. La página está escrita para equipos que ya reúnen datos de preferencias o de trazas en Potato y quieren un primer modelo a partir de esas etiquetas sin exportar los datos a un script de entrenamiento aparte. Cada sección nombra el esquema que produce los datos, da una configuración que pasa el validador de Potato, lista los parámetros que conviene cambiar e indica qué mide la puntuación sobre ítems reservados y si las predicciones vuelven a los anotadores como preetiquetas. La descripción general del entrenamiento de modelos cubre la página Training, y Entrena tu primer modelo recorre todo el ciclo con un clasificador que no necesita GPU.
Entrenadores por esquema de anotación
Cada entrenador de esta página aprende de un esquema de anotación por ejecución, y la página Training ofrece un entrenador solo para los esquemas de los que puede aprender. La tabla siguiente lista los esquemas de los que aprende cada entrenador:
| Entrenador | Esquema | Qué aprende el modelo | Métrica principal sobre ítems reservados | Se muestra a los anotadores |
|---|---|---|---|---|
llm-sft | text | a escribir la respuesta que escribieron los anotadores | F1 por tokens | sí, la respuesta generada rellena el cuadro de texto |
llm-sft | trajectory_edit | a reescribir los pasos de un agente en los pasos corregidos | closer_to_correction | no, solo se guarda y se puntúa |
llm-dpo | pairwise en modo binary | a preferir la respuesta que eligieron los anotadores | exactitud, sin contar los empates | sí, se selecciona la tarjeta de la respuesta preferida |
llm-dpo | trajectory_edit | a preferir los pasos corregidos frente a los originales | closer_to_correction | no, solo se guarda y se puntúa |
reward-model | pairwise (binary y scale), ranking, bws | una puntuación por respuesta | exactitud, exactitud de dirección, tau de Kendall o exactitud mejor-peor | sí, como la respuesta propia del esquema |
prm-reward | process_reward | una valoración de correcto, neutro o incorrecto para cada paso | step_macro_f1 | se ofrece mediante el botón «AI pre-label steps» |
Antes de empezar
Los extras de entrenamiento de Potato instalan PyTorch, Transformers y PEFT para los cuatro entrenadores:
pip install "potato-annotation[train-llm]"
pip install "potato-annotation[train-text]"El primer extra cubre llm-sft y llm-dpo, y el segundo cubre reward-model y prm-reward. pip install "potato-annotation[train]" instala todos los extras de entrenamiento a la vez. Un entrenador cuyos paquetes faltan aparece en la página Training como «not installed», con el comando que instala los paquetes.
Cada entrenador se niega a ajustar con menos de 20 ítems en la partición train (el parámetro min_instances). La partición predeterminada reparte el 70 % de los ítems etiquetados en train, el 15 % en val y el 15 % en test, así que conviene contar con unos 30 ítems etiquetados antes de la primera ejecución. Los ítems val y test quedan fuera del entrenamiento y puntúan el modelo.
llm-sft y llm-dpo parten de Qwen/Qwen2.5-0.5B-Instruct, reward-model de distilbert/distilroberta-base y prm-reward de answerdotai/ModernBERT-base. Los entrenadores de LLM están pensados para ejecutarse en una GPU. Cuando la máquina que ejecuta Potato no tiene GPU, el entrenamiento remoto envía las ejecuciones a un servidor con GPU por ssh o a un clúster SLURM.
Un estudio que reúne preferencias y reescrituras
La primera configuración hace dos preguntas sobre cada consulta de atención al cliente. Los anotadores eligen la mejor de dos respuestas y escriben la respuesta que un anotador querría recibir del asistente. La elección entrena llm-dpo y reward-model, y la respuesta escrita entrena llm-sft. Cada línea de data/answers.jsonl contiene una consulta y las dos respuestas:
{"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 configuración muestra la consulta como texto del ítem y las dos respuestas como tarjetas A y 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 importa por dos motivos, porque la consulta es lo que leen los anotadores y lo que llm-sft coloca después de la pregunta. items_key: responses nombra la lista de la que salen las tarjetas de pairwise, en el orden de los datos, de modo que A es la primera respuesta. writeback.schemas lista solo preferred, así que los anotadores ven la elección del modelo y no una reescritura generada. Las preetiquetas anclan a los anotadores, así que añade rewrite a la lista solo cuando la puntuación sobre ítems reservados de la ejecución de llm-sft justifique mostrar el texto del modelo. min_confidence: 0.7 deja fuera de la página las predicciones de las que el modelo está menos seguro y sigue guardándolas. Comprueba la configuración con el validador de Potato antes de iniciar el servidor:
python -m potato.validate_cli config.yaml --strictInicia Potato, etiqueta al menos 30 consultas y abre /admin/training como administrador. Las ejecuciones siguientes también pueden iniciarse desde la API con el admin_api_key de la configuración en $KEY.
Ajuste supervisado con llm-sft
llm-sft convierte cada ítem etiquetado en un ejemplo de entrenamiento. La consulta es la description del esquema (la pregunta que vieron los anotadores) seguida del texto del ítem, y el objetivo es la respuesta que escribió el anotador. La pérdida cubre solo los tokens de la respuesta, de modo que el modelo aprende a responder y no a repetir la consulta. De forma predeterminada se entrena un adaptador LoRA en lugar de todos los pesos.
Elige llm-sft y el esquema rewrite en la página Training y pulsa Train now, o inicia la misma ejecución desde la 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 mayoría de las ejecuciones cambian solo los parámetros de esta tabla:
| Parámetro | Valor predeterminado | Significado |
|---|---|---|
model_name | Qwen/Qwen2.5-0.5B-Instruct | Un modelo de lenguaje causal de Hugging Face Hub, o un directorio local. |
lora | true | false entrena todos los pesos y necesita varias veces más memoria. {quantize: 4bit} carga el modelo base en 4 bits (véase QLoRA). |
max_length | 1024 | Tokens por ejemplo. Una consulta larga pierde tokens del principio, y la respuesta se conserva. |
max_new_tokens | 256 | La respuesta más larga que genera el modelo. |
instruction | la description del esquema | Texto que se coloca antes del texto de cada ítem. |
system_prompt | ninguno | Un mensaje de sistema para los modelos con plantilla de chat. |
gradient_checkpointing | false | Ahorra memoria de activaciones a costa de cerca de un tercio más de tiempo. |
learning_rate | 2e-4 con LoRA, 1e-5 sin LoRA |
La ejecución conserva el punto de control con la menor pérdida en val. Como la ejecución eligió el punto de control sobre val, la puntuación sobre ítems reservados se lee sobre test. La puntuación es el F1 por tokens, el solapamiento de palabras entre la respuesta generada y la escrita sin importar el orden, y junto a ella se informa de la coincidencia exacta. El F1 por tokens da crédito a una respuesta que usa las palabras correctas en los lugares equivocados, así que conviene leer las dos métricas juntas.
El modelo genera una respuesta de forma voraz para cada ítem de val y test. Con writeback.include_unlabeled: true, el modelo responde también a los ítems que nadie ha etiquetado. La confianza de una predicción es la probabilidad media del modelo para los tokens que generó, y la cola de revisión del modelo se ordena por esa confianza. Con rewrite en writeback.schemas, la respuesta generada rellena el cuadro de texto cuando un anotador abre el ítem.
El texto libre encaja mal con varios anotadores por ítem. El ajuste aggregation resuelve varias respuestas en una comparando las respuestas palabra por palabra, y dos respuestas escritas rara vez coinciden palabra por palabra. Para llm-sft sobre un esquema text, un solo redactor por ítem evita la comparación palabra por palabra.
Ajuste por preferencias con llm-dpo
La optimización directa de preferencias (DPO) entrena un LLM para aumentar la verosimilitud de la respuesta preferida respecto de la otra respuesta, medida frente a una copia de referencia congelada del modelo de partida. llm-dpo construye un ejemplo con cada ítem respondido como A o B. La consulta es el campo prompt del ítem, o el primero de question, instruction, query, input y context que tenga el ítem. Los ítems respondidos con la opción de empate se dejan fuera, porque un empate no marca ninguna respuesta como mejor.
Elige llm-dpo y el esquema preferred, o envía la misma solicitud de antes con "trainer": "llm-dpo" y "schemas": ["preferred"]. Con el adaptador LoRA predeterminado, el modelo de referencia es el mismo modelo con el adaptador desactivado, de modo que no se guarda una segunda copia en memoria. lora: false entrena todos los pesos y conserva una copia congelada como referencia, y la copia congelada duplica la memoria.
| Parámetro | Valor predeterminado | Significado |
|---|---|---|
model_name | Qwen/Qwen2.5-0.5B-Instruct | Un modelo de lenguaje causal. |
beta | 0.1 | Cuánto puede alejarse el modelo de la referencia. Un valor menor deja que el modelo se aleje más. |
epochs | 2 | |
learning_rate | 5e-5 con LoRA, 5e-7 sin LoRA | |
max_length | 1024 | Tokens por consulta y respuesta. |
instruction | ninguno | Texto que se coloca antes de la consulta de cada ítem. |
La ejecución conserva el punto de control con la menor pérdida DPO en val, y el registro de la ejecución indica con qué frecuencia el modelo prefiere la elección de los anotadores en val. La puntuación sobre ítems reservados es la exactitud sobre test, entre los ítems no respondidos como empate, con el F1 macro junto a la exactitud. Una predicción es la respuesta que prefiere el modelo entrenado, con la probabilidad de esa preferencia como confianza. Como preetiqueta, la tarjeta de la respuesta preferida queda seleccionada cuando el anotador abre el ítem. Con randomize_order en el esquema, la selección sigue a la respuesta allí donde el anotador la ve. El adaptador guardado es también una política y genera texto cuando se carga con PEFT. Para saber más sobre cómo reunir los propios datos de preferencias, véanse Recopilación de datos de RLHF y de preferencias y Comparación por pares.
Un modelo de recompensa con reward-model
Un modelo de recompensa asigna un número a una respuesta, para RLHF, para el muestreo best-of-n o para ordenar salidas nuevas. reward-model entrena un transformer con una sola salida para que la respuesta que prefirieron los anotadores reciba una puntuación mayor. La pérdida es la de Bradley–Terry, -log sigmoid(r_winner - r_loser), la pérdida de modelado de recompensas de InstructGPT. El esquema preferred de arriba entrena reward-model sin cambiar la configuración. Cada esquema comparativo aporta una señal de entrenamiento distinta:
| Esquema | Señal de entrenamiento | Métrica principal sobre ítems reservados |
|---|---|---|
pairwise, binary | La respuesta elegida supera a la otra. Los empates se omiten. | exactitud |
pairwise, scale | Gana el lado hacia el que se inclina el deslizador, ponderado por cuánto se inclina. El punto medio se omite. | exactitud de dirección |
ranking | El orden completo, con la pérdida de Plackett-Luce, o todos los pares del orden con ranking_loss: pairs. | tau de Kendall |
bws | El mejor ítem supera a todos los demás de la tupla, y todos los demás superan al peor. | exactitud mejor-peor |
Cuando un ítem tiene un campo de consulta, cada recompensa es la de la respuesta dada la consulta. Las opciones de una clasificación son las etiquetas del esquema, y cada una se puntúa dado el texto del ítem. El servidor construye las tuplas BWS a partir de bws_config, de modo que una ejecución con los datos del propio servidor ya tiene las tuplas, y los archivos de datos no las necesitan.
| Parámetro | Valor predeterminado | Significado |
|---|---|---|
model_name | distilbert/distilroberta-base | Un codificador, o un LLM con lora. |
ranking_loss | plackett_luce | Solo esquemas ranking: plackett_luce o pairs. |
max_length | 512 | Tokens por texto puntuado, consulta y respuesta juntas. |
learning_rate | 2e-5, o 2e-4 con LoRA |
Las predicciones responden a la pregunta propia del esquema, de modo que se puntúan y se escriben de vuelta como las de cualquier otro esquema. La preetiqueta es la tarjeta seleccionada en una elección binaria, la posición del deslizador en una escala, el orden de la lista en una clasificación y el mejor y el peor seleccionados en BWS. Cada predicción guarda además las rewards de las que procede. Solo las diferencias entre recompensas tienen significado. Comparación por pares y escalado mejor-peor compara los formatos comparativos para reunir los datos.
Un estudio que reúne valoraciones de pasos y correcciones
La segunda configuración trabaja con trazas de agente. Los anotadores valoran cada paso de la traza y después corrigen los pasos incorrectos. Las valoraciones de pasos entrenan prm-reward, y las correcciones entrenan llm-sft o llm-dpo. Cada línea de data/traces.jsonl contiene la tarea, los pasos del agente y la respuesta final:
{"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."}Los entrenadores leen la tarea del campo task_description, task o text del ítem, en ese orden:
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 da las tres valoraciones que aprende prm-reward. ai_prelabel: true pone el botón «AI pre-label steps» en el esquema, y las valoraciones entrenadas aparecen mediante el botón. writeback.schemas nombra solo step_quality. Si se lista corrected_trace ahí, el servidor no arranca, y el validador da el 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.Reparar trazas con llm-sft y llm-dpo
Una respuesta de trajectory_edit es un par de trazas, los pasos originales del agente y los pasos después de las correcciones del anotador. Edición de trayectorias para SFT/DPO cubre el editor, y la entrada sobre edición de trayectorias cubre cómo los pares se convierten en datos de entrenamiento. Ambos entrenadores dejan fuera las trazas que nadie editó.
llm-sfttoma la tarea y los pasos originales como consulta y los pasos corregidos como objetivo, de modo que el modelo aprende a reparar una traza. Cada paso se escribe comoStep Nseguido de los campos del paso (action: ...,thought: ...), y la respuesta final sigue al último paso cuando el esquema tienefinal_answer_key.llm-dpotoma la tarea como consulta, los pasos corregidos como respuesta preferida y los pasos originales como la otra respuesta. Una predicción indica si el modelo prefiere la corrección (A) o el original (B), y se hace solo para las trazas reservadas que tienen corrección.
Ambas ejecuciones informan de closer_to_correction como puntuación sobre ítems reservados. Para llm-dpo, la puntuación es la proporción de trazas reservadas en las que el modelo prefiere la corrección. Para llm-sft, la puntuación es la proporción en la que los pasos escritos se parecen más a la corrección que al original según el F1 por tokens, y junto a ella se informa de token_f1 frente a la corrección.
Las predicciones de una ejecución de trajectory_edit se guardan y se puntúan, y los anotadores nunca las ven. El editor siempre parte de la traza original del agente, de modo que la página no tiene dónde mostrar una reescritura predicha. Lee las predicciones desde la página de la ejecución o desde artifacts/predictions.jsonl en el directorio de la ejecución.
Valoración de pasos con prm-reward
Un modelo de recompensa por procesos valora los pasos de una traza y no solo la respuesta final, como en PRM800K. prm-reward alimenta un codificador transformer con la traza completa como una sola entrada, la tarea seguida de Step 1, Step 2 y así sucesivamente. El codificador aprende una valoración de tres clases en el último token de cada paso, de modo que cada valoración se hace con los pasos anteriores a la vista. Los pasos que nadie valoró quedan fuera de la pérdida. El modelo predeterminado, answerdotai/ModernBERT-base, se eligió por su contexto de 8192 tokens.
| Parámetro | Valor predeterminado | Significado |
|---|---|---|
model_name | answerdotai/ModernBERT-base | Un codificador con tokenizador rápido. |
max_length | 1024 | Tokens por traza. |
epochs | 6 | |
batch_size | 8 | Trazas por paso. |
learning_rate | 5e-5, o 3e-4 con LoRA |
Las trazas largas pierden sus últimos pasos. Una traza más larga que max_length tokens se corta por el final, y los pasos posteriores al corte ni se entrenan ni se valoran. El registro de la ejecución cuenta los pasos perdidos. Cuando max_length es mayor de lo que admite el modelo, la ejecución baja max_length al límite del modelo con una advertencia, y el límite es de 512 tokens en la mayoría de los codificadores de tipo BERT. Cuando el registro de la ejecución informa de pasos perdidos, sube max_length y elige un modelo cuyo contexto admita las trazas más largas.
La puntuación sobre ítems reservados es step_macro_f1, el F1 macro sobre las tres valoraciones en todos los pasos valorados. La mayoría de los pasos de la mayoría de las trazas son correctos, así que la exactitud favorecería a un modelo que nunca dice «incorrecto». La ejecución informa además de step_accuracy y first_error_accuracy, la proporción de trazas en las que el primer paso incorrecto del modelo es el primer paso incorrecto de los anotadores.
Una predicción valora todos los pasos que caben en max_length, con la probabilidad de cada valoración como su confianza y el paso de menor confianza como la confianza de la traza. Con el esquema en writeback.schemas y ai_prelabel: true, el botón «AI pre-label steps» ofrece las valoraciones entrenadas para que el anotador las verifique. Un juez LLM configurado en ai_support no se invoca para un ítem que ya tiene valoraciones entrenadas. Anotación de recompensa por procesos cubre el esquema, y la guía de modelos de recompensa por procesos cubre los datos.
Ajustar un modelo más grande con QLoRA
QLoRA carga los pesos del modelo base en 4 bits y entrena un adaptador LoRA encima. llm-sft, llm-dpo, hf-text-classification, vlm-sft y reward-model admiten lora: {quantize: 4bit}, y prm-reward rechaza el ajuste antes de cargar el modelo. Calcula la memoria a partir de la línea «Peak GPU memory» que registra al final toda ejecución de transformers. La sección sobre QLoRA de la referencia de configuración describe cómo se cargan los pesos base y la opción de 8 bits.
Añade este bloque a la configuración anterior para fijar QLoRA como valor predeterminado de llm-sft:
model_training:
enabled: true
trainer_params:
llm-sft:
model_name: Qwen/Qwen2.5-7B-Instruct
lora: {quantize: 4bit}
gradient_checkpointing: trueQLoRA necesita una GPU CUDA y una compilación de bitsandbytes para el CUDA de esa máquina, que se instala con pip install bitsandbytes. Sin una GPU o una compilación que funcionen, la ejecución se detiene en la validación antes de cargar el modelo.
Publicar una ejecución en Hugging Face Hub
Una ejecución correcta cuyo modelo está en el servidor de Potato tiene Publish to the Hugging Face Hub en la página de la ejecución. La subida envía el modelo y una ficha del modelo README.md redactada a partir de la ejecución, con el entrenador, el modelo base y el adaptador, los esquemas y las etiquetas, el tamaño de las particiones, la puntuación sobre ítems reservados y lo que midió esa puntuación, y los identificadores de la ejecución y del paquete. Una ejecución con LoRA guardó solo el adaptador, así que el repositorio del Hub contiene el adaptador, y la ficha nombra el modelo base sobre el que cargarlo.
El token no pasa por el navegador. Crea un token de escritura en tu configuración de Hugging Face y ponlo en el entorno del servidor antes de iniciar Potato:
export HF_TOKEN=hf_your_write_token
potato start config.yamlEl formulario nombra la variable de entorno, HF_TOKEN de forma predeterminada, pide el nombre del repositorio dos veces y deja Private marcado de forma predeterminada. Publicar en Hugging Face Hub lista los archivos que se suben, las comprobaciones previas a la subida y la licencia de la ficha del modelo. La misma subida desde la 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 ruta devuelve 201 con el commit. Un modelo que se quedó en un host remoto con fetch_artifacts: false no puede subirse desde el servidor de Potato, y la página de la ejecución indica dónde está el modelo. Para pasar de un modelo publicado a un ciclo de aprendizaje activo, continúa con Modelo en el ciclo.
Más información
- Configuración del entrenamiento de modelos: todas las claves de
model_training, las puntuaciones sobre ítems reservados, la escritura de vuelta y las rutas. - Entrenadores: los 38 entrenadores, agrupados por tarea.
- Entrenamiento remoto: servidores con GPU por ssh, clústeres SLURM y el agente que sondea.
- Anotación de trayectorias de agentes: reunir datos de trazas en Potato.
- Secciones de referencia del repositorio original para
llm-sft,llm-dpo, trazas de agente,prm-reward,reward-model, QLoRA y publicación en el Hub.
Referencias
- 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