Guía de fine-tuning de LLM: cuándo hacerlo, LoRA y QLoRA

Traducción automática

Este artículo se tradujo automáticamente a partir de la versión original en inglés.

Esta guía está dirigida a ingenieros de Python que deben decidir si hacer fine-tuning de un large language model y cómo hacerlo. Aprenderás cuándo elegir fine-tuning en lugar de prompting o retrieval, y cómo escoger entre métodos como LoRA y QLoRA. La guía también explica cómo preparar los datos, evaluar el modelo entrenado y planificar su despliegue.

La parte práctica ejecuta un repositorio complementario que hace fine-tuning de un modelo de 4B para function calling.

Para una comparación más breve entre fine-tuning, RAG y prompting, consulta Fine-Tuning vs RAG vs Prompting.

¿Deberías hacer fine-tuning?

Antes de gastar horas de GPU, decide si el fine-tuning es la herramienta adecuada para el problema que tienes delante. La figura sigue una petición y muestra dónde actúa cada una de las cuatro soluciones, en qué fallo conviene probarla primero y qué no puede arreglar.

Una petición pasa por el prompt, la ventana de contexto, los pesos del modelo y la elección del siguiente token; prompting, retrieval, fine-tuning y constrained decoding actúan cada uno en uno de estos puntos, y solo el fine-tuning cambia los pesosUna petición pasa por el prompt, la ventana de contexto, los pesos del modelo y la elección del siguiente token; prompting, retrieval, fine-tuning y constrained decoding actúan cada uno en uno de estos puntos, y solo el fine-tuning cambia los pesos

El fine-tuning modifica el comportamiento codificado en los pesos. El retrieval aporta evidencia en tiempo de inferencia, por lo que es un mejor punto de partida para hechos que cambian o que necesitan citas. Los documentos nuevos solo están disponibles tras ingerirlos e indexarlos; comprueba que esa frescura cumple tu requisito.

Los prompts claros y los ejemplos son más baratos de iterar que una nueva ejecución de entrenamiento. Puede que ya resuelvan un problema de estilo o de seguimiento de instrucciones. El fine-tuning puede reducir los tokens repetidos del prompt, pero la latencia de serving depende del modelo, la longitud de salida, el hardware y la implementación; una actualización de pesos no es, por sí misma, una aceleración.

Para la sintaxis, librerías como xgrammar y outlines enmascaran los siguientes tokens no válidos según un schema o una gramática compatibles. Una generación restringida completada con éxito pertenece a ese lenguaje. Trata por separado las truncaciones y otras generaciones incompletas, y valida el resultado antes de usarlo. La restricción no puede hacer que un ID de cliente sea verdadero ni que una elección de herramienta sea adecuada. Mide su latencia en tu backend en lugar de suponer un overhead fijo.

Cada una de las cuatro opciones cambia un paso distinto de la misma petición. Solo el fine-tuning necesita una ejecución de entrenamiento y un nuevo artefacto de modelo; las otras tres cambian el texto, los documentos o un schema alrededor de los mismos pesos. La tabla compara las cuatro opciones:

OpciónQué cambiaCómo se actualizaCoste de puesta en marchaQué no puede arreglar
PromptingInstrucciones y ejemplosEditar el textoBajo: escribir y probar promptsUn comportamiento que el modelo no puede seguir por instrucciones
RAGEvidencia en la ventana de contextoIngerir y reindexar documentosMedio: índice, retriever y evaluaciónUn modelo que ignora o malinterpreta la evidencia
Constrained decodingQué tokens puede emitir el decoderCambiar el schema o la gramáticaBajo a medio: schema y compatibilidad del backendValores incorrectos dentro de una estructura válida
Fine-tuningPesos del modeloReentrenar y evaluar un nuevo artefactoAlto: curación de datos, tiempo de GPU y evaluaciónHechos que cambian después del entrenamiento

Referencia rápida: relacionar problemas y soluciones

RetoPrimer mecanismo que probar¿Por qué?
Falta de conocimientoRAGRecupera evidencia con citas; la frescura depende de la ingesta y la indexación
Formato o tono incorrectosPrompt EngineeringLos modelos modernos siguen bien las instrucciones de estilo mediante ejemplos few-shot
Sintaxis de salida no válidaConstrained decodingImpone un schema o una gramática compatibles durante la generación
Fallos repetidos en una tareaFine-tuning (SFT)Aprende de ejemplos seleccionados de entrada/salida
Desajuste de preferencias por paresPreference optimizationUsa ejemplos elegidos/rechazados cuando el comportamiento de la tarea ya se puede medir
Latencia/coste a escalaDistillation (SFT)Entrena un modelo estudiante más pequeño con las salidas de un teacher mayor
Reducir el tamaño del modeloPost-training quantizationLa cuantización posterior al entrenamiento reduce la memoria de los pesos; la velocidad depende de los kernels compatibles y de la carga

Haz que el caso de negocio sea medible

El fine-tuning puede reducir los tokens recurrentes del prompt o permitir que un modelo más pequeño alcance el objetivo, pero ninguno de los dos ahorros es automático. Calcula el punto de equilibrio con tu propio tráfico y tus precios:

break-even requests=training + evaluation + deployment costbaseline cost/request−tuned cost/request\text{break-even requests} = \frac{\text{training + evaluation + deployment cost}} {\text{baseline cost/request} - \text{tuned cost/request}}

Si el denominador es pequeño, negativo o se basa en una suposición de calidad sin demostrar, el proyecto todavía no tiene un caso económico.

Un ejemplo con cifras ilustrativas: un baseline con prompt envía unos 2.000 tokens de instrucciones y ejemplos con cada petición. Tras el ajuste, la misma tarea necesita unos 50. Con un precio supuesto de $0,50 por millón de tokens de entrada para ambos modelos, el ahorro es de 1.950 tokens, es decir, unos $0,001 por petición. Si el entrenamiento, la evaluación y el despliegue cuestan $3.000, el punto de equilibrio está en unos 3,1 millones de peticiones. Con 100.000 peticiones al día, eso es aproximadamente un mes. Con 5.000 peticiones al día, es más de un año. El cálculo solo vale si el modelo ajustado cumple los mismos umbrales de calidad que el baseline.

La configuración híbrida. Una arquitectura habitual combina un modelo más pequeño adaptado a la tarea con retrieval para los hechos cambiantes. Trata el modelo mayor con prompt como baseline y conserva el modelo más pequeño solo si cumple los mismos umbrales de calidad y seguridad específicos de la tarea.


Tipos de fine-tuning

El fine-tuning puede adoptar tres formas principales. Se distinguen por el tipo de datos que necesitan y por lo que enseñan al modelo. El diagrama muestra una fila de entrenamiento para cada tipo.

El continued pre-training lee notas clínicas sin procesar; SFT lee pares de entrada y salida de texto a SQL; el instruction tuning lee pares de instrucción y respuesta de muchas tareas; SFT y el instruction tuning comparten la pérdida del siguiente tokenEl continued pre-training lee notas clínicas sin procesar; SFT lee pares de entrada y salida de texto a SQL; el instruction tuning lee pares de instrucción y respuesta de muchas tareas; SFT y el instruction tuning comparten la pérdida del siguiente token

1. Continued pre-training (self-supervised)

Entrenas el modelo base con más texto sin etiquetar, usando el siguiente token de cada secuencia como objetivo de entrenamiento. Eso es aprendizaje autosupervisado: el propio texto aporta sus objetivos, igual que en la ejecución original de pre-training.

