Un día con un modelo de 1.5B: qué puede (y no puede) hacer Qwen2.5-Coder
Un relato práctico de poner a Qwen2.5-Coder-1.5B a trabajar como motor de un pipeline de datos real, con las cifras que lo respaldan.
Hay mucho entusiasmo de marketing alrededor de los modelos chicos locales, y menos registros cuidadosos de qué pasa cuando ponés a uno a trabajar en un pipeline real, sin supervisión, durante horas. Este es ese registro, para Qwen2.5-Coder-1.5B-Instruct — una cuantización Q4_K_M GGUF corriendo localmente vía llama.cpp — usado como generador de primera ronda en un pipeline cuyo objetivo final es un dataset específico de Drupal lo bastante preciso como para ayudar a los ingenieros a migrar sitios Drupal 7 más rápido.
El rol que le dimos
El trabajo del modelo: leer un documento fuente (una página de referencia de API, o un par de pregunta-respuesta de un foro técnico) y producir un objeto JSON instruction/response estructurado y anclado en esa fuente — sin copiarla literalmente, y sin inventar hechos que no estén ahí. Es una tarea exigente para un modelo de 1.5B, y cada mejora en el camino tuvo que ganarse contra datos medidos reales, no contra un ejemplo elegido a mano.
Hallazgo 1: tiene instintos fuertes — a veces demasiado fuertes
El comportamiento más interesante que encontramos: cuando el contexto de entrada era largo o ruidoso, el modelo a veces recurría a una respuesta confiada y plausible de sus datos de entrenamiento en vez del texto fuente específico que tenía delante. Capturamos la misma respuesta memorizada y bien formada apareciendo en páginas de documentación sobre clases completamente distintas — una señal clara de que el modelo tenía un prior fuerte sobre "la respuesta habitual" y necesitaba un empujón hacia el texto específico en cuestión.
El arreglo fue directo: en vez de una instrucción genérica de "no inventes cosas", nombramos explícitamente el contenido recurrente exacto y le dijimos claramente que no recurriera a él salvo que la fuente realmente lo tratara. Esto funcionó de inmediato y de forma duradera — una instrucción precisa y específica superó por mucho a una genérica.
Hallazgo 2: se toma tu ejemplo trabajado muy al pie de la letra
Incluimos un ejemplo trabajado (few-shot) en el prompt para demostrar el formato de salida esperado — práctica estándar y bien respaldada. A volumen, los nombres ficticios del ejemplo reaparecían ocasionalmente en respuestas reales, un patrón lo bastante serio como para necesitar un arreglo dedicado (la tasa exacta de filtración medida, y cómo la cerramos, está en un artículo aparte sobre prompting few-shot). La versión corta: una vez que entendimos por qué pasaba (un ejemplo con nombres plausibles es difícil de distinguir del contenido real para un modelo chico), el arreglo fue directo — cambiar el ejemplo por algo deliberadamente inconfundible, y nombrar explícitamente la filtración como prohibida. Esto coincide con investigación publicada sobre "regurgitación de demostraciones" en modelos chicos, lo cual fue un buen chequeo de sanidad de que habíamos diagnosticado el mecanismo real y no solo parcheado un síntoma.
Hallazgo 3: dale una lista de verificación, no solo una instrucción
Cuando exigimos que la salida contuviera tanto una firma copiada exacta como una explicación en lenguaje simple del comportamiento, el modelo a veces devolvía solo la firma. En vez de pelear esto en prosa, movimos la explicación a su propio campo obligatorio en un JSON Schema, forzado mediante decodificación restringida por gramática a nivel de inferencia. Esto resolvió el problema por completo, porque en este caso el modelo ya sabía cómo era una buena explicación; solo necesitaba un empujón estructural para incluirla siempre.
Hallazgo 4: una idea razonable para probar — y la disciplina de revertirla
Probamos una idea más: en vez de dejar que el modelo buscara y copiara una firma de método del código fuente, le dimos una lista numerada y pre-verificada de opciones válidas y le pedimos que eligiera una — eliminando incluso la posibilidad de copiar una firma inexistente. Es una hipótesis razonable, y vale la pena probarla. En este caso, medido en vivo en vez de en una muestra chica revisada a mano, la tasa de aceptación de primera ronda de ese pipeline pasó de aproximadamente 40% a 21,4%, porque el modelo elegía un elemento válido y después describía uno distinto, memorizado, en su lugar.
Revertimos, conservamos la lista numerada como referencia para el modelo en vez de un mecanismo rígido, y seguimos adelante dentro de la misma sesión de trabajo. Esa disposición a probar una idea, medirla con honestidad contra tráfico en vivo en vez de una muestra favorable, y revertir cuando los datos dicen que no, fue lo que hizo confiable el resto del progreso del día.
Lo que realmente marcó la diferencia
En una comparación controlada, mismo verificador, misma muestra fija de 8 documentos reales:
Base (temperatura 0.2)
1/8 aceptados
Self-consistency (3 muestras, voto mayoritario)
0/8
Best-of-3 con puntaje heurístico
0/8
Temperatura 0 (decodificación greedy completa)
2/8 — mejor resultado
Sin ejemplo few-shot
0/8
Rotando entre 3 ejemplos few-shot
0/8
Selección de símbolo tipo multiple-choice
0/8
La decodificación greedy dio el mejor resultado en esta comparación — pasando de 1/8 a 2/8. Vale la pena ser precisos con lo que es eso: una muestra de 8 es lo bastante chica como para que "duplicó" sea una señal preliminar, no un resultado asentado. Fue consistente con una observación separada y a mayor escala en una corrida posterior sin supervisión (más abajo), que fue lo que nos dio suficiente confianza para dejarlo como default de producción — pero la comparación de 8 elementos por sí sola no debería leerse como concluyente estadísticamente.
El resultado final, en números
A lo largo de una corrida de producción de 7 horas sin supervisión, el modelo logró una tasa de aceptación de primera ronda del 46,0% en la tarea de generación de documentación (176 pares verificados escritos) y del 26,4% en la tarea más difícil y menos estructurada de preguntas y respuestas de la comunidad (53 pares verificados) — subiendo desde un punto de partida de 0% en esa segunda tarea más temprano en el proyecto, tras un arreglo separado y no relacionado a un filtro previo demasiado estricto. La cifra de 46,0% iguala la mejor tasa que este pipeline registró jamás, históricamente, con una mezcla notablemente más saludable de casos límite restantes.
Nada de esto convierte a un modelo de 1.5B en un sustituto de uno más grande. Lo que sí muestra es que un modelo chico, diseñado alrededor de sus fallas específicas y bastante predecibles, puede cargar con la mayor parte de una carga de trabajo de generación de alto volumen a costo marginal esencialmente cero — lo cual importa directamente para el objetivo de fondo: producir un dataset de Drupal lo bastante grande y preciso como para ser genuinamente útil en trabajo de migración, sin el costo de pasar cada generación individual por un modelo caro.