Un modelo de apenas 350 millones de parámetros puede parecer demasiado pequeño para tareas exigentes de formato. Sin embargo, un experimento con GRPO demuestra que una afinación específica puede mejorar de forma notable su capacidad para generar JSON y YAML válidos, respetar esquemas y devolver exactamente la estructura solicitada.
El modelo pasó de obtener un 22,6 % a un 29,7 % de respuestas correctas en el benchmark IFStruct después de solo 100 pasos de entrenamiento. La mejora no convierte al modelo en una solución perfecta, pero sí muestra algo importante: cuando el objetivo está bien definido, un modelo pequeño puede volverse mucho más útil para integrarse con sistemas reales.
El problema: una respuesta correcta también debe tener la forma correcta
Los modelos de lenguaje suelen evaluarse por su capacidad para razonar, resumir o extraer información. Pero en una aplicación de producción aparece otra pregunta menos llamativa y mucho más práctica: ¿la respuesta se puede analizar automáticamente sin romper el sistema?
Si una aplicación espera un objeto JSON con cinco campos y el modelo entrega una lista, agrega una propiedad no permitida o deja una cadena sin cerrar, la respuesta puede ser inútil aunque el contenido sea razonable.
A esto se le llama cumplimiento de esquema o schema compliance. En términos simples, significa que la salida:
- Se puede interpretar correctamente.
- Usa el formato solicitado, como JSON, YAML o un bloque de código.
- Incluye los campos obligatorios.
- Respeta los tipos de datos y las restricciones del esquema.
- Tiene la cantidad y estructura esperadas.
El benchmark IFStruct mide precisamente este tipo de confiabilidad. Su conjunto de evaluación contiene 2.000 muestras y comprueba si la salida del modelo es válida y compatible con el esquema indicado.
El punto de partida: LFM2.5-350M
El experimento utiliza LiquidAI/LFM2.5-350M, un modelo compacto de 350 millones de parámetros. Antes de entrenarlo, el equipo lo sirvió localmente mediante llama.cpp y evaluó su rendimiento con la versión BF16 en formato GGUF.
El resultado inicial fue de 452 respuestas correctas de 2.000, equivalente a un 22,6 %. Esta cifra es cercana al 21,1 % reportado anteriormente para el mismo modelo en la publicación de IFStruct, aunque los autores aclaran que su canalización de entrenamiento no intenta reproducir exactamente ese resultado.
La evaluación se ejecutó en un MacBook Pro con chip Apple M5 Max y 36 GB de memoria unificada. El entrenamiento, en cambio, se diseñó para funcionar en una GPU de 16 GB disponible en servicios gratuitos como Colab o Kaggle.
Esta separación es relevante. No necesitas una infraestructura gigantesca para evaluar el modelo localmente, y el entrenamiento puede mantenerse relativamente económico si la tarea está bien delimitada.
Cómo se entrenó con GRPO y LoRA
El equipo utilizó datos de nvidia/Nemotron-RL-instruction_following-structured_outputs, que incluyen instrucciones, un esquema JSON objetivo y la cantidad esperada de campos. Para el entrenamiento se usaron aproximadamente 500 ejemplos.
Como la distribución de esos datos no coincide exactamente con IFStruct, se agregaron dos tipos de variaciones:
- En el 40 % de los ejemplos se indicó que la respuesta debía aparecer dentro de un bloque de código delimitado.
- Un 20 % separado se transformó en tareas cuya salida debía ser una lista en el nivel superior, con una cantidad específica de elementos.
Así, el modelo no aprendió únicamente a producir JSON válido. También practicó dos dificultades comunes en aplicaciones reales: distinguir entre una respuesta envuelta en una clave y una lista directa, y respetar instrucciones sobre bloques de código.
Para actualizar el modelo se utilizó LoRA, una técnica de ajuste eficiente que entrena una pequeña cantidad de parámetros adicionales en lugar de modificar todos los pesos originales. En este caso se entrenaron cerca de 6 millones de parámetros, alrededor del 1,66 % del modelo completo.
El ajuste se aplicó a módulos relacionados con las proyecciones de atención y las capas internas de la arquitectura híbrida de LFM2.5. Esto permite adaptar el comportamiento del modelo con un consumo de memoria mucho menor que un ajuste completo.
Tres recompensas para medir la calidad de cada respuesta
GRPO, o Group Relative Policy Optimization, genera varias respuestas para una misma instrucción y utiliza una función de recompensa para favorecer las mejores. En este experimento, cada respuesta recibió puntuaciones entre 0 y 1 mediante tres criterios.
1. Recompensa por formato
Comprueba si la respuesta se puede analizar y si utiliza la forma solicitada. Por ejemplo, una salida JSON sin bloque de código obtiene la puntuación máxima cuando eso era lo esperado. Si es JSON válido, pero aparece en el formato equivocado, recibe crédito parcial. Si no se puede interpretar, obtiene cero.
2. Recompensa por cantidad de campos
Mide si el objeto contiene la cantidad correcta de campos en el nivel superior. Una coincidencia exacta recibe la máxima puntuación, mientras que la recompensa disminuye según la diferencia.
3. Recompensa por validación del esquema
Comprueba si la respuesta cumple el JSON Schema asociado. El sistema cuenta errores como campos obligatorios ausentes, tipos incorrectos, valores fuera de rango o propiedades adicionales no permitidas.
Las recompensas se combinaron con estos pesos:
- Formato JSON:
1.0 - Cantidad de campos:
0.5 - Validación del esquema:
2.0
El mayor peso se asignó a la validación del esquema porque es el criterio más cercano a la utilidad real de la salida dentro de un software.
Solo 100 pasos, pero una mejora clara
El entrenamiento utilizó 100 pasos, ocho generaciones por grupo de instrucciones, una tasa de aprendizaje de 5e-5 y una penalización KL de 0.01 para evitar que el modelo se alejara demasiado de su comportamiento original.
Después se fusionó el adaptador LoRA con el modelo base y se convirtió el resultado a GGUF BF16 para volver a servirlo mediante llama.cpp. De esta manera, las dos evaluaciones utilizaron una pila de ejecución comparable.
Los resultados fueron los siguientes:
| Grupo de IFStruct | Modelo base | Modelo ajustado | Mejora |
|---|---|---|---|
| General | 22,6 % | 29,7 % | +7,1 puntos |
| JSON | 18,0 % | 31,9 % | +13,9 puntos |
| YAML | 27,2 % | 27,5 % | +0,3 puntos |
| Objeto con clave envolvente | 28,5 % | 29,7 % | +1,2 puntos |
| Lista directa | 16,6 % | 29,7 % | +13,1 puntos |
La mejora más importante aparece exactamente en las áreas que recibieron atención durante el entrenamiento. El rendimiento en JSON subió casi 14 puntos porcentuales y la capacidad de producir listas directas aumentó en 13,1 puntos.
En cambio, YAML prácticamente no cambió. Esto sugiere que el entrenamiento fue específico y no produjo una mejora general en todos los formatos. ¿Es una limitación? Sí, pero también es una señal de control: el modelo mejoró principalmente aquello que las recompensas le enseñaron a priorizar.
Qué errores siguen presentes
El ajuste no eliminó los problemas de cumplimiento. En el modelo entrenado todavía aparecieron miles de casos con campos obligatorios ausentes, además de errores de cantidad de elementos y tipos incompatibles.
También surgieron propiedades adicionales como metadata.tone, speaker_labels y notes. En otras palabras, el modelo aprendió a seguir mejor algunas instrucciones, pero aún puede añadir información que el esquema no autoriza.
El resultado de 29,7 % también quedó por debajo del 33,15 % reportado para Qwen3.5-2B. La comparación debe interpretarse con cuidado, porque se trata de modelos de tamaños y características diferentes. Aun así, el modelo de 350M se acercó considerablemente a uno varias veces más grande mediante un entrenamiento corto y relativamente barato.
Por qué importa para proyectos pequeños
Muchas aplicaciones no necesitan un modelo enorme para resolver una tarea concreta. Un asistente interno que convierte facturas en datos, clasifica tickets o extrae información de documentos puede beneficiarse más de una salida predecible que de una capacidad general de razonamiento.
Este experimento plantea una estrategia práctica:
- Elegir un modelo pequeño que pueda ejecutarse con costos moderados.
- Crear ejemplos cercanos al formato que utilizará la aplicación.
- Definir recompensas que midan errores concretos.
- Ajustar el modelo con LoRA y GRPO.
- Evaluarlo con un benchmark independiente y reproducible.
La clave está en no confundir una mejora en la recompensa de entrenamiento con una solución lista para producción. IFStruct ayuda a detectar avances, pero todavía conviene validar las respuestas en tiempo de ejecución, registrar los errores y utilizar reintentos o restricciones de decodificación cuando sea necesario.
Un modelo pequeño no tiene que saber hacerlo todo. Si consigue entregar de forma confiable la estructura que tu sistema espera, puede ser más útil que un modelo mucho mayor que responde con brillantez, pero rompe el formato en momentos críticos.