Cuándo usarlo:

  • El dominio tiene vocabulario que el modelo base nunca vio (medicina, derecho, bases de código internas).
  • Dispones de mucho texto del dominio, pero no de pares (entrada, salida) etiquetados.
  • El modelo base se equivoca con la terminología específica del dominio.

Ejemplo: entrenar con millones de notas clínicas para que el modelo aprenda abreviaturas médicas, nombres de fármacos y flujos de trabajo clínicos.

2. Supervised fine-tuning (SFT)

El SFT entrena con pares (entrada, salida) etiquetados. Le muestras al modelo la salida exacta que quieres para cada entrada.

Cuándo usarlo:

  • Tienes una tarea concreta con un formato limpio de entrada/salida.
  • Tienes datos etiquetados de calidad, aunque sean pocos.
  • Necesitas un comportamiento predecible con una forma de entrada conocida.

Ejemplo: entrenar con pares (descripción de una consulta SQL, código SQL) para text-to-SQL. Este intervalo de calendario fijo supone que signup_date se almacena en UTC:

{
    "input": "Get users who signed up in August 2026 (UTC)",
    "output": "SELECT * FROM users WHERE signup_date >= '2026-08-01' AND signup_date < '2026-09-01'"
}

3. Instruction tuning

El instruction tuning es un caso especial de SFT diseñado para que los modelos sigan una gran variedad de instrucciones en lenguaje natural. Los datos de entrenamiento son pares (instrucción, respuesta) de muchas tareas distintas.

Cuándo usarlo:

  • Quieres un asistente de propósito general (como ChatGPT o Claude).
  • El modelo debe atender peticiones variadas y abiertas.
  • Estás construyendo una interfaz de chat.

Ejemplo: entrenar con miles de instrucciones diversas como “Resume este artículo”, “Escribe un poema sobre X”, “Explica Y en términos sencillos”.

Comparación

AspectoContinued pre-trainingSFTInstruction tuning
DatosTexto sin etiquetarPares (entrada, salida)Pares (instrucción, respuesta)
EtiquetasNinguna (autosupervisado)Específicas de la tareaTareas diversas
ObjetivoConocimiento del dominioComportamiento específico de la tareaSeguir cualquier instrucción
Volumen de datosNormalmente el corpus más grandeLo determinan la cobertura de la tarea y la diversidad de erroresNormalmente más amplio que el SFT específico de una tarea

Un objetivo, dos datasets. El SFT y el instruction tuning usan el mismo objetivo de predicción del siguiente token; la diferencia está en la amplitud y la construcción del dataset. El continued pre-training es un experimento aparte, y después hay que probar tanto la mejora en el dominio como la regresión de las capacidades generales.


El pipeline de fine-tuning en 7 etapas

El fine-tuning tiene siete etapas. Cada etapa tiene sus propios modos de fallo, y saltarse una suele acabar en un mal modelo más adelante. El diagrama muestra las etapas en orden, lo que cada etapa añade al registro de la versión y el camino de vuelta desde la monitorización hasta la preparación de datos.

Siete etapas, desde la preparación de datos hasta la monitorización; las etapas uno a cinco añaden al registro de la versión la versión del dataset, la revisión del modelo base, el tokenizer, la plantilla de chat y la configuración de entrenamiento, el hash del adapter y el informe de evaluación; la monitorización carga la versión anterior cuando se supera el umbral de rollback y devuelve los hallazgos a la etapa 1Siete etapas, desde la preparación de datos hasta la monitorización; las etapas uno a cinco añaden al registro de la versión la versión del dataset, la revisión del modelo base, el tokenizer, la plantilla de chat y la configuración de entrenamiento, el hash del adapter y el informe de evaluación; la monitorización carga la versión anterior cuando se supera el umbral de rollback y devuelve los hallazgos a la etapa 1

Cada etapa se apoya en la anterior:

  1. Preparación de datos — Define la unidad de evaluación, divide los datos y luego límpialos y dales formato
  2. Selección del modelo — Elige el modelo base adecuado y carga los pesos
  3. Configuración del entrenamiento — Comprueba la serialización y los objetivos de la loss, y luego elige el método (LoRA, QLoRA, DoRA) y sus ajustes
  4. Fine-tuning — Ejecuta SFT con un framework; añade DPO u ORPO solo si hace falta
  5. Evaluación — Mide el rendimiento y valida la calidad
  6. Despliegue — Exporta y sirve tu modelo
  7. Monitorización — Sigue el rendimiento, mantén el modelo e itera

Los datos primero. El entrenamiento reproduce los fallos sistemáticos de los ejemplos. Inspecciona las etiquetas, las fugas, la cobertura y el cumplimiento de políticas antes de dedicar tiempo a barridos del optimizador.


Etapa 1: Preparación de datos

Los problemas de los datos llegan al modelo entrenado, así que esta etapa recibe el mayor cuidado. El diagrama sigue tickets de ejemplo por ese orden: primero se divide por origen y después se aplican cinco transformaciones dentro de cada partición.

Los tickets y sus casi duplicados van enteros a entrenamiento o evaluación; el filtrado y el tratamiento de datos sensibles se aplican en ambas particiones; la deduplicación elimina los casi duplicados; el aumento añade reescrituras solo a entrenamiento, y una reescritura en evaluación filtraría su ticket de origenLos tickets y sus casi duplicados van enteros a entrenamiento o evaluación; el filtrado y el tratamiento de datos sensibles se aplican en ambas particiones; la deduplicación elimina los casi duplicados; el aumento añade reescrituras solo a entrenamiento, y una reescritura en evaluación filtraría su ticket de origen

Cinco transformaciones después de dividir por origen

Asigna los grupos de origen a entrenamiento o evaluación antes de transformar las filas. Un ticket de soporte y todas sus reescrituras permanecen en la misma partición. Agrupa los casi duplicados conocidos antes de asignar los grupos y vuelve a comprobarlo entre particiones tras la limpieza. Congela el conjunto de evaluación; aumenta solo los datos de entrenamiento.

Enumera los errores que tus ejemplos deben enseñar al modelo a corregir y define después los campos y etiquetas obligatorios de cada fila. Elige las herramientas de procesamiento para esos pasos. DataTrove ejecuta el filtrado y la deduplicación como trabajos distribuidos para corpus grandes; Distilabel genera y etiqueta filas sintéticas con un modelo teacher.

1. Ingesta y filtrado

  • Acción: elimina el UTF-8 roto y el contenido ajeno a la tarea. Etiqueta las negativas según la política: conserva las negativas correctas como objetivos de entrenamiento y casos de evaluación; excluye solo las negativas no válidas o ajenas a la tarea.
  • Herramientas: Trafilatura cuando la fuente son páginas web; omítela si los datos ya son texto. Los modelos de identificación de idioma de fastText para detectar el idioma.

2. Política de datos sensibles

  • Acción: decide qué puede aprender el modelo y después oculta, tokeniza o excluye los campos personales y confidenciales según corresponda.
  • Herramientas: Microsoft Presidio cuando necesitas reconocedores configurables, varios idiomas o anonimización reversible; scrubadub para un pipeline más pequeño en Python puro que elimina del texto los datos personales habituales.
  • Por qué: un detector es solo un control; siguen aplicándose los requisitos de procedencia, consentimiento, retención, acceso y eliminación.

