Ir al contenido principal

Ajuste fino de GPT-OSS

Guía paso a paso para ajustar el modelo OpenAI GPT-OSS 20B en el conjunto de datos de preguntas y respuestas médicas, tanto en precisión como en adopción de estilo.
Actualizado 27 ago 2025  · 6 min de lectura

El GPT-OSS 20B es un modelo de lenguaje de peso abierto de la serie gpt-oss de OpenAI, diseñado para casos de uso especializados y de baja latencia. Cuenta con 21 000 millones de parámetros (3600 millones activos) y está optimizado para funcionar de manera eficiente en hardware de consumo, ya que solo requiere unos 16 GB de memoria gracias ala cuantificación MXFP4 ( ).

A pesar de ser más pequeño que el GPT-OSS 120B, sigue admitiendo funciones avanzadas como niveles de razonamiento configurables (bajo, medio, alto), acceso completo a la cadena de pensamiento para la depuración y la transparencia, y capacidades agenticas como llamada de funciones, navegación web y ejecución de código.

En esta guía, te guiaré a través del proceso completo de configuración y ajuste de GPT-OSS 20B. Esto incluye:

  1. Instalación de dependencias y preparación del entorno
  2. Carga del modelo y del tokenizador
  3. Creación del estilo de indicaciones utilizando el nuevo paquete OpenAI Harmony Python.
  4. Carga del conjunto de datos y aplicación del formato Harmony
  5. Ejecutar una inferencia de modelo simple antes del ajuste fino.
  6. Configuración del modelo para el entrenamiento
  7. Ajuste del modelo en un conjunto de datos de preguntas y respuestas médicas.
  8. Realizar inferencias tras el ajuste para evaluar las mejoras.

Recuerda que este tutorial está destinado a la investigación y la educación, y no al diagnóstico ni al tratamiento de pacientes.

Imagen del autor

1. Configuración del entorno

Aunque el modelo GPT-OSS tiene 21 000 millones de parámetros, sigue requiriendo una memoria GPU considerable, al menos 80 GB de VRAM para manejar el modelo cómodamente. Una forma rápida de empezar es lanzando una instancia RunPod H100 o un entorno GPU similar con mucha memoria.

Máquina RunPod H100SXM

Una vez que el entorno esté listo, instala todos los paquetes Python necesarios para el procesamiento de datos, el ajuste de modelos y el programa de experimentos:

%%capture
%pip install -U accelerate 
%pip install -U peft 
%pip install -U trl 
%pip install -U bitsandbytes
%pip install -U transformers
%pip install -U trackio
%pip install -U openai-harmony
%pip install -U tiktoken
%pip install -U pyctcdecode

Por último, inicia sesión en Hugging Face Hub con tu clave API. Esto te permitirá guardar y compartir el modelo ajustado y el tokenizador directamente en tu cuenta de Hugging Face:

from huggingface_hub import notebook_login

notebook_login()

2. Cargando el modelo GPT-OSS y el tokenizador

A continuación, cargamos el GPT-OSS 20B con cuantificación MXFP4, pero inmediatamente descuantificamos los pesos a bfloat16 al cargarlos, utilizando atención anticipada, caché desactivada y colocación automática de dispositivos.

import torch
from transformers import AutoModelForCausalLM, Mxfp4Config

quantization_config = Mxfp4Config(dequantize=True)
model_kwargs = dict(
    attn_implementation="eager",
    torch_dtype=torch.bfloat16,
    quantization_config=quantization_config,
    use_cache=False,
    device_map="auto",
)

model = AutoModelForCausalLM.from_pretrained("openai/gpt-oss-20b", **model_kwargs)

Además del modelo, también necesitamos el tokenizador del mismo repositorio. El tokenizador se encarga de convertir el texto en tokens (identificadores) que el modelo puede procesar y, a continuación, descodificar los resultados del modelo de nuevo en texto legible para los humanos.

from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("openai/gpt-oss-20b")

3. Creación de estilos de prompt con OpenAI Harmony

OpenAI Harmony es un formato de respuesta especializado utilizado por los modelos GPT-OSS para estructurar conversaciones, resultados de razonamiento y llamadas a funciones de manera coherente. 

