En este tutorial, implementamos un flujo de trabajo de optimización directa de preferencias de un extremo a otro para alinear un modelo de lenguaje grande con las preferencias humanas sin utilizar un modelo de recompensa. Combinamos DPOTrainer de TRL con QLoRA y PEFT para hacer factible la alineación basada en preferencias en una única GPU Colab. Entrenamos directamente en el conjunto de datos binarios UltraFeedback, donde cada mensaje tiene una respuesta elegida y una rechazada, lo que nos permite dar forma al comportamiento y estilo del modelo en lugar de simplemente recordar hechos.
Configuramos el entorno de ejecución e instalamos todas las bibliotecas necesarias para DPO, PEFT y capacitación cuantificada. Definimos todos los hiperparámetros globales, límites de conjuntos de datos y configuraciones de optimización en un solo lugar. También inicializamos el generador de números aleatorios y confirmamos la disponibilidad de la GPU para garantizar ejecuciones reproducibles.
Cargamos el tokenizador y el modelo de lenguaje base usando cuantificación de 4 bits para minimizar el uso de memoria. Configuramos bitsandbytes para permitir un cálculo eficiente de estilo QLoRA en GPU Colab. Preparamos el modelo para el entrenamiento deshabilitando el uso de caché para evitar incompatibilidades durante la retropropagación.
Adjuntamos adaptadores LoRA a las capas de atención y proyección de avance del modelo. Restringimos el entrenamiento a un pequeño conjunto de parámetros para que el ajuste sea eficiente y estable. Habilitamos puntos de control de gradiente para reducir aún más el consumo de memoria de la GPU durante el entrenamiento.
prueba_raw = ds[test_split] si test_split no es Ninguno más Ninguno print(“Splits:”, ds.keys()) print(“Usando train split:”, train_split, “size:”, len(train_raw)) si test_raw no es Ninguno: print(“Usando test split:”, test_split, “size:”, len(test_raw)) def _extract_last_user_and_assistant(messages): last_user_idx = Ninguno last_asst_idx = Ninguno para i, m en enumerate(messages): if m.get(“role”) == “user”: last_user_idx = i if m.get(“role”) == “assistant”: last_asst_idx = i si last_user_idx es Ninguno o last_asst_idx es Ninguno: devuelve Ninguno, Ninguno Prompt_messages = mensajes[: last_user_idx + 1]
asistente_texto = mensajes[last_asst_idx].get(“content”, “”) devuelve mensajes_indicación, texto_asistente def format_example(ex): mensajes_elegidos = ex[“chosen”]
mensajes_rechazados = ex[“rejected”]
Prompt_msgs_c, texto_elegido = _extract_last_user_and_assistant(msgs_elegidos) Prompt_msgs_r, texto_rechazado = _extract_last_user_and_assistant(msgs_rechazados) si Prompt_msgs_c es Ninguno o Prompt_msgs_r es Ninguno: devuelve {“prompt”: Ninguno, “elegido”: Ninguno, “rechazado”: Ninguno} aviso_texto = tokenizer.apply_chat_template( aviso_msgs_c, tokenize=False, add_generación_prompt=True ) return { “prompt”: aviso_texto, “elegido”: texto_elegido.strip(), “rechazado”: texto_rechazado.strip(), } train_raw = train_raw.shuffle(seed=SEED) train_raw = train_raw.select(range(min(MAX_TRAIN_SAMPLES, len(train_raw)))) train_ds = train_raw.map(format_example, remove_columns=train_raw.column_names) train_ds = train_ds.filter(lambda x: x[“prompt”] no es Ninguno y len(x[“chosen”]) > 0 y len(x[“rejected”]) > 0) si test_raw no es Ninguno: test_raw = test_raw.shuffle(seed=SEED) test_raw = test_raw.select(range(min(MAX_EVAL_SAMPLES, len(test_raw)))) eval_ds = test_raw.map(format_example, remove_columns=test_raw.column_names) eval_ds = eval_ds.filter(lambda x: x[“prompt”] no es Ninguno y len(x[“chosen”]) > 0 y len(x[“rejected”]) > 0) else: eval_ds = Ninguno print(“Ejemplos de tren:”, len(train_ds), “Ejemplos de evaluación:”, len(eval_ds) si eval_ds no es Ninguno más 0) print(train_ds[0])
Cargamos el conjunto de datos binarios UltraFeedback y seleccionamos dinámicamente las divisiones de tren y prueba adecuadas. Extraemos respuestas solicitadas, elegidas y rechazadas de conversaciones de varios turnos y las formateamos utilizando la plantilla de chat del modelo. Mezclamos, filtramos y submuestreamos los datos para crear conjuntos de datos de capacitación y evaluación limpios y eficientes.
Configuramos el objetivo de formación del DPO con parámetros de optimización y programación cuidadosamente elegidos. Inicializamos DPOTrainer para optimizar directamente los pares de preferencias sin un modelo de recompensa. Entrenamos los adaptadores LoRA y guardamos los artefactos del modelo alineado para su posterior inferencia.
print(“\n” + “=”*90) print(f”Muestra #{i}”) print(“- Mensaje:\n”, mensaje) base_out = generate_text(base_model, mensaje) dpo_out = generar_text(dpo_model, mensaje) print(“\n- Salida del modelo base:\n”, base_out) print(“\n- Salida DPO (LoRA):\n”, dpo_out) print(“\nListo.”)
Recargamos el modelo base y adjuntamos los adaptadores DPO LoRA entrenados para realizar inferencias. Generamos respuestas tanto del modelo original como del alineado utilizando las mismas indicaciones de comparación. Evaluamos cualitativamente cómo la optimización de preferencias cambia el comportamiento del modelo inspeccionando las salidas una al lado de la otra.
En conclusión, demostramos cómo DPO proporciona una alternativa estable y eficiente a RLHF al optimizar directamente los pares de preferencias con un objetivo simple y bien definido. Demostramos que el ajuste fino eficiente de los parámetros con LoRA y la cuantificación de 4 bits permite la experimentación práctica incluso bajo estrictas restricciones informáticas. Validamos cualitativamente la alineación comparando generaciones antes y después del entrenamiento de DPO, confirmando que el modelo aprende a preferir respuestas de mayor calidad sin dejar de ser liviano y desplegable.
Consulta los CÓDIGOS COMPLETOS aquí. Además, no dude en seguirnos en Twitter y no olvide unirse a nuestro SubReddit de más de 100.000 ML y suscribirse a nuestro boletín. ¡Esperar! estas en telegrama? Ahora también puedes unirte a nosotros en Telegram.