
Un modelo que no escribe, solo decide: puse a Intern-Decision a predecir la lluvia sin entrenarlo
Intern-Decision salió el 26 de septiembre de 2026: modelos de 0,8B a 4B que no generan texto y devuelven una probabilidad por pregunta en una sola pasada. Lo instalé el mismo día, tropecé con tres fallos al instalarlo, uno de los cuales lo hacía entre 2 y 3,2 veces más lento, y lo puse a predecir la lluvia de mañana en 27.256 días de siete ciudades chilenas sin entrenarlo. El 2B llega a un AUC de 0,823, casi lo mismo que un Naive Bayes entrenado con 74.145 filas, y queda bajo la regresión logística.
La semana pasada apareció una categoría nueva de modelo de lenguaje: uno que no escribe. Jev, de TypeSafe AI, recibe texto y devuelve números: probabilidades para categorías, preguntas de sí o no y calificaciones. Pero Jev, según esa presentación, funciona solo como API.
Hoy, 26 de septiembre, Shanghai AI Lab subió a Hugging Face Intern-Decision: la misma idea con pesos abiertos, licencia Apache 2.0 y tres tamaños: el más grande ocupa unos 9 GiB de memoria de video y los otros dos caben en tarjetas modestas. Cuando lo bajé tenía cero descargas.
Lo instalé esa misma mañana y le hice la pregunta que ya le hice a nueve modelos en la serie de algoritmos: ¿va a llover mañana? La diferencia es que a este no lo entrené.
$ python banco.py 2B lluvia
veredicto > Sin entrenarlo con estos datos, el 2B quedó 0,024 de AUC por debajo de la regresión logística entrenada y empató al Naive Bayes, a 21,8 ms por consulta.▋
Qué es
Un modelo de lenguaje normal responde generando texto, token a token. Intern-Decision hace otra cosa: recibe un estado (un texto, y opcionalmente imágenes) y un esquema de preguntas cerradas, arma una plantilla con un marcador <decision> por pregunta y hace una sola pasada hacia adelante. En la posición anterior a cada marcador lee los logits, se queda solo con los de las opciones permitidas (A, B, C…) y aplica softmax después de dividirlos por una temperatura fijada de fábrica.
No hay generate() ni muestreo. Por eso contesta en milisegundos, y por eso puede responder varias preguntas por el precio de una.
No se muestrea ningún token. La respuesta entera son dos logits leídos en una posición, y por eso un modelo de 0,8B contesta en unos 20 ms en GPU.
Los tres tamaños están afinados desde Qwen3.5, que mezcla atención normal con atención lineal. Ese detalle de arquitectura es el que más me costó en la instalación.
Instalación, y lo que se rompió
El README pide Python 3.12, pip install -r requirements.txt e importar DecisionEngine desde la carpeta del modelo. Lo hice al pie de la letra, dentro de un entorno aislado en mi máquina con la RTX 4070 Ti SUPER, y tropecé tres veces.
1. El modelo no encuentra su propio tokenizador. La primera carga falla con Couldn't instantiate the backend tokenizer, aunque tokenizer.json está ahí, también con la versión de transformers que fija el modelo (5.14.1). La causa está en su inference.py: usa Path(__file__).resolve().parent como carpeta del checkpoint. En la caché de Hugging Face, inference.py es un enlace simbólico a blobs/<hash>, así que resolve() apunta a la carpeta de blobs, donde ningún archivo conserva su nombre. Se arregla pasando la carpeta del snapshot explícita: DecisionEngine(checkpoint=snap). Guardé la reproducción con las dos cargas: la que falla sale de la carpeta blobs y la que pasa el snapshot carga sin problemas.
2. Corre, pero entre 2 y 3,2 veces más lento de lo que debería. Al cargar, transformers avisa en una línea fácil de perder: The fast path is not available because one of the required library is not installed. Qwen3.5 usa atención lineal, y su camino rápido necesita flash-linear-attention y causal-conv1d. Ninguna de las dos está en el requirements.txt del modelo. Sin ellas, transformers cae a una implementación en PyTorch puro.
3. causal-conv1d no compila con el CUDA que tenía. Para esta combinación de torch y CUDA se compila desde el código. Con el nvcc 13.0 falla en los encabezados de la glibc de Fedora 43 (mathcalls.h: error: exception specification is incompatible with that of previous function "rsqrt"). Con el nvcc 13.1 compila; lo verifiqué después con un archivo mínimo, y da igual si el compilador del host es gcc 14 o gcc 15. Lo que usé, limitado a la arquitectura de mi tarjeta:
export CUDA_HOME=/usr/local/cuda-13.1 PATH=/usr/local/cuda-13.1/bin:$PATH
export TORCH_CUDA_ARCH_LIST=8.9 NVCC_CCBIN=/usr/bin/g++-14 CC=gcc-14 CXX=g++-14 MAX_JOBS=8
uv pip install flash-linear-attention
uv pip install --no-build-isolation causal-conv1d
Los dos caminos usan núcleos de cálculo distintos y pueden dar probabilidades levemente distintas; no cuantifiqué esa diferencia sobre el test completo. Todas las predicciones de lluvia que siguen se hicieron con el camino rápido.
Cuánto tarda cada consulta
Medí dentro de un contenedor con límites de pod: 8 CPU, 24 GB de RAM y la GPU por CDI. Doscientas consultas por modelo después de calentar, midiendo desde que entra el diccionario hasta que sale la respuesta. Hice dos tipos: el ejemplo del README (tres preguntas a la vez) y la consulta de lluvia (una pregunta).
Sin flash-linear-attention ni causal-conv1d, transformers cae a PyTorch puro para la atención lineal. Un segundo de animación son 20 ms medidos.
Mediana de 30 consultas en un Ryzen 7 7800X3D con cuota de 8 CPU y sin GPU visible. Un segundo de animación es un segundo medido.
En GPU, media de 200 consultas: con camino rápido el p95 queda a menos de 0,3 ms de la media, y sin él a menos de 1 ms. En CPU, mediana de 30, porque la dispersión es mayor: el 4B en float32 va de 3,2 s en la mediana a 5,6 s en el p95. En la tabla del README, medida en una RTX 4090, el autor declara 34, 33 y 44 ms.
| Modelo | Con camino rápido | Sin las dos librerías | Memoria pico (MiB asignados por PyTorch) | Declarado (RTX 4090) |
|---|---|---|---|---|
| Intern-Decision-0.8B | 19,9 ms | 63,3 ms | 1.948 | 34,0 ms |
| Intern-Decision-2B | 21,8 ms | 64,1 ms | 4.255 | 33,3 ms |
| Intern-Decision-4B | 42,5 ms | 85,4 ms | 9.056 | 44,2 ms |
Latencia media del ejemplo de tres preguntas, que es la que declara el README. La de lluvia, con una sola pregunta, queda dentro de 1,1 ms de esa cifra; la figura de arriba usa la de lluvia.
En CPU, con el mismo pod (cuota de 8 CPU) y sin GPU visible, la mediana del 0,8B es 0,30 segundos por consulta en bf16 y 0,53 en float32. El 4B llega a 1,6 segundos en bf16 y 3,2 en float32, con un p95 de 5,6. De a una consulta por vez, el 0,8B en bf16 da unas 200 decisiones por minuto; el mismo modelo en la GPU, unas 3.000. El motor carga en bf16 por defecto y acepta dtype="float32"; en este Ryzen, que sí tiene instrucciones bf16, bf16 fue el más rápido.
Dos cosas me llamaron la atención. La consulta del README, con tres preguntas, y la de lluvia, con una, tardan casi lo mismo; no es una prueba limpia de que tres cuesten lo mismo que una, porque los estados tienen largos distintos, pero va en la dirección que promete la idea. Y en mi tarjeta, más modesta que la del autor, el 0,8B y el 2B quedan por debajo de lo que declara. No sé con qué instalación midió.
La prueba propia: ¿llueve mañana?
La serie de algoritmos usa siempre la misma tabla: datos diarios de NASA POWER para siete ciudades chilenas, de La Serena a Punta Arenas. Se entrena con 1984-2012 (74.145 filas) y se prueba con 2016-2026 (27.256 días). La pregunta es si mañana caerá al menos 1 mm.
A Intern-Decision no le di las filas de entrenamiento. Por cada día de prueba le mandé un estado en texto con la misma información meteorológica que usan los modelos entrenados, pero escrita: valores redondeados, el viento en ocho rumbos, la fecha completa en vez de sus senos y cosenos, y el nombre de la ciudad. Más una pregunta de sí o no:
Weather station in Temuco, Chile (latitude -38.7). Date: 2021-08-07.
Today: max temperature 10.5 °C, min temperature 0.2 °C, precipitation 0.7 mm
(yesterday 9.6 mm), relative humidity 86 %, dew point 3.3 °C, surface pressure
99.16 kPa (+0.39 kPa since yesterday), wind 4.9 m/s from the S, solar radiation
12.3 MJ/m².
rain (noul): Will it rain at least 1 mm tomorrow at this station?
Lo escribí en inglés porque su banco de pruebas está en inglés. La probabilidad de “sí” es la predicción. Con eso se calcula el AUC, la misma métrica que usa toda la serie, sobre los mismos 27.256 días.
- Boosting, 594 rondas (entrenado)0,883
- Bosque de 200 árboles (entrenado)0,882
- Regresión logística (entrenada)0,847
- Intern-Decision-2B (sin entrenar)0,823
- Naive Bayes gaussiano (entrenado)0,822
- Intern-Decision-4B (sin entrenar)0,821
- Intern-Decision-0.8B (sin entrenar)0,803
- Persistencia: mañana igual que hoy0,709
Modelos entrenados: tabla unificada de la serie (transformer/vs_models.json), entrenados con 1984-2012. Intern-Decision: sin entrenamiento, una consulta por día, dentro del pod con la RTX 4070 Ti SUPER.
Leído con calma: un modelo al que no entrené con estas estaciones ordena los días mejor que la regla ingenua de “mañana igual que hoy”, y queda a 0,0004 de un Naive Bayes que sí se entrenó con 74.145 filas. Pero la regresión logística más simple, un archivo de 1,8 KB que contesta en menos de un milisegundo en CPU (0,4 ms, medidos en la serie con un hilo y fuera de este contenedor), le gana por 0,024. Y los modelos de árboles, el bosque y el boosting, por 0,06.
El 4B no mejora al 2B. Saca 0,821 contra 0,823, con el doble de memoria y el doble de latencia. En el promedio de los benchmarks del autor el 4B sí gana, así que esto parece propio de la tarea.
El problema no está en el orden, está en la escala
El AUC solo mira si los días lluviosos quedan por encima de los secos. No mira si la probabilidad dice la verdad. Y ahí aparece lo que más me sorprendió.
En el 10 % de días que cada modelo considera más probables de lluvia, llovió el 63,2 % de las veces con el 2B, el 63,6 % con el 4B y el 63,3 % con la regresión logística. En ese extremo aciertan en la misma proporción. Lo que cambia es el número que ponen al lado.
- regresión logística entrenada
- Intern-Decision-2B sin entrenar
- Intern-Decision-4B sin entrenar
Los mismos 27.256 días de prueba para las tres curvas. Sobre la diagonal el modelo se queda corto: en el tramo de 0,5 a 0,6 del 2B, el más alto con al menos 30 días, predijo 0,52 en promedio y llovió el 79 % de las veces.
El máximo que emitió el 2B en los 27.256 días fue 0,62. Solo el 2 % de los días llegó a 0,5 o más, y apenas el 1,3 % lo superó; en el tramo de 0,5 a 0,6, con 547 días, predijo 0,52 en promedio y llovió el 79 %. Su etiqueta de decisión (el campo decision, que elige la opción más probable y en un empate exacto dice “no”) marca lluvia en 346 de 27.256 días: acierta el 5 % de los días que sí llovieron. El 0,8B es peor: su máximo fue exactamente 0,50, en 6 días, y como el empate se resuelve a “no”, su etiqueta no marcó lluvia ni un solo día.
Dónde gana sin entrenar
Separado por ciudad, el resultado no es parejo. El 2B le gana a la regresión logística en tres de las siete ciudades, y no siguen un patrón simple de frecuencia: La Serena, donde casi nunca llueve, Santiago, y Punta Arenas, donde más llueve.
| Ciudad | Días con lluvia al día siguiente | Regresión logística | Intern-Decision-2B |
|---|---|---|---|
| La Serena | 2,2 % | 0,836 | 0,886 |
| Santiago | 9,7 % | 0,735 | 0,771 |
| Punta Arenas | 39,2 % | 0,560 | 0,617 |
| Valparaíso | 7,4 % | 0,850 | 0,809 |
| Concepción | 20,5 % | 0,860 | 0,815 |
| Temuco | 26,1 % | 0,839 | 0,779 |
| Puerto Montt | 36,5 % | 0,828 | 0,780 |
Tengo una hipótesis que no probé. La regresión logística aprende un solo conjunto de pesos para las siete ciudades; recibe la latitud (su tercer coeficiente más fuerte en valor absoluto), pero no el nombre de la ciudad. El modelo de lenguaje lee “La Serena” como contexto, y en La Serena, donde casi nunca llueve, ese contexto puede valer más que un peso compartido. Punta Arenas, donde llueve el 39 % de los días, no encaja en esa explicación. También puede ser otra cosa: el AUC por ciudad mide cómo ordena dentro de cada ciudad, y ahí cualquier ventaja local cuenta.
Lo que esta medición no dice
- Es una sola tarea. Tabular, numérica y en un dominio donde un modelo entrenado con 74.000 filas tiene toda la ventaja. En clasificación de texto libre, que es para lo que el autor lo muestra, el resultado puede ser otro.
- No ajusté la redacción del estado. Escribí un prompt razonable y no lo cambié después de ver resultados, a propósito: ajustar el texto mirando el test sería entrenar a mano sobre el test.
- El modelo pudo haber visto datos de clima en su preentrenamiento. No hay forma de saber cuánto de la temporada 2016-2026 conoce Qwen3.5.
- La latencia es por consulta, de a una. Según su README el motor recibe una solicitud por llamada; no medí lotes ni consultas en paralelo.
- La CPU la medí en el mismo pod con cuota de 8 CPU (Ryzen 7 7800X3D), no en una CPU vieja. Mi otra máquina, un i5-9400F, estaba ocupada con otro trabajo y descarté lo que medí ahí.
Cuándo lo usaría y cuándo no
Lo usaría para decisiones sobre texto donde no tengo datos etiquetados todavía: clasificar tickets, enrutar solicitudes, decidir si un mensaje necesita respuesta humana. Veinte milisegundos por consulta en una tarjeta de consumo, varias preguntas en una sola pasada y un JSON tipado en vez de texto que hay que parsear. No lo medí en esas tareas; es donde lo probaría primero, como clasificador inicial mientras se juntan etiquetas.
No lo usaría donde ya tengo datos para entrenar algo simple. En esta prueba, con 74.145 filas de entrenamiento, una regresión logística de 1,8 KB le ganó en calidad y en latencia, y corre en cualquier parte. Tampoco usaría su probabilidad tal cual para decidir con un umbral: en mi prueba estaba comprimida y la etiqueta de decisión casi nunca decía que sí.
Y si lo instalas, instala también flash-linear-attention y causal-conv1d. Sin ellas se paga entre 2 y 3,2 veces la latencia sin ningún aviso más que una línea en el registro.
Medido el 26 de septiembre de 2026 en una RTX 4070 Ti SUPER de 16 GB, dentro de un contenedor con 8 CPU y 24 GB (Fedora 43), con torch 2.14.0+cu130 (el README fija 2.9.1), transformers 5.14.1, flash-linear-attention 0.5.2, causal-conv1d 1.7.0 y el driver NVIDIA 595.91. Datos de NASA POWER, la misma tabla de toda la serie.
Fuentes
- Intern-Decision, colección de InternLM en Hugging Face, publicada el 26 de septiembre de 2026. Pesos de 0,8B, 2B y 4B con licencia Apache 2.0, y el README con los benchmarks y latencias declaradas.
- internlm/Intern-Decision, el repositorio del proyecto.
- Jev introduces a new shape of LLM, Simon Willison, 21 de septiembre de 2026. La presentación de los modelos de decisión y de Jev, que según ella funciona solo como API.
- Qwen3.5-4B, el modelo base del 4B.
- flash-linear-attention y causal-conv1d, las dos librerías del camino rápido que faltan en su instalación.
- NASA POWER, la fuente de los datos diarios de las siete estaciones.
Comentarios
Todavía no hay comentarios. El primero es tuyo.