From 6b6f58230e00e3d2971ffa7250778c41e20d1393 Mon Sep 17 00:00:00 2001 From: "I. Alonso" Date: Sat, 10 Oct 2026 22:21:03 +0200 Subject: [PATCH] Add Spanish translation for Chapter 12 Sections 4 to 7 (GRPO in TRL, Unsloth & Schedule) --- chapters/es/_toctree.yml | 14 ++ chapters/es/chapter12/4.mdx | 224 ++++++++++++++++++++++ chapters/es/chapter12/5.mdx | 234 +++++++++++++++++++++++ chapters/es/chapter12/6.mdx | 368 ++++++++++++++++++++++++++++++++++++ chapters/es/chapter12/7.mdx | 18 ++ 5 files changed, 858 insertions(+) create mode 100644 chapters/es/chapter12/4.mdx create mode 100644 chapters/es/chapter12/5.mdx create mode 100644 chapters/es/chapter12/6.mdx create mode 100644 chapters/es/chapter12/7.mdx diff --git a/chapters/es/_toctree.yml b/chapters/es/_toctree.yml index 10e3ce7a6..dffcf2019 100644 --- a/chapters/es/_toctree.yml +++ b/chapters/es/_toctree.yml @@ -113,7 +113,21 @@ - local: chapter8/2 title: ¿Qué hacer cuando se produce un error? +- title: 12. Construir modelos de razonamiento + subtitle: Aprende a construir modelos de razonamiento como DeepSeek R1 + new: true + sections: + - local: chapter12/4 + title: Implementación de GRPO en TRL + - local: chapter12/5 + title: Ejercicio práctico para ajustar un modelo con GRPO + - local: chapter12/6 + title: Ejercicio práctico con Unsloth + - local: chapter12/7 + title: Próximamente... + - title: Glosario sections: - local: glossary/1 title: Glosario + diff --git a/chapters/es/chapter12/4.mdx b/chapters/es/chapter12/4.mdx new file mode 100644 index 000000000..f6907fb18 --- /dev/null +++ b/chapters/es/chapter12/4.mdx @@ -0,0 +1,224 @@ +# Implementación de GRPO en TRL[[implementacion-de-grpo-en-trl]] + +En esta página aprenderemos a implementar Group Relative Policy Optimization (GRPO) utilizando la librería Transformer Reinforcement Learning (TRL). Nos centraremos en la implementación práctica con una cantidad mínima de código. + +Exploraremos los conceptos centrales de GRPO tal como se incorporan en el `GRPOTrainer` de TRL, utilizando fragmentos de la documentación oficial de TRL como guía. + +> [!TIP] +> Este capítulo está dirigido a principiantes en TRL. Si ya estás familiarizado con TRL, quizás también quieras consultar la [implementación de Open R1](https://github.com/huggingface/open-r1/blob/main/src/open_r1/grpo.py) de GRPO. + +En primer lugar, recordemos algunos de los conceptos importantes del algoritmo GRPO: + +- Formación de grupos: El modelo genera múltiples terminaciones para cada prompt. +- Aprendizaje de preferencias: El modelo aprende de una función de recompensa que compara grupos de terminaciones. +- Configuración del entrenamiento: El modelo utiliza una configuración para controlar el proceso de entrenamiento. + +¿Qué necesitamos hacer para implementar GRPO? + +- Definir un conjunto de datos de prompts. +- Definir una función de recompensa que tome una lista de terminaciones y devuelva una lista de recompensas. +- Configurar el proceso de entrenamiento con un `GRPOConfig`. +- Entrenar el modelo utilizando el `GRPOTrainer`. + +Aquí tienes un ejemplo mínimo para comenzar con el entrenamiento de GRPO: + +```python +from trl import GRPOTrainer, GRPOConfig +from datasets import load_dataset + +# 1. Cargar tu conjunto de datos +dataset = load_dataset("your_dataset", split="train") + + +# 2. Definir una función de recompensa simple +def reward_func(completions, **kwargs): + """Ejemplo: Recompensar terminaciones más largas""" + return [float(len(completion)) for completion in completions] + + +# 3. Configurar el entrenamiento +training_args = GRPOConfig( + output_dir="output", + num_train_epochs=3, + per_device_train_batch_size=4, + gradient_accumulation_steps=2, + logging_steps=10, +) + +# 4. Inicializar y entrenar +trainer = GRPOTrainer( + model="your_model", # ej. "Qwen/Qwen2-0.5B-Instruct" + args=training_args, + train_dataset=dataset, + reward_funcs=reward_func, +) +trainer.train() +``` + +## Componentes clave + +### 1. Formato del conjunto de datos + +Tu conjunto de datos debe contener prompts a los que responderá el modelo. El entrenador de GRPO generará múltiples terminaciones para cada prompt y utilizará la función de recompensa para compararlas. + +### 2. Función de recompensa + +La función de recompensa es crucial: determina cómo aprende el modelo. Aquí hay dos ejemplos prácticos: + +```python +# Ejemplo 1: Recompensa basada en la longitud de la terminación +def reward_length(completions, **kwargs): + return [float(len(completion)) for completion in completions] + + +# Ejemplo 2: Recompensa basada en coincidir con un patrón +import re + + +def reward_format(completions, **kwargs): + pattern = r"^.*?.*?$" + return [1.0 if re.match(pattern, c) else 0.0 for c in completions] +``` + +### 3. Configuración del entrenamiento + +Parámetros clave a considerar en `GRPOConfig`: + +```python +training_args = GRPOConfig( + # Parámetros esenciales + output_dir="output", + num_train_epochs=3, + num_generations=4, # Número de terminaciones a generar para cada prompt + per_device_train_batch_size=4, # Queremos obtener todas las generaciones en un lote por dispositivo + # Opcionales pero útiles + gradient_accumulation_steps=2, + learning_rate=1e-5, + logging_steps=10, + # Específicos de GRPO (opcionales) + use_vllm=True, # Acelerar la generación +) +``` + +El parámetro `num_generations` es particularmente importante para GRPO ya que define el tamaño del grupo: cuántas terminaciones diferentes generará el modelo para cada prompt. Este es un diferenciador clave de otros métodos de RL: + +- Demasiado pequeño (ej., 2-3): Puede no proporcionar suficiente diversidad para comparaciones significativas +- Recomendado (4-16): Proporciona un buen equilibrio entre diversidad y eficiencia computacional +- Valores mayores: Pueden mejorar el aprendizaje pero aumentan significativamente el costo computacional + +El tamaño del grupo debe elegirse según tus recursos computacionales y la complejidad de tu tarea. Para tareas simples, grupos más pequeños (4-8) pueden ser suficientes, mientras que tareas de razonamiento más complejas podrían beneficiarse de grupos más grandes (8-16). + +## Consejos para el éxito + +1. **Gestión de memoria**: Ajusta `per_device_train_batch_size` y `gradient_accumulation_steps` en función de la memoria de tu GPU. +2. **Velocidad**: Habilita `use_vllm=True` para una generación más rápida si tu modelo es compatible. +3. **Monitoreo**: Observa las métricas registradas durante el entrenamiento: + - `reward`: Recompensa promedio en todas las terminaciones + - `reward_std`: Desviación estándar dentro de los grupos de recompensa + - `kl`: Divergencia KL del modelo de referencia + +## Diseño de funciones de recompensa + +El artículo de DeepSeek R1 demuestra varios enfoques eficaces para el diseño de funciones de recompensa que puedes adaptar para tu propia implementación de GRPO: + +### 1. Recompensas basadas en la longitud + +Una de las recompensas más fáciles de implementar es una recompensa basada en la longitud. Puedes penalizar terminaciones demasiado cortas o demasiado largas: + +```python +def reward_len(completions, **kwargs): + ideal_length = 20 + return [-abs(ideal_length - len(completion)) for completion in completions] +``` + +Esta función de recompensa penaliza las terminaciones que son demasiado cortas o demasiado largas, animando al modelo a generar respuestas cercanas a la longitud ideal de 20 tokens. + + + +## 2. Recompensas basadas en reglas para tareas verificables + +Para tareas con respuestas objetivamente correctas (como matemáticas o programación), puedes implementar funciones de recompensa basadas en reglas: + +```python +def problem_reward(completions, answers, **kwargs): + """Función de recompensa para problemas matemáticos con respuestas verificables + completions: lista de terminaciones a evaluar + answers: lista de respuestas a los problemas del conjunto de datos + """ + + rewards = [] + for completion, correct_answer in zip(completions, answers): + # Extraer la respuesta de la terminación + try: + # Este es un ejemplo simplificado; necesitarías un análisis adecuado + answer = extract_final_answer(completion) + # Recompensa binaria: 1 para correcta, 0 para incorrecta + reward = 1.0 if answer == correct_answer else 0.0 + rewards.append(reward) + except: + # Si no podemos analizar una respuesta, otorgamos una recompensa baja + rewards.append(0.0) + + return rewards +``` + + + +## 3. Recompensas basadas en formato + +También puedes recompensar el formato adecuado, lo cual fue importante en el entrenamiento de DeepSeek R1: + +```python +def format_reward(completions, **kwargs): + """Recompensar las terminaciones que siguen el formato deseado""" + # Ejemplo: Comprobar si la terminación sigue un formato pensar-luego-responder + pattern = r"(.*?)\s*(.*?)" + + rewards = [] + for completion in completions: + match = re.search(pattern, completion, re.DOTALL) + if match: + # Comprobar si hay contenido sustancial en ambas secciones + think_content = match.group(1).strip() + answer_content = match.group(2).strip() + + if len(think_content) > 20 and len(answer_content) > 0: + rewards.append(1.0) + else: + rewards.append( + 0.5 + ) # Recompensa parcial por formato correcto pero contenido limitado + else: + rewards.append(0.0) # Sin recompensa por formato incorrecto + + return rewards +``` + + + +Estos ejemplos demuestran cómo puedes implementar funciones de recompensa inspiradas en el proceso de entrenamiento de DeepSeek R1, centrándote en la corrección, el formato y las señales combinadas. + +## ¡Eso es todo! + +En la siguiente sección, realizarás un ejercicio para implementar GRPO en TRL. diff --git a/chapters/es/chapter12/5.mdx b/chapters/es/chapter12/5.mdx new file mode 100644 index 000000000..2c1867cb2 --- /dev/null +++ b/chapters/es/chapter12/5.mdx @@ -0,0 +1,234 @@ + + +# Ejercicio práctico: Ajustar un modelo con GRPO[[ejercicio-practico-ajustar-un-modelo-con-grpo]] + +Ahora que has visto la teoría, ¡pongámosla en práctica! En este ejercicio, ajustarás finamente un modelo con GRPO. + +> [!TIP] +> Este ejercicio fue elaborado por el experto en ajuste fino de LLMs [@mlabonne](https://huggingface.co/mlabonne). + +## Instalar dependencias + +Primero, instalemos las dependencias para este ejercicio: + +```bash +!pip install -qqq datasets==3.2.0 transformers==4.47.1 trl==0.14.0 peft==0.14.0 accelerate==1.2.1 bitsandbytes==0.45.2 wandb==0.19.7 --progress-bar off +!pip install -qqq flash-attn --no-build-isolation --progress-bar off +``` + +Ahora importaremos las librerías necesarias: + +```python +import torch +from datasets import load_dataset +from peft import LoraConfig, get_peft_model +from transformers import AutoModelForCausalLM, AutoTokenizer +from trl import GRPOConfig, GRPOTrainer +``` + +## Importar e iniciar sesión en Weights & Biases + +Weights & Biases es una herramienta para registrar y monitorear tus experimentos. La usaremos para monitorear nuestro proceso de ajuste fino. + +```python +import wandb + +wandb.login() +``` + +Puedes hacer este ejercicio sin iniciar sesión en Weights & Biases, pero se recomienda hacerlo para realizar un seguimiento de tus experimentos e interpretar los resultados. + +## Cargar el conjunto de datos + +Ahora carguemos el conjunto de datos. En este caso, usaremos el conjunto de datos [`mlabonne/smoltldr`](https://huggingface.co/datasets/mlabonne/smoltldr), que contiene una lista de historias cortas. + +```python +dataset = load_dataset("mlabonne/smoltldr") +print(dataset) +``` + +## Cargar el modelo + +Ahora, carguemos el modelo. + +Para este ejercicio, utilizaremos el modelo [`SmolLM2-135M`](https://huggingface.co/HuggingFaceTB/SmolLM2-135M). + +Este es un modelo pequeño de 135M de parámetros que se ejecuta en hardware limitado. Esto lo hace ideal para el aprendizaje, aunque no es el modelo más potente del mercado. Si tienes acceso a hardware más potente, puedes intentar ajustar un modelo más grande como [`SmolLM2-1.7B`](https://huggingface.co/HuggingFaceTB/SmolLM2-1.7B). + +```python +model_id = "HuggingFaceTB/SmolLM-135M-Instruct" +model = AutoModelForCausalLM.from_pretrained( + model_id, + torch_dtype="auto", + device_map="auto", + attn_implementation="flash_attention_2", +) +tokenizer = AutoTokenizer.from_pretrained(model_id) +``` + +## Cargar LoRA + +Ahora, carguemos la configuración de LoRA. Aprovecharemos LoRA para reducir la cantidad de parámetros entrenables y, a su vez, la huella de memoria necesaria para ajustar el modelo. + +Si no estás familiarizado con LoRA, puedes leer más al respecto en el [Capítulo 11](https://huggingface.co/learn/course/en/chapter11/3). + +```python +# Cargar LoRA +lora_config = LoraConfig( + task_type="CAUSAL_LM", + r=16, + lora_alpha=32, + target_modules="all-linear", +) +model = get_peft_model(model, lora_config) +print(model.print_trainable_parameters()) +``` + +```sh +Total trainable parameters: 135M +``` + +## Definir la función de recompensa + +Como se mencionó en la sección anterior, GRPO puede utilizar cualquier función de recompensa para mejorar el modelo. En este caso, utilizaremos una función de recompensa simple que anima al modelo a generar texto de 50 tokens de longitud. + +```python +# Función de recompensa +ideal_length = 50 + + +def reward_len(completions, **kwargs): + return [-abs(ideal_length - len(completion)) for completion in completions] +``` + +## Definir los argumentos de entrenamiento + +Ahora definamos los argumentos de entrenamiento. Usaremos la clase `GRPOConfig` para definir los argumentos de entrenamiento en el estilo típico de `transformers`. + +Si es la primera vez que defines argumentos de entrenamiento, puedes consultar la clase [TrainingArguments](https://huggingface.co/docs/transformers/en/main_classes/trainer#trainingarguments) para más información, o el [Capítulo 2](https://huggingface.co/learn/course/en/chapter2/1) para una introducción detallada. + +```python +# Argumentos de entrenamiento +training_args = GRPOConfig( + output_dir="GRPO", + learning_rate=2e-5, + per_device_train_batch_size=8, + gradient_accumulation_steps=2, + max_prompt_length=512, + max_completion_length=96, + num_generations=8, + optim="adamw_8bit", + num_train_epochs=1, + bf16=True, + report_to=["wandb"], + remove_unused_columns=False, + logging_steps=1, +) +``` + +Ahora podemos inicializar el entrenador con el modelo, el conjunto de datos y los argumentos de entrenamiento, y comenzar el entrenamiento. + +```python +# Entrenador +trainer = GRPOTrainer( + model=model, + reward_funcs=[reward_len], + args=training_args, + train_dataset=dataset["train"], +) + +# Entrenar modelo +wandb.init(project="GRPO") +trainer.train() +``` + +El entrenamiento toma alrededor de 1 hora en una sola GPU A10G, disponible en Google Colab o a través de Hugging Face Spaces. + +## Subir el modelo al Hub durante el entrenamiento + +Si establecemos el argumento `push_to_hub` en `True` y el argumento `model_id` con un nombre de modelo válido, el modelo se subirá al Hugging Face Hub mientras entrenamos. ¡Esto es útil si deseas comenzar a probar el modelo de inmediato! + +## Interpretar los resultados del entrenamiento + +`GRPOTrainer` registra la recompensa de tu función de recompensa, la pérdida y una serie de otras métricas. + +Nos centraremos en la recompensa de la función de recompensa y en la pérdida. + +Como puedes ver, la recompensa de la función de recompensa se acerca a 0 a medida que el modelo aprende. Esta es una buena señal de que el modelo está aprendiendo a generar texto con la longitud correcta. + +![Recompensa de la función de recompensa](https://huggingface.co/reasoning-course/images/resolve/main/grpo/13.png) + +Podrías notar que la pérdida comienza en cero y luego aumenta durante el entrenamiento, lo que puede parecer contradictorio. Este comportamiento es el esperado en GRPO y está directamente relacionado con la formulación matemática del algoritmo. La pérdida en GRPO es proporcional a la divergencia KL (el límite relativo a la política original). A medida que avanza el entrenamiento, el modelo aprende a generar texto que coincide mejor con la función de recompensa, lo que hace que diverja más de su política inicial. Esta divergencia creciente se refleja en el valor ascendente de la pérdida, lo que realmente indica que el modelo se está adaptando exitosamente para optimizar la función de recompensa. + +![Pérdida](https://huggingface.co/reasoning-course/images/resolve/main/grpo/14.png) + +## Guardar y publicar el modelo + +¡Compartamos el modelo con la comunidad! + +```python +merged_model = trainer.model.merge_and_unload() +merged_model.push_to_hub( + "SmolGRPO-135M", private=False, tags=["GRPO", "Reasoning-Course"] +) +``` + +## Generar texto + +🎉 ¡Has ajustado exitosamente un modelo con GRPO! Ahora generemos algo de texto con el modelo. + +Primero, ¡definiremos un documento bastante largo! + +```python +prompt = """ +# A long document about the Cat + +The cat (Felis catus), also referred to as the domestic cat or house cat, is a small +domesticated carnivorous mammal. It is the only domesticated species of the family Felidae. +Advances in archaeology and genetics have shown that the domestication of the cat occurred +in the Near East around 7500 BC. It is commonly kept as a pet and farm cat, but also ranges +freely as a feral cat avoiding human contact. It is valued by humans for companionship and +its ability to kill vermin. Its retractable claws are adapted to killing small prey species +such as mice and rats. It has a strong, flexible body, quick reflexes, and sharp teeth, +and its night vision and sense of smell are well developed. It is a social species, +but a solitary hunter and a crepuscular predator. Cat communication includes +vocalizations—including meowing, purring, trilling, hissing, growling, and grunting—as +well as body language. It can hear sounds too faint or too high in frequency for human ears, +such as those made by small mammals. It secretes and perceives pheromones. +""" + +messages = [ + {"role": "user", "content": prompt}, +] +``` + +Ahora podemos generar texto con el modelo: + +```python +# Generar texto +from transformers import pipeline + +generator = pipeline("text-generation", model="SmolGRPO-135M") + +## O usar el modelo y tokenizador que definimos anteriormente +# generator = pipeline("text-generation", model=model, tokenizer=tokenizer) + +generate_kwargs = { + "max_new_tokens": 256, + "do_sample": True, + "temperature": 0.5, + "min_p": 0.1, +} + +generated_text = generator(messages, generate_kwargs=generate_kwargs) + +print(generated_text) +``` + +# Conclusión + +En este capítulo, hemos visto cómo ajustar finamente un modelo con GRPO. También hemos visto cómo interpretar los resultados del entrenamiento y generar texto con el modelo. diff --git a/chapters/es/chapter12/6.mdx b/chapters/es/chapter12/6.mdx new file mode 100644 index 000000000..a0861a928 --- /dev/null +++ b/chapters/es/chapter12/6.mdx @@ -0,0 +1,368 @@ + + +# Ejercicio práctico: GRPO con Unsloth[[ejercicio-practico-grpo-con-unsloth]] + +En este ejercicio, ajustarás finamente un modelo con GRPO (Group Relative Policy Optimization) usando Unsloth, para mejorar las capacidades de razonamiento de un modelo. Cubrimos GRPO en el [Capítulo 3](/course/chapter3/3). + +Unsloth es una librería que acelera el ajuste fino de LLMs, haciendo posible entrenar modelos más rápido y con menos recursos computacionales. Unsloth se conecta con TRL, por lo que aprovecharemos lo que aprendimos en las secciones anteriores y lo adaptaremos a las particularidades de Unsloth. + +> [!TIP] +> Este ejercicio se puede ejecutar en una GPU Google Colab T4 gratuita. Para disfrutar de la mejor experiencia, sigue el cuaderno enlazado arriba y pruébalo tú mismo. + +## Instalar dependencias + +Primero, instalemos las librerías necesarias. Necesitaremos Unsloth para el ajuste fino acelerado y vLLM para una inferencia rápida. + +```bash +pip install unsloth vllm +pip install --upgrade pillow +``` + +## Configurar Unsloth + +Unsloth proporciona una clase (`FastLanguageModel`) que integra transformers con las optimizaciones de Unsloth. Vamos a importarla: + +```python +from unsloth import FastLanguageModel +``` + +Ahora, carguemos el modelo Gemma 3 1B Instruct de Google y configurémoslo para el ajuste fino: + +```python +from unsloth import FastLanguageModel +import torch + +max_seq_length = 1024 # Se puede aumentar para secuencias de razonamiento más largas +lora_rank = 32 # Mayor rango = más inteligente, pero más lento + +model, tokenizer = FastLanguageModel.from_pretrained( + model_name="google/gemma-3-1b-it", + max_seq_length=max_seq_length, + load_in_4bit=True, # False para LoRA 16bit + fast_inference=True, # Habilitar inferencia rápida con vLLM + max_lora_rank=lora_rank, + gpu_memory_utilization=0.6, # Reducir si se agota la memoria +) + +model = FastLanguageModel.get_peft_model( + model, + r=lora_rank, # ¡Elige cualquier número > 0! Sugeridos: 8, 16, 32, 64, 128 + target_modules=[ + "q_proj", + "k_proj", + "v_proj", + "o_proj", + "gate_proj", + "up_proj", + "down_proj", + ], # Eliminar QKVO si se agota la memoria + lora_alpha=lora_rank, + use_gradient_checkpointing="unsloth", # Habilitar ajuste fino con contexto largo + random_state=3407, +) +``` + +Este código carga el modelo en cuantización de 4 bits para ahorrar memoria y aplica LoRA (Low-Rank Adaptation) para un ajuste fino eficiente. El parámetro `target_modules` especifica qué capas del modelo ajustar, y `use_gradient_checkpointing` permite entrenar con contextos más largos. + +> [!TIP] +> No cubriremos los detalles de LoRA en este capítulo, pero puedes aprender más en el [Capítulo 11](/course/chapter11/3). + +## Preparación de datos + +Para este ejercicio, utilizaremos el conjunto de datos GSM8K, que contiene problemas matemáticos de escuela primaria. Formatearemos los datos para alentar al modelo a mostrar su razonamiento antes de proporcionar una respuesta. + +Primero, definiremos el formato de los prompts y las respuestas: + +```python +# Definir el prompt del sistema que instruye al modelo a usar un formato específico +SYSTEM_PROMPT = """ +Respond in the following format: + +... + + +... + +""" + +XML_COT_FORMAT = """\ + +{reasoning} + + +{answer} + +""" +``` + +Ahora, preparemos el conjunto de datos: + +```python +import re +from datasets import load_dataset, Dataset + + +# Funciones auxiliares para extraer respuestas de diferentes formatos +def extract_xml_answer(text: str) -> str: + answer = text.split("")[-1] + answer = answer.split("")[0] + return answer.strip() + + +def extract_hash_answer(text: str) -> str | None: + if "####" not in text: + return None + return text.split("####")[1].strip() + + +# Función para preparar el conjunto de datos GSM8K +def get_gsm8k_questions(split="train") -> Dataset: + data = load_dataset("openai/gsm8k", "main")[split] + data = data.map( + lambda x: { + "prompt": [ + {"role": "system", "content": SYSTEM_PROMPT}, + {"role": "user", "content": x["question"]}, + ], + "answer": extract_hash_answer(x["answer"]), + } + ) + return data + + +dataset = get_gsm8k_questions() +``` + +El conjunto de datos se prepara extrayendo la respuesta y formateándola como una cadena. + +## Definir funciones de recompensa + +Como comentamos en [una página anterior](/course/chapter12/4), GRPO puede utilizar funciones de recompensa para guiar el aprendizaje del modelo según criterios verificables como longitud y formato. + +En este ejercicio, definiremos varias funciones de recompensa que fomentan diferentes aspectos de un buen razonamiento. Por ejemplo, recompensaremos al modelo por proporcionar una respuesta entera y por seguir el formato estricto. + +```python +# Función de recompensa que comprueba si la respuesta es correcta +def correctness_reward_func(prompts, completions, answer, **kwargs) -> list[float]: + responses = [completion[0]["content"] for completion in completions] + q = prompts[0][-1]["content"] + extracted_responses = [extract_xml_answer(r) for r in responses] + print( + "-" * 20, + f"Question:\n{q}", + f"\nAnswer:\n{answer[0]}", + f"\nResponse:\n{responses[0]}", + f"\nExtracted:\n{extracted_responses[0]}", + ) + return [2.0 if r == a else 0.0 for r, a in zip(extracted_responses, answer)] + + +# Función de recompensa que comprueba si la respuesta es un número entero +def int_reward_func(completions, **kwargs) -> list[float]: + responses = [completion[0]["content"] for completion in completions] + extracted_responses = [extract_xml_answer(r) for r in responses] + return [0.5 if r.isdigit() else 0.0 for r in extracted_responses] + + +# Función de recompensa que comprueba si la respuesta sigue el formato estricto +def strict_format_reward_func(completions, **kwargs) -> list[float]: + pattern = r"^\n.*?\n\n\n.*?\n\n$" + responses = [completion[0]["content"] for completion in completions] + matches = [re.match(pattern, r) for r in responses] + return [0.5 if match else 0.0 for match in matches] + + +# Función de recompensa que comprueba si la respuesta sigue un formato más flexible +def soft_format_reward_func(completions, **kwargs) -> list[float]: + pattern = r".*?\s*.*?" + responses = [completion[0]["content"] for completion in completions] + matches = [re.match(pattern, r) for r in responses] + return [0.5 if match else 0.0 for match in matches] + + +# Función de recompensa que cuenta las etiquetas XML y penaliza el contenido adicional +def count_xml(text) -> float: + count = 0.0 + if text.count("\n") == 1: + count += 0.125 + if text.count("\n\n") == 1: + count += 0.125 + if text.count("\n\n") == 1: + count += 0.125 + count -= len(text.split("\n\n")[-1]) * 0.001 + if text.count("\n") == 1: + count += 0.125 + count -= (len(text.split("\n")[-1]) - 1) * 0.001 + return count + + +def xmlcount_reward_func(completions, **kwargs) -> list[float]: + contents = [completion[0]["content"] for completion in completions] + return [count_xml(c) for c in contents] +``` + +Estas funciones de recompensa tienen diferentes propósitos: + +| Función de recompensa | Propósito | +|-----------------------|-----------| +| `correctness_reward_func` | Recompensa al modelo cuando su respuesta coincide con la correcta | +| `int_reward_func` | Recompensa al modelo por proporcionar una respuesta numérica | +| `strict_format_reward_func` y `soft_format_reward_func` | Recompensan al modelo por seguir el formato especificado | +| `xmlcount_reward_func` | Recompensa el uso adecuado de etiquetas XML y penaliza el contenido extra después de las etiquetas de cierre | + +## Entrenamiento con GRPO + +Ahora configuraremos el entrenador GRPO con nuestro modelo, tokenizador y funciones de recompensa. Esta parte sigue el mismo enfoque que el [ejercicio anterior](/course/chapter12/5). + +```python +from trl import GRPOConfig, GRPOTrainer + +max_prompt_length = 256 + +training_args = GRPOConfig( + learning_rate=5e-6, + adam_beta1=0.9, + adam_beta2=0.99, + weight_decay=0.1, + warmup_ratio=0.1, + lr_scheduler_type="cosine", + optim="paged_adamw_8bit", + logging_steps=1, + per_device_train_batch_size=1, + gradient_accumulation_steps=1, # Aumentar a 4 para un entrenamiento más fluido + num_generations=6, # Disminuir si se agota la memoria + max_prompt_length=max_prompt_length, + max_completion_length=max_seq_length - max_prompt_length, + # num_train_epochs = 1, # Establecer en 1 para una ejecución de entrenamiento completa + max_steps=250, + save_steps=250, + max_grad_norm=0.1, + report_to="none", # Se puede usar Weights & Biases + output_dir="outputs", +) + +trainer = GRPOTrainer( + model=model, + processing_class=tokenizer, + reward_funcs=[ + xmlcount_reward_func, + soft_format_reward_func, + strict_format_reward_func, + int_reward_func, + correctness_reward_func, + ], + args=training_args, + train_dataset=dataset, +) +``` + +`GRPOConfig` establece varios hiperparámetros para el entrenamiento: +- `use_vllm`: Permite inferencia rápida con vLLM +- `learning_rate`: Controla la rapidez con la que aprende el modelo +- `num_generations`: Número de terminaciones a generar para cada prompt +- `max_steps`: Número total de pasos de entrenamiento a realizar + +Ahora comencemos el entrenamiento: + +```python +trainer.train() +``` + +> [!WARNING] +> El entrenamiento puede llevar algún tiempo. Es posible que no veas aumentar las recompensas inmediatamente; pueden pasar entre 150 y 200 pasos antes de que comiences a ver mejoras. ¡Sé paciente! + +## Probar el modelo + +Después del entrenamiento, probemos nuestro modelo para ver cómo se comporta. Primero, guardaremos los pesos de LoRA: + +```python +model.save_lora("grpo_saved_lora") +``` + +Ahora, probemos el modelo con una nueva pregunta: + +```python +from vllm import SamplingParams + +text = tokenizer.apply_chat_template( + [ + {"role": "system", "content": SYSTEM_PROMPT}, + {"role": "user", "content": "Calculate pi."}, + ], + tokenize=False, + add_generation_prompt=True, +) + +sampling_params = SamplingParams( + temperature=0.8, + top_p=0.95, + max_tokens=1024, +) +output = ( + model.fast_generate( + text, + sampling_params=sampling_params, + lora_request=model.load_lora("grpo_saved_lora"), + )[0] + .outputs[0] + .text +) + +print(output) +``` + +Deberías ver que el modelo ahora sigue el formato especificado, mostrando su razonamiento antes de proporcionar una respuesta. + +## Guardar el modelo + +Unsloth ofrece varias opciones para guardar tu modelo ajustado finamente, pero nos centraremos en la más habitual: + +```python +# Guardar en precisión de 16 bits +model.save_pretrained_merged("model", tokenizer, save_method="merged_16bit") +``` + +## Subir al Hugging Face Hub + +Subiremos el modelo al Hugging Face Hub utilizando el método `push_to_hub_merged`. Este método nos permite subir el modelo en múltiples formatos de cuantización: + +```python +# Subir al Hugging Face Hub (requiere un token) +model.push_to_hub_merged( + "your-username/model-name", tokenizer, save_method="merged_16bit", token="your-token" +) +``` + +Unsloth también admite guardar en formato GGUF para su uso con llama.cpp: + +```python +model.push_to_hub_gguf( + "your-username/model-name", + tokenizer, + quantization_method=["q4_k_m", "q8_0", "q5_k_m"], + token="your-token", +) +``` + +Los archivos GGUF se pueden usar con llama.cpp o sistemas basados en interfaz de usuario como Jan u Open WebUI. + +## Conclusión + +En este ejercicio, has aprendido a: +1. Configurar Unsloth para un ajuste fino acelerado +2. Preparar datos para el entrenamiento con GRPO +3. Definir funciones de recompensa personalizadas para guiar el aprendizaje del modelo +4. Entrenar un modelo usando GRPO +5. Probar el modelo ajustado +6. Guardar el modelo en varios formatos + +GRPO es una técnica poderosa para alinear modelos de lenguaje con comportamientos específicos, y Unsloth la hace accesible incluso en hardware limitado. Al combinar múltiples funciones de recompensa, puedes guiar al modelo para que siga un formato específico y al mismo tiempo mejore sus capacidades de razonamiento. + +Para más información y recursos, consulta: +- [Documentación de Unsloth](https://docs.unsloth.ai/) +- [Discord de Unsloth](https://discord.gg/unsloth) +- [GitHub de Unsloth](https://github.com/unslothai/unsloth) diff --git a/chapters/es/chapter12/7.mdx b/chapters/es/chapter12/7.mdx new file mode 100644 index 000000000..6a87b72c2 --- /dev/null +++ b/chapters/es/chapter12/7.mdx @@ -0,0 +1,18 @@ +# Próximamente...[[proximamente]] + +¡Este capítulo se está impartiendo actualmente como una cohorte en vivo! Si has completado el material hasta ahora, esto es lo que puedes esperar: + +## Calendario del curso + +| Fecha | Unidad | +|------|------| +| ~7 de marzo de 2025~ | ~Examen No-Code y Certificación~ | +| ~14 de marzo de 2025~ | ~Siguiente ejercicio práctico~ | +| 21 de marzo de 2025 | Revisión interactiva de código | +| Abril de 2025 | Más material escrito sobre la construcción de modelos de razonamiento | +| Abril de 2025 | Sesiones en vivo sobre la construcción de Open R1 | +| Abril de 2025 | Examen de código y certificación | + +## Mantente al día + +Si deseas seguir el curso, sigue [The Reasoning Course](https://huggingface.co/reasoning-course) y ¡únete a la [comunidad de Discord](https://discord.gg/F3vZujJH)!