MXFP4 bit-compatible: por qué repacking le gana al round-trip a bf16
Cuando un motor de inferencia agrega soporte para una arquitectura nueva, el camino por defecto es convertir los pesos del checkpoint original a un formato interno propio: dequantizar lo que venga cuantizado, reconstruir en punto flotante, y volver a cuantizar si hace falta. Ese camino funciona, pero en modelos de billones de parámetros el costo no es trivial. Es exactamente el problema que llama.cpp resolvió distinto al integrar Kimi-K3.
El caso: expertos MoE que ya venían en el formato correcto
Kimi-K3 es un modelo de texto con atención híbrida KDA (lineal) más MLA (completa), MoE con expertos “latentes” y una activación propia que reemplaza SwiGLU. Los expertos enrutados del checkpoint original se distribuyen en formato “compressed-tensors mxfp4-pack-quantized”. La nota técnica del PR es explícita sobre lo que encontró el equipo: ese formato es bit-compatible con el MXFP4 que ggml ya usa internamente. Mismo esquema de asignación de códigos E2M1, mismo byte de escala E8M0. Lo único que cambia son las posiciones de los nibbles dentro de cada bloque.
Esa diferencia, ser bit-compatible pero no byte-idéntico, es la que determina la estrategia. En lugar de dequantizar a bf16 y volver a cuantizar (un round-trip que las notas cifran en cerca de 5.5 TB para este modelo), el equipo optó por repaquetar los bloques: reordenar los nibbles sin pasar por punto flotante en ningún momento. El resultado, según la propia verificación del PR, es una conversión sin pérdida: los bloques MXFP4 repaqueteados dequantizan a los pesos fuente con error 0.0e+00.
Ese repack no se escribió desde cero. Las notas señalan que DeepSeek-V4 ya hacía exactamente el mismo reordenamiento de bits, así que Kimi-K3 reutilizó ese helper en lugar de duplicar la lógica.
La verificación no se quedó en “compila y corre”
Lo segundo que vale la pena señalar es cómo se validó la integración, porque es tan replicable como el truco del repacking. El equipo comparó contra la implementación de referencia de Moonshot (transformers más los kernels Triton de KDA de fla) sobre un modelo reducido que ejercita cada componente específico de K3. Midieron el error relativo de los logits en la posición final contra la referencia en fp32: 6.7e-05 de error relativo, correlación 1.00000000, para las dos rutas posibles del delta-net (chunked y recurrente).
Después de una segunda pasada para corregir tipos en el conversor, repitieron la comparación end-to-end y dejaron registrado que no hubo cambio de comportamiento: 8.386e-03 de error relativo, correlación 0.99996630. Y antes de dar el trabajo por cerrado, corrieron el modelo completo cuantizado (Kimi-K3-Q2_K) en 8x B200 y confirmaron que contenido, reasoning_content, los deltas de streaming y las tool calls salían correctos, con el finish_reason esperado en cada caso.
Ninguno de esos tres pasos, verificación numérica contra referencia, revalidación tras el refactor de tipos, y prueba end-to-end con el modelo real, es opcional si el objetivo es confiar en una integración de arquitectura nueva. Cada uno detecta un tipo de error distinto: el primero errores de implementación del kernel, el segundo regresiones introducidas al limpiar el código, el tercero problemas que solo aparecen con el checkpoint completo y la carga real de la red.
Reglas
Antes de escribir un pipeline de dequantizar y volver a cuantizar para un formato cuantizado nuevo, compara su especificación bit a bit con los formatos que ya soportas. Si coinciden el esquema de códigos y el byte de escala, y solo cambia el layout de bits dentro del bloque, se trata de un repack, no de una conversión numérica, y evitarlo ahorra tiempo de proceso y una fuente de error de redondeo innecesaria.
Si ya tienes un helper que hace ese reordenamiento de bits para un modelo, revisa si el modelo nuevo usa el mismo formato antes de reimplementarlo.
No des por verificada una integración de arquitectura nueva solo porque genera texto. Mide error relativo y correlación contra una implementación de referencia en al menos dos niveles, pesos y logits, y repite la comparación end-to-end después de cualquier refactor posterior, no solo en el commit inicial.
Fuente: https://github.com/ggml-org/llama.cpp/releases/tag/b10448
Seguir leyendo
Esto se demuestra en video, y se sigue a diario en la comunidad
Las demos completas van al canal. El seguimiento release por release, adentro.