3. Deduplicación (MinHash LSH)

  • Acción: elimina los casi duplicados para reducir la repetición y el riesgo de fugas; esto no evita la memorización.
  • Herramientas: DataTrove cuando el corpus necesita procesamiento distribuido.

4. Aumento sintético, si es necesario

  • Acción: usa un modelo teacher adecuado para reescribir los ejemplos de entrenamiento como pares instrucción-respuesta, conservando sus ID de origen. No aumentes la partición reservada para evaluación.
  • Herramientas: Distilabel.
  • Validación: muestrea las salidas del teacher, compáralas con la misma rúbrica que las etiquetas humanas e informa por separado de los resultados de los ejemplos sintéticos y de los escritos por personas. Un subconjunto de evaluación así se llama slice; otros slices útiles son el idioma y el tipo de tarea.

5. Formato

  • Acción: convierte a un formato estándar (Alpaca o ShareGPT).

Ejemplos de formatos de datos

Formato Alpaca (seguimiento de instrucciones):

{
    "instruction": "Summarize the following text.",
    "input": "The text to be summarized...",
    "output": "This is the summary."
}

Almacenamiento al estilo ShareGPT (conversacional):

{
    "conversations": [
        { "from": "user", "value": "Hello, who are you?" },
        { "from": "assistant", "value": "I am a helpful AI assistant." }
    ]
}

Comprobaciones del dataset

  • Cobertura antes que volumen. Añade ejemplos que representen modos de fallo distintos, no repeticiones del caso fácil mayoritario.
  • Limpieza. Elimina el texto irrelevante, normaliza los espacios en blanco y mantén un formato coherente.
  • Equilibrio. Conserva los casos raros importantes e informa del rendimiento por slice.
  • Separación. Divide por origen, usuario, documento o tiempo cuando una división aleatoria por filas filtraría casi duplicados.
  • Procedencia. Registra el origen, la licencia o el permiso, el historial de transformaciones y la vía de eliminación de cada versión del dataset.

Etapa 2: Selección del modelo y hardware

El modelo base y su requisito mínimo de memoria de GPU determinan lo que puedes entrenar.

Empieza por el modelo base más pequeño que ya supere las comprobaciones de referencia irrenunciables. Confirma:

  • la licencia y las condiciones de redistribución para el producto previsto;
  • el idioma, el dominio, el uso de herramientas y el comportamiento de seguridad antes de la adaptación;
  • la compatibilidad del tokenizer y la chat template con el dataset;
  • el contexto máximo y el comportamiento de truncación que necesitan los ejemplos reales;
  • la compatibilidad tanto en el framework de entrenamiento como en el motor de serving de destino.

El fine-tuning es un paso de adaptación, no una reparación para un modelo base inadecuado. Si el modelo falla en capacidades que el dataset no cubre, elige otro modelo base antes de ejecutar más epochs.

Estima la memoria de tu ejecución

No existe una tabla duradera de “tamaño del modelo → GPU”. La memoria pico cambia con la precisión de los pesos, el optimizador, el número de parámetros entrenables, la longitud de secuencia, el tamaño del micro-batch, el activation checkpointing, la implementación de la atención y el overhead del framework. Empieza con una estimación de memoria y después ejecuta una prueba rápida con longitud máxima en tu stack exacto.

Componente de memoriaFine-tuning completoLoRAQLoRA
Pesos basePrecisión de entrenamientoCongelados, normalmente BF16/FP16Congelados, normalmente NF4 de 4 bits
GradientesTodos los pesos entrenablesPesos del adapterPesos del adapter
Estados del optimizadorTodos los pesos entrenablesPesos del adapterPesos del adapter
ActivacionesDependen del tamaño del batch y de la longitud de secuencia en todos los métodosLa misma dependenciaLa misma dependencia

El gráfico aplica la tabla al modelo base de la demo, Nemotron-Nano-4B con 4,51 mil millones de parámetros. Desglosa la memoria aproximada de cada método en pesos y estado entrenable; las activaciones se suman encima.

El fine-tuning completo necesita unos 54 GB: 9 GB de pesos, 9 GB de gradientes y 36 GB de estados de Adam; LoRA necesita unos 9,5 GB y QLoRA unos 4 GB, porque su estado entrenable ronda los 0,5 GBEl fine-tuning completo necesita unos 54 GB: 9 GB de pesos, 9 GB de gradientes y 36 GB de estados de Adam; LoRA necesita unos 9,5 GB y QLoRA unos 4 GB, porque su estado entrenable ronda los 0,5 GB

El artículo original de QLoRA logró que un modelo LLaMA de 65B cupiera en una sola GPU de 48 GB en su configuración concreta. Ese resultado es una cota útil, no una promesa de que cualquier arquitectura actual de 70B, longitud de contexto, kernel o trainer quepa en el mismo dispositivo.

Cálculo de memoria

Para un modelo con PP parámetros, los pesos por sí solos requieren aproximadamente 2P2P bytes en BF16/FP16 o 0.5P0.5P bytes con cuatro bits, antes de los metadatos de cuantización y los buffers de ejecución. El entrenamiento completo con un optimizador de tipo Adam añade gradientes, estados del optimizador y, a menudo, pesos maestros de mayor precisión. LoRA evita la mayor parte de la memoria del estado entrenable; QLoRA reduce además el tamaño de los pesos base congelados. Las activaciones aún pueden dominar con secuencias largas.

Usa este flujo de trabajo:

  1. Elige la secuencia más larga y el micro-batch que debes admitir.
  2. Estima los pesos y el estado entrenable, dejando margen para activaciones y kernels.
  3. Ejecuta un paso de forward/backward con la longitud máxima.
  4. Registra la memoria pico asignada y reservada.
  5. Solo entonces escala el tamaño del batch, el rank, la longitud de secuencia o el número de GPUs.

Etapa 3: Configuración y métodos de entrenamiento

La etapa 3 tiene dos partes. Primero comprueba que cada fila almacenada se convierte en el texto de entrenamiento que esperas. Después elige el método de entrenamiento y sus ajustes.

De las filas almacenadas a los objetivos de entrenamiento

ShareGPT es una forma de dataset; ChatML es una convención de serialización. Convierte from/value a los roles y campos que acepta el trainer y usa después la chat template del modelo seleccionado. No sustituyas por tokens de ChatML solo porque los datos sean conversacionales.

Una fila pasa por cinco pasos:

  1. Fila ShareGPT: pares from/value.
  2. messages y tools: los roles que acepta el trainer.
  3. apply_chat_template con la plantilla del modelo seleccionado. La inferencia debe usar la misma plantilla.
  4. IDs de tokens y etiquetas, donde -100 significa que no hay loss.
  5. Entrena.

Imprime el texto renderizado del paso 3 antes de entrenar.

Para la fila del saludo anterior, los mensajes normalizados son:

messages = [
    {"role": "user", "content": "Hello, who are you?"},
    {"role": "assistant", "content": "I am a helpful AI assistant."},
]
# Inspection step after loading the selected model's tokenizer:
rendered = tokenizer.apply_chat_template(
    messages, tokenize=False, add_generation_prompt=False,
)
print(rendered)

Inspecciona la fila serializada real antes de entrenar: los delimitadores de rol, los tokens de fin de turno y los tool schemas deben coincidir con los de la inferencia. En una fila de tool use, conserva las definiciones de herramientas, las tool calls del asistente y los tool results como campos distintos. Comprueba que la truncación no elimine la respuesta supervisada ni corte por la mitad un argumento de una herramienta.