Define cómo se organizan los mensajes entre las diferentes funciones (sistema, programadores, usuario, asistente) y permite que el modelo genere resultados a través de múltiples canales, por ejemplo, separando el razonamiento de la cadena de pensamiento, los preámbulos de llamada a herramientas y las respuestas finales dirigidas al usuario.

En esta sección, crearemos una función que tomará una pregunta y una respuesta y las formateará en un mensaje de conversación al estilo Harmony. Utiliza la biblioteca openai_harmony para codificar la conversación de forma estructurada.

from openai_harmony import (
    load_harmony_encoding, HarmonyEncodingName,
    Conversation, Message, Role,
    SystemContent, DeveloperContent, ReasoningEffort
)

enc = load_harmony_encoding(HarmonyEncodingName.HARMONY_GPT_OSS)

def render_pair_harmony(question, answer):
    convo = Conversation.from_messages([
        Message.from_role_and_content(
            Role.DEVELOPER,
            DeveloperContent.new().with_instructions(
                "You are a medical expert with advanced knowledge in clinical reasoning and diagnostics. "
                "Respond with ONLY the final diagnosis/cause in ≤5 words."
            )
        ),
        Message.from_role_and_content(Role.USER, question.strip()),
        Message.from_role_and_content(Role.ASSISTANT, answer.strip()),
    ])
    tokens = enc.render_conversation(convo)
    text = enc.decode(tokens)
    return text

4. Carga y formateo del conjunto de datos

Ahora cargaremos el conjunto de datos de preguntas y respuestas médicas (FreedomIntelligence/medical-o1-verifiable-problem ) y aplicaremos la función prompt_style_harmony a cada ejemplo. El texto transformado se almacena en una nueva columna llamada «texto», lo que proporciona un formato limpio y basado en instrucciones que se ajusta al estilo de las indicaciones de los programadores.

from datasets import load_dataset

def prompt_style_harmony(examples):
    qs = examples["Open-ended Verifiable Question"]
    ans = examples["Ground-True Answer"]
    outputs = {"text": []}
    for q, a in zip(qs, ans):
        rendered = render_pair_harmony(q, a)
        outputs["text"].append(rendered)
    return outputs

dataset = load_dataset(
    "FreedomIntelligence/medical-o1-verifiable-problem",
    split="train[0:1000]"
)
dataset = dataset.map(prompt_style_harmony, batched=True)
print(dataset.column_names)

print(dataset[0]["text"])

El conjunto de datos ahora contiene una nueva columna «texto», que contiene la conversación con formato Harmony. Esto incluye:

  • Instrucciones para programadores (por ejemplo, responder de forma concisa con un diagnóstico)
  • Mensaje del usuario (la pregunta médica)
  • Mensaje del asistente (la respuesta correcta)
['Open-ended Verifiable Question', 'Ground-True Answer', 'text']
<|start|>developer<|message|># Instructions

You are a medical expert with advanced knowledge in clinical reasoning and diagnostics. Respond with ONLY the final diagnosis/cause in ≤5 words.<|end|><|start|>user<|message|>An 88-year-old woman with osteoarthritis is experiencing mild epigastric discomfort and has vomited material resembling coffee grounds multiple times. Considering her use of naproxen, what is the most likely cause of her gastrointestinal blood loss?<|end|><|start|>assistant<|message|>Gastric ulcer<|end|>

5. Inferencia del modelo antes del ajuste fino

Antes de entrenar el modelo, es importante evaluar su rendimiento de referencia para compararlo después del ajuste fino. Para ello, definimos una función auxiliar render_inference_harmony que prepara un mensaje al estilo Harmony que contiene las instrucciones estrictas de los programadores y la pregunta del usuario, pero deja la respuesta del asistente en blanco para que el modelo la complete.

def render_inference_harmony(question):
    convo = Conversation.from_messages([
        Message.from_role_and_content(
            Role.DEVELOPER,
            DeveloperContent.new().with_instructions(
                "You are a medical expert with advanced knowledge in clinical reasoning and diagnostics. "
                "Respond with ONLY the final diagnosis/cause in ≤5 words."
            )
        ),
        Message.from_role_and_content(Role.USER, question.strip()),
    ])
    tokens = enc.render_conversation_for_completion(convo, Role.ASSISTANT)
    text = enc.decode(tokens)
    return text

