Transformers ya carga checkpoints GGUF sin desempaquetarlos
Hugging Face metió los propios kernels Metal de ggml, los de llama.cpp, adentro de transformers para que los archivos GGUF corran con los pesos todavía empaquetados. El alcance es angosto a propósito: Apple Silicon, y Qwen3.5 denso y MoE para arrancar.
Hugging Face anunció este martes que transformers ya puede correr cuantizaciones GGUF de forma directa: carga el formato de archivo único que construyó el equipo de llama.cpp y genera con los pesos todavía empaquetados, en vez de expandirlos primero a precisión completa. El trabajo reutiliza los propios kernels ggml de llama.cpp, traídos mediante la librería kernels de Hugging Face. El alcance anunciado es angosto a propósito: inferencia local en Apple Silicon, empezando por la arquitectura Qwen3.5.
Qué salió en concreto
El paso de carga es un argumento extra. Le pasás a `from_pretrained` un repositorio del Hub y un nombre de archivo en `gguf_file`, y de ahí en adelante todo es la API habitual de transformers: plantillas de chat, `generate`, procesadores de logits, lo de siempre. Según la documentación de GGUF, cuando el kernel `ggml-org/ggml-quantization` está disponible el cargador usa MPS por defecto, mantiene los pesos empaquetados y corre las multiplicaciones de matrices directamente sobre los bloques empaquetados. También cambia la implementación de atención a `ggml-org/ggml-attn`, el mismo kernel de flash attention que llama.cpp usa para decode y prefill. Si no se puede bajar ese kernel, el modelo cae de forma silenciosa: `sdpa` para la atención y descuantización completa al cargar, que cuesta memoria.
Los requisitos son lo bastante específicos como para mirarlos antes de planear nada alrededor de esto:
- Una Mac con Apple Silicon. El camino de inferencia empaquetada es solo MPS por ahora.
- transformers desde main — la función todavía no está en una release etiquetada — más una versión compatible de `kernels`.
- Una versión de PyTorch cubierta por los builds publicados del kernel ggml-quantization, que Hugging Face describe como habitualmente las dos últimas releases.
- Pesos Qwen3.5 densos o Qwen3.5 MoE, incluidos checkpoints Qwen3.8 compatibles. Cualquier otra arquitectura pasa por el cargador viejo, que siempre descuantiza.
Un detalle que conviene tener presente: las cargas empaquetadas fuerzan float32 automáticamente, porque Hugging Face midió que es más rápido en MPS. Pasar otro dtype produce una advertencia, no el dtype que pediste.
El argumento del tamaño
El sentido de GGUF siempre fue meter un modelo en la memoria que realmente tenés. Hugging Face usa como ejemplo el Qwen3.5-4B de Unsloth: el archivo BF16 sin cuantizar pesa 8,42 GB, `Q6_K` 3,53 GB, `Q5_K_M` 3,14 GB y `Q4_K_M` 2,74 GB. La recomendación es arrancar en `Q4_K_M` y subir si te sobra memoria, con la salvedad de que el costo en calidad de una cuantización más agresiva depende del modelo y de la tarea, y hay que medirlo sobre el trabajo que de verdad querés hacer.
La parte de velocidad no es solo para GGUF
Los kernels más rápidos solo rinden si la GPU tiene trabajo encolado, así que dos cambios entraron en el propio `generate` y aplican a todos los modelos de transformers, no solo a los GGUF. El PR #48814, mergeado el 15 de septiembre, descarta la máscara de padding de unos al inicio de la generación para entradas decoder-only sin padding, así el código de atención deja de revisarla una y otra vez. El PR #47975, mergeado el 4 de septiembre, deja de sincronizar el acelerador en cada paso de decode leyendo la bandera de parada un paso más tarde; el PR reporta una mejora de 1,10x a 1,20x en MPS y ninguna regresión en CUDA. Hugging Face además escribió su propio kernel Metal `topk` para el ruteo de expertos en modelos MoE.
Benchmarks, leídos con cuidado
Hugging Face reporta que transformers queda cerca de llama.cpp en tres checkpoints GGUF — un modelo denso chico, uno denso más grande y uno MoE — medidos en una MacBook Pro M2 Max con 32 GB de memoria unificada, macOS 26.6, PyTorch 2.12.1 y kernels 0.17.0. La propia empresa aclara la salvedad: las dos columnas no son condiciones idénticas de benchmark, porque la medición de transformers sale de `generate` e incluye prefill, mientras que `llama-bench` reporta throughput solo de decode. Tomá la comparación como direccional.
Para qué hacer esto
Hugging Face es poco habitualmente directa en no competir consigo misma acá: llama.cpp sigue siendo, en sus palabras, el motor recomendado cuando la prioridad es inferencia local eficiente. Lo que agrega transformers es el lado Python: inspeccionar activaciones con hooks, evaluar checkpoints cuantizados con los flujos de evaluación que ya tenés, validar que una conversión a GGUF coincide con los pesos originales, probar decodificaciones propias, o hacer fine-tuning desde un archivo GGUF vía `GgufConfig(dequantize=True)`. El mismo checkpoint también se puede servir en un endpoint compatible con OpenAI usando `transformers serve`, direccionado como `<repo>:<archivo>.gguf`, que es lo que permite apuntar un cliente como Jan o Pi a una máquina local.
La ambición declarada es más grande que el formato. Como un kernel opera sobre tensores y no exige que el modelo entero venga de un archivo GGUF, Hugging Face quiere usar los kernels de ggml para acelerar arquitecturas que llama.cpp no implementa — modelos de investigación, arquitecturas nuevas, variantes propias — y con el tiempo modelos de visión, audio y multimodales. Eso es una hoja de ruta, no una función ya publicada. Por ahora el resumen honesto es un primer paso bien acotado: un backend, una familia de modelos y una lista clara de lo que todavía no funciona, incluida la inferencia por lotes con padding.
Por qué importa
- GGUF es el formato de distribución de facto para inferencia local, y hasta ahora usar uno en transformers significaba descuantizarlo y perder el ahorro de memoria que lo hacía atractivo. La carga empaquetada cierra esa brecha para una primera porción de modelos.
- Los arreglos del bucle de generación que hay detrás no están atados a GGUF. Descartar la máscara de padding redundante y diferir el chequeo de parada aceleran `generate` en todos los modelos de transformers, con una ganancia medida de 1,10x a 1,20x en Apple Metal.
- Reutilizar kernels de ggml adentro de PyTorch marca un camino donde arquitecturas nuevas o de nicho consigan rendimiento de nivel llama.cpp sin tener que reimplementarse antes en llama.cpp — aunque Hugging Face todavía no publicó nada de eso.
Puntos clave
- transformers ya puede cargar un checkpoint GGUF con `from_pretrained(..., gguf_file=...)` y generar sin desempaquetar los pesos.
- El camino empaquetado es solo MPS y hoy cubre Qwen3.5 denso y MoE, más checkpoints Qwen3.8 compatibles; todo lo demás descuantiza al cargar.
- Requiere transformers desde main, un build compatible de `kernels` y un PyTorch reciente — todavía no hay release etiquetada.
- Dos PRs mergeados en `generate` recortan la sincronización CPU-GPU para todos los modelos, reportados en 1,10x a 1,20x en MPS.
- Hugging Face sigue recomendando llama.cpp para inferencia local eficiente; el valor acá está en herramientas de PyTorch, evaluación y fine-tuning.
Fuentes
- Hugging FaceFuente primariaTransformers now runs llama.cpp quantshuggingface.co
- Hugging FaceFuente primariaGGUF — Transformers quantization documentationhuggingface.co
- huggingface/transformers on GitHubFuente primaria[generate] Drop attention mask early without padding (#48814)github.com
- huggingface/transformers on GitHubFuente primaria[generate] stop synchronizing the accelerator on every decode step (#47975)github.com
- gguf
- llama-cpp
- transformers
- quantization
- local-inference
- apple-silicon
- metal
- kernels
- Hugging Face
- Qwen3.5
- Qwen3.8