El loss masking decide por qué tokens se recompensa al modelo. En el entrenamiento de secuencia completa, el texto del usuario, del sistema, de las herramientas y del asistente pueden contribuir todos. En el entrenamiento solo del asistente, esos tokens de contexto siguen siendo visibles para la atención, pero sus etiquetas se ignoran (normalmente -100); solo contribuyen los tramos del asistente. Para este saludo, la respuesta debería ser un objetivo y la pregunta del usuario no, si la intención es un comportamiento solo del asistente.

El diagrama muestra ambos ajustes para una conversación con tool use.

En el entrenamiento de secuencia completa todos los segmentos son objetivo; en el entrenamiento solo del asistente, solo lo son la tool call y la respuesta del asistenteEn el entrenamiento de secuencia completa todos los segmentos son objetivo; en el entrenamiento solo del asistente, solo lo son la tool call y la respuesta del asistente

La documentación de SFT de TRL admite assistant_only_loss=True para datasets conversacionales cuyas plantillas exponen tramos de generación. Una plantilla sin esos tramos no puede proporcionar esa máscara. Los datasets de prompt/completion tienen una opción propia, completion_only_loss. Antes de activar cualquiera de las dos, inspecciona un batch tokenizado y decodifica los tokens con etiquetas no ignoradas. Después repite la prueba con una conversación con herramientas y un ejemplo de longitud máxima. La demo que aparece más adelante en esta guía usa loss de secuencia completa, porque la plantilla de Nemotron no tiene tramos de generación.

Elige una longitud de secuencia a partir de la distribución de los datos y ejecuta la comprobación de memoria con longitud máxima de la etapa 2. El packing coloca varios ejemplos más cortos en una misma secuencia de entrenamiento. Empieza sin él para que los límites de las filas sean fáciles de inspeccionar. Prueba el packing solo cuando las filas serializadas y las etiquetas sean correctas.

Fine-tuning completo frente a PEFT

El fine-tuning completo (FFT) actualiza todos los pesos, así que los gradientes y el estado del optimizador escalan con todo el modelo. El pico no puede deducirse solo del número de parámetros, pero queda muy por encima de la memoria necesaria para cargar los pesos para inferencia.

El parameter-efficient fine-tuning (PEFT) entrena solo un pequeño subconjunto de parámetros y congela el resto, de modo que los gradientes y los estados del optimizador cubren solo ese subconjunto.

LoRA: el punto de partida

LoRA (Low-Rank Adaptation) congela una matriz preentrenada y representa su actualización aprendida con dos matrices más pequeñas. El artículo original lo justifica con la hipótesis de que las actualizaciones de adaptación útiles tienen un rank intrínseco bajo.

Aquí, dind_{in} es el ancho de entrada de la capa y doutd_{out} su ancho de salida. El rank rr es la dimensión compartida más pequeña de las dos matrices entrenables: un rank menor usa menos parámetros entrenables. El ajuste α\alpha controla la escala de la actualización mediante el cociente α/r\alpha/r.

El diagrama muestra la matriz congelada, las dos matrices entrenables y cómo se suma su producto escalado, con el número de parámetros de una matriz de ancho 4.096 con rango 16.

Una entrada pasa por la matriz congelada W0 de 4.096 por 4.096 y, en paralelo, por las matrices entrenables A y B; la salida de B·A se escala por alpha entre r y se suma; con rango 16, A y B suman 131.072 parámetros frente a 16.777.216 en W0Una entrada pasa por la matriz congelada W0 de 4.096 por 4.096 y, en paralelo, por las matrices entrenables A y B; la salida de B·A se escala por alpha entre r y se suma; con rango 16, A y B suman 131.072 parámetros frente a 16.777.216 en W0

Para una matriz congelada W0∈Rdout×dinW_0 \in \mathbb{R}^{d_{out} \times d_{in}}, LoRA aprende:

  • A∈Rr×dinA \in \mathbb{R}^{r \times d_{in}}
  • B∈Rdout×rB \in \mathbb{R}^{d_{out} \times r}

La capa adaptada es:

W′=W0+αrBAW' = W_0 + \frac{\alpha}{r}BA

El adapter tiene r(din+dout)r(d_{in}+d_{out}) parámetros entrenables en lugar de dindoutd_{in}d_{out} para esa matriz. Para una matriz cuadrada de ancho 4.096 con rank 16, eso supone una reducción de 128× en los parámetros entrenables de esa matriz. El ahorro en el modelo completo depende de qué matrices reciben adapters.

Comparación de métodos PEFT

MétodoQué cambiaElígelo cuando
LoRABase congelada más actualizaciones entrenables de bajo rankEl modelo base cabe con holgura y quieres artefactos pequeños por tarea
QLoRALoRA con la base congelada almacenada en forma de 4 bitsLa memoria de los pesos base es el factor limitante
DoRASepara la magnitud del peso de una dirección actualizada con LoRAUn baseline de LoRA medido deja una brecha de calidad que justifica la complejidad extra
Fine-tuning completoTodos los pesos del modeloPEFT no alcanza el objetivo y la mejora de calidad justifica el entrenamiento distribuido y los checkpoints completos

DoRA: LoRA con descomposición de pesos

DoRA (Weight-Decomposed Low-Rank Adaptation) separa la magnitud de cada vector de pesos de su dirección. El artículo de DoRA aplica una actualización LoRA al componente direccional mientras aprende la magnitud por separado. El diagrama dibuja una columna de pesos como una flecha: B·A gira su dirección y una magnitud m entrenada fija su longitud.

Una columna de pesos dibujada como una flecha cuya longitud es la magnitud y cuyo ángulo es la dirección; sumar BA gira la dirección; normalizar y multiplicar por el valor entrenado m fija la nueva longitudUna columna de pesos dibujada como una flecha cuya longitud es la magnitud y cuyo ángulo es la dirección; sumar BA gira la dirección; normalizar y multiplicar por el valor entrenado m fija la nueva longitud

Cómo funciona:

En lugar de tratar los pesos como una única entidad, DoRA descompone los pesos preentrenados en dos componentes:

  1. Magnitud: un valor entrenable por cada vector de pesos.
  2. Dirección: un vector normalizado que se actualiza mediante matrices de bajo rank.

En notación compacta por columnas:

W′=mV+BA∥V+BA∥cW' = m \frac{V + BA}{\lVert V + BA \rVert_c}

donde:

  • m = magnitud (entrenable)
  • V=W0V = W_0 = la matriz inicial congelada; se normaliza después de sumar BABA
  • BABA = la actualización direccional de bajo rank aprendida
  • ∥⋅∥c\lVert \cdot \rVert_c = normalización por columnas

Efectos de la descomposición:

  • Más grados de libertad que LoRA estándar, porque la magnitud puede cambiar de forma independiente.
  • Mejores resultados que LoRA en varias configuraciones que describe el artículo que lo propone.
  • Parámetros y cómputo adicionales, así que conviene verificar la mejora en tu tarea y en tu ruta de serving.

Fusión de adapters para aprendizaje multitarea

Los adapters separados permiten que una sola base congelada admita varias tareas. Puedes dirigir las peticiones a un adapter, servir varios adapters desde un mismo motor cuando esté soportado o crear un candidato fusionado offline. La fusión puede introducir interferencias, así que evalúa el artefacto fusionado en lugar de suponer que los adapters de origen se combinan sin problemas.