A continuación, tomamos la primera muestra del conjunto de datos, la formateamos utilizando esta función, la tokenizamos y la pasamos por el modelo para generar una respuesta:

question = dataset[0]["Open-ended Verifiable Question"]

text = render_inference_harmony(question)

inputs = tokenizer(
    [text + tokenizer.eos_token], return_tensors="pt"
).to("cuda")
outputs = model.generate(
    input_ids=inputs.input_ids,
    attention_mask=inputs.attention_mask,
    max_new_tokens=20,
    eos_token_id=tokenizer.eos_token_id,
    use_cache=True,
)
response = tokenizer.batch_decode(outputs)
print(response[0])

En esta prueba, el resultado del modelo es notablemente más largo y descriptivo de lo solicitado, a pesar de que las instrucciones de los programadores pedían explícitamente un diagnóstico conciso (≤5 palabras). 

<|start|>developer<|message|># Instructions

You are a medical expert with advanced knowledge in clinical reasoning and diagnostics. Respond with ONLY the final diagnosis/cause in ≤5 words.<|end|><|start|>user<|message|>An 88-year-old woman with osteoarthritis is experiencing mild epigastric discomfort and has vomited material resembling coffee grounds multiple times. Considering her use of naproxen, what is the most likely cause of her gastrointestinal blood loss?<|end|><|start|>assistant<|return|>We have an old woman with NSAID use causing ulcer bleed. The question: "An 88

En lugar de responder simplemente «Úlcera gástrica» (la etiqueta de verdad fundamental), el modelo generó una explicación detallada sobre el uso de AINE y la hemorragia por úlcera.

dataset[0]["Ground-True Answer"]
'Gastric ulcer'

6. Configuración del modelo para la formación

Para ajustar el modelo de manera eficiente, utilizamos LoRA (Low-Rank Adaptation), que nos permite entrenar solo un pequeño subconjunto de parámetros mientras mantenemos congelado el modelo base. Esto reduce drásticamente el uso de memoria y el costo de formación.

Comenzamos definiendo un adaptador de red ( LoraConfig ) que especifica el rango, el factor de escala y las capas objetivo específicas donde se insertarán los adaptadores LoRA.

from peft import LoraConfig, get_peft_model

peft_config = LoraConfig(
    r=8,
    lora_alpha=16,
    target_modules="all-linear",
    target_parameters=[
        "7.mlp.experts.gate_up_proj",
        "7.mlp.experts.down_proj",
        "15.mlp.experts.gate_up_proj",
        "15.mlp.experts.down_proj",
        "23.mlp.experts.gate_up_proj",
        "23.mlp.experts.down_proj",
    ],
)
peft_model = get_peft_model(model, peft_config)
peft_model.print_trainable_parameters()

Solo hay unos 15 millones de parámetros (≈0,07 % del modelo completo) que se pueden entrenar, y el tamaño del adaptador es de unos 16 MB, lo que supone una enorme ganancia de eficiencia en comparación con el ajuste fino completo.

trainable params: 15,040,512 || all params: 20,929,797,696 || trainable%: 0.0719

A continuación, configuramos la configuración de entrenamiento utilizando SFTConfig de la biblioteca trl. Esto incluye hiperparámetros como la tasa de aprendizaje, el tamaño del lote, la acumulación de gradientes, la relación de calentamiento y el tipo de programador:

from trl import SFTConfig

training_args = SFTConfig(
    learning_rate=2e-4,
    gradient_checkpointing=True,
    num_train_epochs=1,
    logging_steps=10,
    per_device_train_batch_size=4,
    gradient_accumulation_steps=4,
    max_length=2048,
    warmup_ratio=0.03,
    lr_scheduler_type="cosine_with_min_lr",
    lr_scheduler_kwargs={"min_lr_rate": 0.1},
    output_dir="gpt-oss-20b-medical-qa",
    report_to="trackio",
    push_to_hub=True,
)

7. Entrenamiento del modelo

Con el conjunto de datos preparado, el adaptador LoRA conectado y los argumentos de entrenamiento configurados, ya podemos iniciar el proceso de ajuste fino. Utilizamos el pa quete « SFTTrainer » de la biblioteca « trl », que simplifica el ajuste supervisado al gestionar el modelo, el tokenizador, el conjunto de datos y el bucle de entrenamiento en un solo lugar:

from trl import SFTTrainer

trainer = SFTTrainer(
    model=peft_model,
    args=training_args,
    train_dataset=dataset,
    processing_class=tokenizer,
)
trainer.train()

Durante el entrenamiento, la pérdida disminuye gradualmente, lo que indica que el modelo está aprendiendo a alinear sus respuestas con el conjunto de datos de preguntas y respuestas médicas con formato Harmony.

Pérdida en el entrenamiento del modelo

Una vez completado el entrenamiento, guardamos el modelo ajustado localmente y luego lo enviamos al Hugging Face Hub. Es importante destacar que solo se cargan los pesos del adaptador LoRA (no el modelo 20B completo), lo que hace que el repositorio sea ligero y fácil de compartir:

trainer.save_model(training_args.output_dir)
trainer.push_to_hub(dataset_name="kingabzpro/gpt-oss-20b-medical-qa")

Esto crea un nuevo repositorio de modelos en Hugging Face en: kingabzpro/gpt-oss-20b-medical-qa.

Fuente: kingabzpro/gpt-oss-20b-medical-qa

8. Inferencia del modelo tras el ajuste fino

Tras el ajuste, podemos probar el modelo para verificar si ahora sigue más fielmente las instrucciones de los programadores. Para ello, primero cargamos el modelo base GPT-OSS 20B y, a continuación, añadimos los adaptadores LoRA ajustados.

from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel

# Load the tokenizer
tokenizer = AutoTokenizer.from_pretrained("openai/gpt-oss-20b")

# Load the original model first
model_kwargs = dict(attn_implementation="eager", torch_dtype="auto", use_cache=True, device_map="auto")
base_model = AutoModelForCausalLM.from_pretrained("openai/gpt-oss-20b", **model_kwargs).cuda()

# Merge fine-tuned weights with the base model
peft_model_id = "gpt-oss-20b-medical-qa"
model = PeftModel.from_pretrained(base_model, peft_model_id)
model = model.merge_and_unload()

A continuación, realizamos una inferencia sobre una muestra del conjunto de datos. La pregunta se formatea al estilo Harmony, se tokeniza, se pasa por el modelo y se decodifica de nuevo en texto:

question = dataset[0]["Open-ended Verifiable Question"]

text = render_inference_harmony(question)

inputs = tokenizer(
    [text + tokenizer.eos_token], return_tensors="pt"
).to("cuda")
outputs = model.generate(
    input_ids=inputs.input_ids,
    attention_mask=inputs.attention_mask,
    max_new_tokens=20,
    eos_token_id=tokenizer.eos_token_id,
    use_cache=True,
)
response = tokenizer.batch_decode(outputs)
print(response[0])

El modelo ajustado ahora produce una respuesta breve y precisa. Esto se acerca mucho más a la etiqueta de la verdad fundamental («Úlcera gástrica») y cumple la restricción de ≤5 palabras.

<|start|>developer<|message|># Instructions

You are a medical expert with advanced knowledge in clinical reasoning and diagnostics. Respond with ONLY the final diagnosis/cause in ≤5 words.<|end|><|start|>user<|message|>An 88-year-old woman with osteoarthritis is experiencing mild epigastric discomfort and has vomited material resembling coffee grounds multiple times. Considering her use of naproxen, what is the most likely cause of her gastrointestinal blood loss?<|end|><|start|>assistant<|return|><|message|>Stomach ulcer<|end|><|return|>

También podemos analizar otra muestra para confirmar la consistencia:

question = dataset[10]["Open-ended Verifiable Question"]

text = render_inference_harmony(question)

inputs = tokenizer(
    [text + tokenizer.eos_token], return_tensors="pt"
).to("cuda")
outputs = model.generate(
    input_ids=inputs.input_ids,
    attention_mask=inputs.attention_mask,
    max_new_tokens=20,
    eos_token_id=tokenizer.eos_token_id,
    use_cache=True,
)
response = tokenizer.batch_decode(outputs)
print(response[0])

Perfecto. El modelo ajustado ha generado respuestas concisas, precisas y que siguen las instrucciones.

<|start|>developer<|message|># Instructions