La figura compara las dos opciones para dos adapters sobre una misma base. Conserva el modelo fusionado solo si supera ambas suites de pruebas.

Servir mantiene una base en memoria y la petición indica el adapter; fusionar da un único modelo que solo se conserva si supera ambas suites de pruebasServir mantiene una base en memoria y la petición indica el adapter; fusionar da un único modelo que solo se conserva si supera ambas suites de pruebas

Métodos de fusión habituales:

  1. Concatenación: combina los parámetros de los adapters y aumenta el rank efectivo. Rápida y sencilla.
  2. Combinación lineal: suma ponderada de adapters. Los pesos fijan cuánto contribuye cada tarea.
  3. SVD: descomposición de matrices para la fusión. Más flexible, pero más lenta.

add_weighted_adapter de PEFT los implementa como combination_type="cat", "linear" y "svd"; la guía de fusión de modelos de PEFT los describe, junto con métodos más recientes.

Ejemplo: un adapter para resumen y otro para traducción, fusionados en un único modelo multitarea.


Etapa 4: Preference alignment (opcional)

El SFT es la ejecución principal de entrenamiento, y las secciones de frameworks que siguen a esta etapa la ejecutan. Esta etapa trata de lo que puedes añadir después del SFT, si los errores lo requieren.

El SFT aprende demostraciones. La preference optimization, en cambio, aprende de comparaciones como “la respuesta elegida A es mejor que la respuesta rechazada B”. Úsala solo cuando la preferencia por pares sea la etiqueta adecuada para el error; la corrección factual y el cumplimiento de políticas suelen necesitar evaluadores más sólidos que una preferencia global.

El diagrama muestra las etapas de cada método, los modelos que carga y qué hace un paso de entrenamiento.

RLHF basado en PPO tiene tres etapas y genera respuestas nuevas que un reward model puntúa; DPO tiene dos etapas y entrena con pares guardados frente a una referencia fija; ORPO entrena con pares guardados en una sola etapaRLHF basado en PPO tiene tres etapas y genera respuestas nuevas que un reward model puntúa; DPO tiene dos etapas y entrena con pares guardados frente a una referencia fija; ORPO entrena con pares guardados en una sola etapa

RLHF basado en PPO

RLHF significa reinforcement learning from human feedback. La policy es el modelo de lenguaje que genera las respuestas; un reward model puntúa esas respuestas con preferencias aprendidas de comparaciones humanas. Una receta basada en PPO tiene tres etapas:

  1. SFT: aprender la tarea.
  2. Reward model: se entrena con preferencias humanas (elegida frente a rechazada).
  3. PPO (Proximal Policy Optimization): genera respuestas y actualiza el modelo de lenguaje para aumentar sus puntuaciones de recompensa.

El coste operativo viene de los distintos componentes:

  • Es complicado de implementar y mantener.
  • Es caro: se entrenan varios modelos.
  • Muestrear del modelo que se está entrenando exige comprobar si hay actualizaciones inestables y respuestas que explotan el reward model sin cumplir la tarea.

DPO

DPO (Direct Preference Optimization) prescinde del reward model separado y del bucle de reinforcement learning. Favorece las respuestas elegidas y desalienta los cambios grandes respecto a un modelo de partida fijo, llamado modelo de referencia. El artículo de DPO deriva este objetivo limitando cuánto pueden alejarse las probabilidades de las respuestas de esa referencia; el límite matemático es una restricción de divergencia KL.

Un par de preferencia contiene el mismo prompt y dos respuestas candidatas. Esta fila abreviada muestra sus campos; las etiquetas reales necesitan respuestas completas y un motivo para preferir una:

{
    "prompt": "Explain quantum computing",
    "chosen": "Quantum computing uses qubits...",   # Preferred response
    "rejected": "Well, it's complicated..."        # Non-preferred response
}

Qué cambia en la práctica:

  • Una ruta de código más simple (sin reward model separado ni bucle de RL).
  • Entrenamiento con pares de preferencias guardados, en lugar de generar respuestas nuevas para puntuarlas con el reward model en cada actualización.
  • Un modelo de referencia fijo, o sus log probabilities guardadas para las respuestas de entrenamiento, en la formulación estándar.

DPO es más fácil de prototipar que un pipeline completo de PPO, pero no es una mejora de calidad automática. Los resultados dependen de la policy de partida, la calidad de los pares, los ajustes de la loss, los efectos de la longitud y el protocolo de evaluación. Compáralo con un checkpoint de SFT en las mismas suites de preferencias y de tareas reservadas para evaluación.

ORPO

ORPO (Odds-Ratio Preference Optimization) combina la loss de log-verosimilitud negativa del SFT con una penalización de odds ratio sobre las respuestas rechazadas. Elimina el modelo de referencia separado y puede combinar el aprendizaje de la tarea y la preference optimization en una sola ejecución.

Cómo funciona: ORPO usa una loss combinada que hace dos cosas a la vez:

  1. Maximiza la verosimilitud de la respuesta elegida (aprender la tarea).
  2. Penaliza la respuesta rechazada con un término de odds ratio (aprender preferencias).

TRL ya incluye ORPO como ORPOConfig en trl.experimental.orpo (trainer ORPO de TRL). Conviene conocer dos ajustes:

  • learning_rate: el artículo usó tasas bajas como 8e-6; ajústala según tu modelo, tu batch y tus datos en lugar de copiar un único valor como regla.
  • beta: controla el término de preferencia en relación con el término de SFT.

El compromiso:

  • Una sola etapa de entrenamiento en lugar de dos.
  • Sin reward model.
  • Sin forward pass del modelo de referencia.
  • Una ejecución acoplada: si el aprendizaje de la tarea o el comportamiento de preferencia empeoran, no hay un checkpoint intermedio de SFT del mismo pipeline que inspeccionar.

Elige según el diseño de los datos y de la evaluación:

  • Usa DPO cuando ya tienes un checkpoint de SFT satisfactorio y quieres un experimento de preferencias offline más simple.
  • Prueba ORPO cuando un objetivo de una sola etapa y sin referencia encaja con tus datos y tus restricciones operativas.
  • Usa RLHF basado en PPO cuando el muestreo online frente a una recompensa explícita aprendida forma parte del requisito y puedes vigilar la explotación de la recompensa.

Ninguno es la opción por defecto para todas las tareas. Conserva un baseline solo con SFT e informa tanto de las métricas de la tarea como de las de preferencia.


Frameworks de fine-tuning

Las etapas 3 y 4 eligieron el método. Las dos secciones siguientes eligen un framework y ejecutan SFT con él.

Los frameworks se solapan y cambian rápido. Elige según la ruta de ejecución que debas admitir, fija las versiones y mantén la configuración de entrenamiento lo bastante portable para reproducirla fuera de un notebook.

Unsloth: velocidad y eficiencia de memoria

Unsloth se integra con trl y transformers de Hugging Face y ofrece kernels optimizados, checkpointing y rutas de fine-tuning cuantizado para los modelos compatibles.

  • Kernels de GPU Triton personalizados para atención, RoPE y entropía cruzada que evitan el overhead de PyTorch.
  • Backprop eficiente en memoria que recalcula las activaciones durante el backward pass en lugar de conservarlas en memoria.
  • Operaciones fusionadas que reúnen varios pasos (layer norm + linear y similares) en llamadas únicas a la GPU.
  • Cuantización de 4 bits integrada directamente en la ruta de QLoRA, con una dequantización optimizada.