You are a medical expert with advanced knowledge in clinical reasoning and diagnostics. Respond with ONLY the final diagnosis/cause in ≤5 words.<|end|><|start|>user<|message|>What substance is primarily filtered in the renal tubules with minimal secretion or re-absorption?<|end|><|start|>assistant<|return|><|message|>creatinine<|end|><|return|>

Conclusión

Son tiempos emocionantes para la comunidad de IA de código abierto. Aunque los nuevos modelos GPT-OSS aún no alcancen los primeros puestos en las pruebas comparativas, son muy prometedores, especialmente en sus respectivas clases de parámetros, donde los modelos más pequeños pueden seguir ofreciendo resultados impresionantes.

En este tutorial, hemos ajustado el modelo GPT-OSS 20B para responder preguntas médicas utilizando adaptadores LoRA y el estilo de indicaciones Harmony. Los resultados son alentadores: el modelo ajustado produce respuestas concisas y precisas alineadas con las tareas de razonamiento clínico. Con más épocas de entrenamiento, conjuntos de datos más grandes y indicaciones del sistema más refinadas, el rendimiento puede mejorarse aún más.

Si estás buscando un curso práctico para familiarizarte con el ajuste fino, no dejes de consultar Ajuste fino con Llama 3.


Abid Ali Awan's photo
Author
Abid Ali Awan
LinkedIn
Twitter

Soy un científico de datos certificado que disfruta creando aplicaciones de aprendizaje automático y escribiendo blogs sobre ciencia de datos. Actualmente me centro en la creación de contenidos, la edición y el trabajo con grandes modelos lingüísticos.

Temas

Mejores cursos de OpenAI

Programa

Fundamentos de OpenAI

0 min
Empieza a crear sistemas de IA utilizando modelos de OpenAI. Aprende a utilizar la API de OpenAI para solicitar los modelos GPT y Whisper de OpenAI.
Ver detallesRight Arrow
Comienza el curso
Ver másRight Arrow
Relacionado

blog

¿Qué es GPT-4 y por qué es importante?

OpenAI ha anunciado el lanzamiento de su último gran modelo lingüístico, GPT-4. Este modelo es un gran modelo multimodal que puede aceptar tanto entradas de imagen como de texto y generar salidas de texto.
Abid Ali Awan's photo

Abid Ali Awan

9 min

An avian AI exits its cage

blog

12 alternativas de código abierto a GPT-4

Alternativas de código abierto a GPT-4 que pueden ofrecer un rendimiento similar y requieren menos recursos informáticos para funcionar. Estos proyectos vienen con instrucciones, fuentes de código, pesos del modelo, conjuntos de datos e IU de chatbot.
Abid Ali Awan's photo

Abid Ali Awan

9 min

blog

Todo lo que sabemos sobre GPT-5

Descubre cómo GPT-5 evolucionará hasta convertirse en un sistema unificado con funciones avanzadas, cuyo lanzamiento está previsto para el verano de 2025, basándose en la última hoja de ruta de OpenAI y en la historia de GPT.
Josep Ferrer's photo

Josep Ferrer

8 min

Tutorial

Ajuste fino de GPT-3 mediante la API OpenAI y Python

Libere todo el potencial de GPT-3 mediante el ajuste fino. Aprenda a utilizar la API de OpenAI y Python para mejorar este modelo de red neuronal avanzado para su caso de uso específico.
Zoumana Keita 's photo

Zoumana Keita

Tutorial

Cómo ajustar GPT 3.5: Liberar todo el potencial de la IA

Explore GPT-3.5 Turbo y descubra el potencial transformador del ajuste fino. Aprenda a personalizar este modelo de lenguaje avanzado para aplicaciones especializadas, mejore su rendimiento y comprenda los costes asociados, la seguridad y las consideraciones de privacidad.
Moez Ali's photo

Moez Ali

Tutorial

Ajuste fino de LLaMA 2: Guía paso a paso para personalizar el modelo de lenguaje grande

Aprende a ajustar Llama-2 en Colab utilizando nuevas técnicas para superar las limitaciones de memoria y computación y hacer más accesibles los grandes modelos lingüísticos de código abierto.
Abid Ali Awan's photo

Abid Ali Awan

Ver másVer más