El orden de importación importa. Sigue el orden de importación del ejemplo de Unsloth para la versión que fijes. Unsloth aplica parches durante la importación, así que importarlo antes que trl y transformers evita optimizaciones ausentes o errores específicos de la versión.

# Correct order
from unsloth import FastLanguageModel  # Must be first!
from trl import SFTConfig, SFTTrainer

# Avoid this order with Unsloth's patched path
from trl import SFTTrainer
from unsloth import FastLanguageModel

Ideal para: entrenamiento con una sola GPU, prototipos y notebooks de Colab.

Las cifras publicadas de velocidad y memoria varían según el modelo, la longitud de secuencia, el batch, la precisión y el hardware. Mide los tokens por segundo y la memoria pico en tu propia ejecución en lugar de tratar una proporción destacada como una propiedad del framework.

Axolotl: entrenamiento basado en configuración

Para ejecuciones distribuidas definidas por configuración, Axolotl mantiene toda la ejecución en un único archivo YAML. Estas claves seleccionan una base de 4 bits, un adapter QLoRA y un dataset en formato Alpaca:

# axolotl_config.yaml (excerpt)
base_model: meta-llama/Meta-Llama-3-8B
load_in_4bit: true
adapter: qlora
lora_r: 32
lora_alpha: 16 # alpha/r = 0.5 here; the demo uses 2. Sweep it with the learning rate.
datasets:
    - path: data/training_data.jsonl
      type: alpaca

Los módulos objetivo, la longitud de secuencia, el packing, el tamaño del batch, la learning rate y los epochs van en el mismo archivo; la referencia de configuración enumera todas las claves. Ejecuta el entrenamiento:

axolotl train axolotl_config.yaml

Comparación de frameworks

HerramientaResulta útil cuandoVerifica antes de comprometerte
UnslothQuieres una ruta optimizada para modelos compatibles, con ejemplos concisosLa matriz de compatibilidad de modelos, GPU, cuantización y entrenamiento distribuido
AxolotlQuieres configuraciones declarativas y recetas distribuidas integradasEl schema exacto de la configuración y el launcher de la versión fijada
TRLQuieres acceso directo a los trainers de SFT y de preferencias de Hugging FaceEl formato del dataset, la chat template, el loss masking y la integración con PEFT
TorchtuneYa ejecutas recetas de torchtune; ya no se mantiene activamente (desde 2025)La cobertura de recetas de modelos y la compatibilidad de exportación

Demo práctica: fine-tuning con Unsloth

Esta demo enseña a un modelo a seleccionar una función y a proporcionar sus argumentos. Mi unsloth-finetune-demo hace fine-tuning de Llama-3.1-Nemotron-Nano-4B-v1.1 con el dataset glaive-function-calling-v2. La ruta de entrenamiento con CUDA de este repositorio requiere Linux o WSL2 y una GPU de NVIDIA. Unsloth también tiene una ruta de entrenamiento con GPU de Intel independiente que esta demo no implementa. Fija el commit que ejecutes; el archivo de bloqueo fija las dependencias, incluida TRL 0.24.0.

El diagrama muestra la ejecución completa con sus valores reales: cómo una fila de Glaive se convierte en texto de entrenamiento que conserva las definiciones de herramientas, la división, los ajustes de entrenamiento, los tres directorios de salida y la primera comprobación.

Las cadenas system y chat de una fila de Glaive pasan a tools y messages, se renderizan en texto de entrenamiento con AVAILABLE_TOOLS y TOOLCALL, se dividen 95 a 5, entrenan un adapter LoRA de rango 16 sobre una base de 4 bits y exportan un adapter, un modelo fusionado y un archivo GGUFLas cadenas system y chat de una fila de Glaive pasan a tools y messages, se renderizan en texto de entrenamiento con AVAILABLE_TOOLS y TOOLCALL, se dividen 95 a 5, entrenan un adapter LoRA de rango 16 sobre una base de 4 bits y exportan un adapter, un modelo fusionado y un archivo GGUF

Ejecútala

git clone https://github.com/slavadubrov/unsloth-finetune-demo.git
cd unsloth-finetune-demo
uv sync

# Train on 1,000 random rows, then also save a merged model and a GGUF file
uv run finetune --max-samples 1000 --merge --gguf q4_k_m

Cada ejecución de finetune entrena. --merge y --gguf añaden exportaciones después del entrenamiento; no hay un comando que solo exporte. La ejecución escribe tres directorios:

  • outputs/unsloth-nemotron-function-calling/: el adapter LoRA
  • outputs/unsloth-nemotron-function-calling-merged/: el modelo fusionado de 16 bits (--merge)
  • outputs/unsloth-nemotron-function-calling-gguf/: el archivo GGUF (--gguf q4_k_m)

Antes de que empiece el entrenamiento, la ejecución imprime la primera fila de entrenamiento tal como la ve el modelo. Léela: las definiciones de herramientas aparecen dentro de <AVAILABLE_TOOLS> y cada llamada aparece dentro de <TOOLCALL>.

Preparación de datos

Cada fila de glaive tiene dos cadenas. La columna system contiene todas las definiciones de funciones; la columna chat contiene la conversación, con las llamadas escritas como <functioncall> {...}. Entrenar solo con chat enseña al modelo a llamar a herramientas cuyos schemas nunca ve. El data.py de la demo convierte cada fila en messages y una lista tools aparte, renderiza ambos con la chat template del propio modelo y reserva el 5 % de las filas como partición de evaluación:

def to_text(example: dict, tokenizer) -> dict:
    """Render one glaive row with the model's chat template. Empty text marks a dropped row."""
    try:
        messages, tools = to_messages(example)
    except (ValueError, KeyError):  # bad JSON in a tool definition or call
        return {"text": ""}
    if not messages:
        return {"text": ""}
    text = tokenizer.apply_chat_template(messages, tools=tools or None, tokenize=False)
    # The trainer tokenizes this text with special tokens on, which adds BOS again.
    return {"text": text.removeprefix(tokenizer.bos_token or "")}

Con el dataset completo se conservan 112.811 de las 112.960 filas; 149 filas tienen JSON mal formado y se descartan. Con este tokenizer, la fila renderizada más larga tiene 4.076 tokens, así que un límite de 4.096 tokens no trunca ninguna.

La chat template de la model card tiene un defecto para el entrenamiento. Renderiza una tool call pasada sin la llave de cierre después de arguments, así que el texto dentro de <TOOLCALL> no es JSON válido. Un modelo entrenado con ese texto aprende a emitirlo, y el parser de serving no puede leer la llamada. El repositorio incluye la plantilla con la llave añadida y la usa en el entrenamiento y en el serving. Su prueba solo con CPU renderiza conversaciones con herramientas y comprueba que todos los <TOOLCALL> se parsean.

Configuración de la demo

El config.py de la demo define los valores de LoRA:

LORA_R = 16        # Adapter capacity; tune against held-out results
LORA_ALPHA = 32    # Update scale alpha/r = 2
LORA_DROPOUT = 0.05
MAX_SEQ_LENGTH = 4096

LORA_TARGET_MODULES enumera las siete proyecciones de atención y MLP de este modelo de la familia Llama, de q_proj a down_proj.

El cociente alpha/rank. alpha/r escala la actualización clásica de LoRA. alpha = 2r es una heurística de partida habitual en la documentación de algunas herramientas, no una garantía de estabilidad. Barre el rank, alpha, la learning rate y los módulos objetivo solo cuando los datos y el baseline estén fijados.

Carga del modelo

El model.py de la demo carga la base en 4 bits, sustituye la chat template por la corregida y añade los adapters LoRA:

from unsloth import FastLanguageModel

model, tokenizer = FastLanguageModel.from_pretrained(
    model_name=MODEL_NAME,
    max_seq_length=MAX_SEQ_LENGTH,
    load_in_4bit=True,  # QLoRA: the frozen base is stored in 4-bit form
    trust_remote_code=True,
)
# Training rows, the saved tokenizer, and vLLM all use this one template.
tokenizer.chat_template = CHAT_TEMPLATE_PATH.read_text()

model = FastLanguageModel.get_peft_model(
    model,
    r=LORA_R,
    lora_alpha=LORA_ALPHA,
    lora_dropout=LORA_DROPOUT,
    target_modules=LORA_TARGET_MODULES,
    use_gradient_checkpointing="unsloth",  # Lower activation memory; extra compute
)

Configuración de entrenamiento

El training.py de la demo pasa las filas renderizadas al SFTTrainer de TRL. Evalúa la loss de tokens reservados para evaluación una vez por epoch. Esa loss es un diagnóstico, no la precisión de las tool calls; la etapa 5 sigue exigiendo una evaluación de la tarea por separado.

trainer = SFTTrainer(
    model=model,  # 4-bit base with LoRA adapters from FastLanguageModel
    processing_class=tokenizer,
    train_dataset=train_dataset,
    eval_dataset=eval_dataset,
    args=SFTConfig(
        output_dir="outputs/unsloth-nemotron-function-calling",
        dataset_text_field="text",
        max_length=4096,
        packing=False,  # One row per sequence, so row boundaries are easy to inspect
        assistant_only_loss=False,  # The Nemotron template has no generation spans
        eval_strategy="epoch",
        per_device_train_batch_size=2,
        gradient_accumulation_steps=4,
        learning_rate=2e-4,
        num_train_epochs=3,
        bf16=True,  # The repository falls back to FP16 on GPUs without BF16
        optim="adamw_8bit",
    ),
)
trainer.train()

Primera comprobación

uv run infer --prompt "Book a flight to Tokyo"

infer carga el adapter y envía el prompt con una herramienta, book_flight(destination), renderizada con la misma plantilla que las filas de entrenamiento. Un adapter entrenado debería responder con <TOOLCALL>[{"name": "book_flight", "arguments": {"destination": "Tokyo"}}]</TOOLCALL>. Si responde en texto plano, inspecciona la fila de entrenamiento impresa antes de entrenar más tiempo.


Etapa 5: Evaluación

Fija los casos de prueba, las reglas de puntuación y los umbrales de aprobación antes de la primera ejecución. Como mínimo, compara el checkpoint ajustado con el modelo base sin ajustar exacto, con el mismo prompt, los mismos ajustes de decodificación y el mismo entorno de herramientas. Informa de la calidad agregada solo después de revisar los slices de fallo que el proyecto debía mejorar.

Sigue cuatro grupos:

  1. Tarea objetivo: coincidencia exacta, éxito de ejecución, rúbrica humana u otro resultado vinculado al caso de uso.
  2. Regresión: capacidades generales y slices de tareas ya soportadas que la adaptación podría dañar.
  3. Seguridad y políticas: negativas, fuga de datos, prompt injection o restricciones específicas del dominio.
  4. Operaciones: latencia, throughput, memoria, tamaño del artefacto y coste con la configuración de serving prevista.

Benchmarks automatizados

Usa lm-evaluation-harness para comprobar tareas estandarizadas relevantes junto con las pruebas de tu producto. Este comando requiere un checkpoint fusionado de Hugging Face, que la demo escribe con --merge:

lm_eval --model hf \
    --model_args pretrained=./outputs/unsloth-nemotron-function-calling-merged \
    --tasks hellaswag,arc_easy,mmlu \
    --batch_size 8

LLM-as-judge

Para la calidad subjetiva, un modelo mayor puede ayudar a puntuar, pero calibra su criterio con ejemplos revisados por personas y mantén oculta la identidad del candidato. Dale al juez la salida del modelo, la respuesta esperada y una rúbrica que defina cada puntuación para cada criterio, como relevancia, precisión y formato.

Evaluación específica del dominio

Reserva ejemplos reales por origen, usuario, documento o tiempo para que los casi duplicados no se filtren entre las particiones. Para function calling, valida la trayectoria completa: selección de la herramienta, argumentos, resultado de la ejecución, recuperación y respuesta final. Informa de intervalos de confianza o de recuentos pareados de victorias y derrotas cuando la muestra sea pequeña, e inspecciona cada regresión en un slice crítico.

Comprueba cada trayectoria en cuatro pasos:

  1. Se selecciona la herramienta correcta.
  2. Los argumentos son válidos y correctos.
  3. El resultado de la ejecución es el esperado.
  4. El modelo se recupera de un error de la herramienta.

Cuenta cada fallo por comprobación y por slice. Una respuesta final correcta puede ocultar un argumento erróneo o una recuperación omitida.

El código de evaluación del Berkeley Function Calling Leaderboard (BFCL) puntúa la selección de funciones y los argumentos frente a llamadas de referencia. Úsalo como comprobación de regresión junto con tus propias conversaciones reservadas para evaluación.


Etapa 6: Despliegue y formatos de salida

Elige el artefacto según el motor de serving y el plan de rollback, no solo según el tamaño del archivo. Exporta todos los formatos desde los mismos objetos model y tokenizer evaluados, para que todos los artefactos lleven los mismos pesos entrenados. El diagrama compara los tres artefactos: qué contiene cada archivo, qué lo carga y su contrapartida.

El adapter es un archivo pequeño que vLLM carga junto al modelo base; el modelo fusionado tiene aproximadamente el tamaño de la base en 16 bits; el archivo GGUF contiene pesos de unos 4 bits para Ollama y llama.cppEl adapter es un archivo pequeño que vLLM carga junto al modelo base; el modelo fusionado tiene aproximadamente el tamaño de la base en 16 bits; el archivo GGUF contiene pesos de unos 4 bits para Ollama y llama.cpp

En la demo, uv run finetune siempre guarda el adapter, --merge añade el modelo fusionado y --gguf q4_k_m añade el archivo GGUF, todo en una sola ejecución de entrenamiento. Las llamadas de Python que siguen muestran lo que hace cada flag.

1. Adapter LoRA

model.save_pretrained("outputs/unsloth-nemotron-function-calling")
tokenizer.save_pretrained("outputs/unsloth-nemotron-function-calling")
  • Tamaño: proporcional a los módulos objetivo, el rank, las capas y el dtype; a menudo mucho menor que la base.
  • Ideal para: desarrollo, adapters de tarea versionados y motores que admiten LoRA directamente.
  • Ventaja adicional: puedes cambiar de adapter sin volver a descargar el modelo base.

2. Modelo fusionado

# What --merge runs: merge the adapter into the base weights and save a standalone model
model.save_pretrained_merged(
    "outputs/unsloth-nemotron-function-calling-merged", tokenizer, save_method="merged_16bit"
)
  • Tamaño: aproximadamente el del checkpoint base completo con la precisión de salida elegida.
  • Ideal para: motores o vías de distribución que no admiten el adapter por separado.
  • Compromiso: artefacto mayor y despliegue más lento; carga más simple de un único modelo.

3. Formato GGUF

# What --gguf q4_k_m runs; file size and name depend on the Unsloth version
model.save_pretrained_gguf(
    "outputs/unsloth-nemotron-function-calling-gguf", tokenizer, quantization_method="q4_k_m"
)
  • Tamaño: depende del modelo; aproximadamente pesos de cuatro bits más metadatos en las variantes Q4.
  • Ideal para: inferencia en CPU, Ollama, llama.cpp y despliegue en el edge.
  • Opciones: q4_k_m (más pequeño), q5_k_m (más fidelidad de los pesos), q8_0 (mayor y de más fidelidad). Mide el impacto en la tarea después de la conversión.

Etapa 7: Serving y monitorización

Con vLLM

Este comando sirve el adapter de la demo sobre el modelo base. Sirve con el parser y la chat template de la model card; deben coincidir con la plantilla usada en el entrenamiento. La model card registra el parser llama_nemotron_json mediante el archivo de plugin llama_nemotron_nano_toolcall_parser.py. El repositorio de la demo proporciona la plantilla corregida.

Usa un entorno independiente de Python 3.12 con vLLM 0.12.0: el parser fijado importa la API del parser de herramientas de esta versión, que cambió en versiones posteriores. El comando fija el modelo base y el parser a la misma revisión de la model card; usa también esa revisión de la base para entrenar. La compatibilidad del código fuente y una prueba del parser en CPU no confirman que el serving en GPU funcione. Verifica la carga del modelo y el tool call previsto en tu GPU. Ejecuta desde la raíz del repositorio:

uv venv --python 3.12 .venv-vllm
uv pip install --python .venv-vllm/bin/python "vllm==0.12.0" "huggingface-hub==0.36.0"
source .venv-vllm/bin/activate

hf download nvidia/Llama-3.1-Nemotron-Nano-4B-v1.1 \
    llama_nemotron_nano_toolcall_parser.py \
    --revision d552708a9d575fa8d4a690b988fd870d65279f98 --local-dir serving

vllm serve nvidia/Llama-3.1-Nemotron-Nano-4B-v1.1 \
    --revision d552708a9d575fa8d4a690b988fd870d65279f98 \
    --trust-remote-code \
    --enable-lora \
    --lora-modules function-calling=./outputs/unsloth-nemotron-function-calling \
    --enable-auto-tool-choice \
    --tool-parser-plugin ./serving/llama_nemotron_nano_toolcall_parser.py \
    --tool-call-parser llama_nemotron_json \
    --chat-template ./src/unsloth_demo/chat_template.jinja \
    --host 127.0.0.1 \
    --port 8000 \
    --max-model-len 4096

El comando uv run --with openai infer-vllm de la demo prueba la generación de argumentos mediante la API compatible con OpenAI. Envía una herramienta mínima, book_flight, con un string destination obligatorio, y pide una llamada propuesta; no ejecuta ninguna reserva. La petición de cli/inference.py es:

client = OpenAI(base_url="http://localhost:8000/v1", api_key="dummy")
response = client.chat.completions.create(
    model="function-calling",  # the --lora-modules name, not the base model
    messages=[{"role": "user", "content": "Book a flight to Tokyo"}],
    tools=[DEMO_TOOL],  # book_flight(destination), defined in config.py
    tool_choice="auto",
)
print(response.choices[0].message.tool_calls)

Para esta entrada, el resultado previsto es book_flight con {"destination": "Tokyo"}. Como la elección de herramienta es automática, comprueba también si el modelo devolvió alguna llamada. La herramienta usa la misma forma que las definiciones de herramientas de glaive con las que se entrenó el modelo. El parser de vLLM lee la llamada del texto generado, así que una llamada mal formada vuelve como contenido de texto plano sin tool_calls. vLLM también puede restringir los argumentos de una función a su schema cuando la función está marcada con strict: true; la guía de tool calling describe los ajustes del servidor y qué parsers lo admiten.

Con Ollama (local)

Lista el directorio outputs/unsloth-nemotron-function-calling-gguf y sustituye MODEL_FILE.gguf en el siguiente ejemplo por el nombre del archivo generado; las versiones de Unsloth pueden nombrar los archivos de otra forma. Un archivo GGUF por sí solo no garantiza un formato correcto de chat y herramientas en Ollama o llama.cpp. Usa la plantilla con la que se entrenó y prueba el artefacto importado.

# Create Modelfile
echo 'FROM ./outputs/unsloth-nemotron-function-calling-gguf/MODEL_FILE.gguf' > Modelfile

# Import to Ollama
ollama create my-function-model -f Modelfile

# Run
ollama run my-function-model

Con llama.cpp (CPU)

Con la misma exportación GGUF y la misma sustitución del nombre de archivo, este comando comprueba una respuesta en texto plano. No prueba una tool call completa:

llama-cli -m ./outputs/unsloth-nemotron-function-calling-gguf/MODEL_FILE.gguf \
    -p "What's the weather in Tokyo?" \
    --ctx-size 4096

Monitoriza el modelo publicado

El ciclo de vida no termina con una loss de entrenamiento saludable. Registra la revisión del modelo base, el tokenizer y la chat template, el hash del adapter, la versión del dataset, la configuración de entrenamiento y el informe de evaluación como una única unidad de release. En producción, monitoriza el éxito de la tarea, las salidas no válidas, los fallos de política, la latencia y la deriva de la entrada con los mismos slices que se usaron offline. Mantén cargable el artefacto anterior y define un umbral de rollback antes del lanzamiento.

En resumen:

  1. Unidad de release: revisión del modelo base, tokenizer y chat template, hash del adapter, versión del dataset, configuración de entrenamiento e informe de evaluación.
  2. Monitoriza el éxito de la tarea, las salidas no válidas, los fallos de política, la latencia y la deriva de la entrada con los mismos slices que offline.
  3. Si una métrica cae por debajo del umbral de rollback fijado antes del lanzamiento, carga el artefacto anterior.

Tu próxima prueba

Antes de una ejecución completa, inspecciona una conversación serializada y sus objetivos de loss, y completa un paso de forward/backward con la longitud máxima. Guarda esa configuración junto con la división del dataset y el informe del baseline. Si falta un tool schema, consérvalo en la fila de entrenamiento. Si una respuesta se trunca, ajusta la longitud de secuencia y el formato para que la respuesta completa sobreviva. Resuelve esos problemas antes de añadir epochs.

Referencias

Artículos e investigación

Herramientas de procesamiento de datos

Constrained decoding

  • xgrammar — Constrained decoding con FSMs
  • outlines — Generación estructurada para LLMs

Frameworks de entrenamiento

  • Unsloth — Framework de fine-tuning optimizado
  • Axolotl — Entrenamiento basado en configuración y launchers distribuidos
  • TRL — Librería de Hugging Face para SFT y entrenamiento con preferencias
  • PEFT model merging — add_weighted_adapter y métodos de fusión
  • Torchtune — Librería de fine-tuning nativa de PyTorch, ya no se mantiene activamente

Inferencia y despliegue

Evaluación

Guías y recursos