22 сентября 2026 года Hugging Face добавила в Transformers прогон GGUF, при котором веса остаются упакованными. Пост написали Marc Sun, Arthur Zucker и Lysandre: файл с Hub, аргумент gguf_file в from_pretrained, генерация на Apple Silicon. Если бот или скрипт уже живёт на кванте llama.cpp, тот же чекпоинт можно открыть обычным Python API. Пост Hugging Face.
GGUF собирает в один файл веса, сведения о токенизаторе и, если положили, шаблон чата. Квант меняет размер, не имя модели. У Unsloth для Qwen3.5-4B в таблице поста такие файлы:
- BF16, 8,42 ГБ. Это ориентир без кванта.
- Q6_K, 3,53 ГБ.
- Q5_K_M, 3,14 ГБ.
- Q4_K_M, 2,74 ГБ. С этого варианта в посте советуют начинать.
Память осталась, смотрите Q5_K_M или Q6_K. Более злой квант большую модель впихнёт, но насколько сядет качество, общей цифры в посте нет. Это видно только на своей задаче.
Qwen3.5 и упакованные ядра на MPS
Быстрый путь, где матрица читается прямо из упакованных блоков, сейчас только Apple Silicon, бэкенд MPS. Ядра ggml привозит библиотека kernels, считают они на Metal. В тексте поста к этому пути отнесены Qwen3.5 dense, Qwen3.5 MoE и совместимые чекпоинты Qwen3.8.
Карточка GGUF на ветке main уже чуть уже. Packed там назван для Qwen3.5 и Qwen3.5 MoE. Пример MoE: репозиторий unsloth/Qwen3.5-35B-A3B-GGUF, файл Qwen3.5-35B-A3B-Q4_K_M.gguf. На этом пути загрузчик сам ставит float32, на MPS так быстрее. Другой dtype даст предупреждение.
Остальные архитектуры файл не отказываются открыть. Их берёт старый загрузчик, и он всегда распаковывает веса в обычную плотную модель. В карточке в этом списке Llama, Mistral, Qwen2, Qwen2Moe, Phi3, Bloom, Falcon, StableLM, GPT2, Starcoder2 и ещё ряд. Память тогда не про файл 2,74 ГБ. Qwen3.8 на графике поста есть, в карточке docs его в packed-списке нет. Если чекпоинт 3.8 встанет только через распаковку, страницы уже разъехались, это не ваша ошибка в команде.
Ставить надо main, не тот пакет, который сейчас ставится обычным pip. Страница docs так и подписана: вы смотрите main, для этого нужна установка из исходников. Стабильная ветка, куда она отсылает за обычным pip, на момент страницы v5.17.0. Рядом нужен kernels. PyTorch берут из двух свежих релизов, под которые собраны ядра ggml-org/ggml-quantization. Пин версии пост в команде не даёт. На машине, где мерили скорость, стояли PyTorch 2.12.1 и kernels 0.17.0.
from transformers import AutoModelForCausalLM, AutoTokenizer
model_id = "unsloth/Qwen3.5-4B-GGUF"
filename = "Qwen3.5-4B-Q4_K_M.gguf"
tokenizer = AutoTokenizer.from_pretrained(model_id, gguf_file=filename)
model = AutoModelForCausalLM.from_pretrained(
model_id,
gguf_file=filename,
)
Дальше обычный generate. Отдельного режима после gguf_file нет. Без ядра квантования загрузчик распакует веса на старте, и памяти уйдёт больше. Внимание на MPS при установленном kernels по умолчанию идёт через ggml-org/ggml-attn. В посте: ядро не скачалось, будет предупреждение и откат на sdpa, тот же режим можно задать аргументом attn_implementation. Карточка docs суше: без ядра остаётся своя реализация внимания, а явно переданный attn_implementation всегда главнее. Имеет смысл сверить обе страницы, они не слово в слово.
Serve на localhost:8000
Тот же файл поднимается как OpenAI-совместимый API. Для сервера в посте отдельная установка: extras serving с того же git, плюс kernels. Имя модели с двоеточием. До него репозиторий Hub, после него конкретный файл, иначе из пачки квантов неясно, какой грузить.
transformers serve "unsloth/Qwen3.5-4B-GGUF:Qwen3.5-4B-Q4_K_M.gguf"
Клиенту отдают адрес http://localhost:8000/v1 и тот же model id. Флаг --reasoning бывает off, on и auto. auto смотрит дефолт шаблона чата. Подробности флагов пост отсылает в раздел reasoning у transformers serve.
llama.cpp с рекомендации при этом не снимают. Нужна именно быстрая локальная машина и железо шире одного Mac, в посте по-прежнему советуют llama.cpp: свой рантайм, своя память, больше устройств. Transformers здесь про другое. Тот же GGUF внутри PyTorch.
Зачем тогда тащить квант в from_pretrained. Хук на активации. Eval, который уже написан на transformers, без второго стека. Проверка конвертации: оригинал и GGUF в одном загрузчике, ошибка кванта никуда не девается. Свой logits processor или свой цикл вместо generate. Дообучение только после распаковки, GgufConfig(dequantize=True) и dtype bfloat16. Учить прямо в Q4 этот путь не обещает.
Отдельно ускорили сам цикл generate, и это уже не только GGUF. PR 48814 выкидывает лишнюю attention-маску в начале, если у decoder-only входа нет паддинга. Причинное внимание остаётся. PR 47975 откладывает проверку стопа на следующий шаг, чтобы процессор не ждал GPU на каждом токене. Лишний шаг после стопа из ответа вырезают. Ядра уменьшают цену операции, эти два патча убирают лишние синхронизации вокруг неё.
70,4 и 71,8 tok/s на M2 Max
Сравнение с llama.cpp гоняли на MacBook Pro M2 Max, 32 ГБ общей памяти, macOS 26.6, питание от сети. llama.cpp: сборка 5f55650a7, релиз b10200, Metal из ggml 0.18.0, команда llama-bench -p 0 -n 128 -r 3. Метрика tg128, среднее из трёх, только decode. Transformers: generate на 128 новых токенов из промпта в 12 токенов, лучший из трёх прогретых прогонов, и prefill в замер входит. В абзаце поста таблицы tok/s нет. Цифры стоят на графике этого же поста.
- Qwen3.5-4B, Q4_K_M, 2,74 ГБ. Transformers 70,4 tok/s, llama.cpp 71,8 ± 0,4.
- Qwen3.8-27B, UD-Q4_K_M, 16,5 ГБ. Transformers 15,9, llama.cpp 13,4 ± 0,9.
- Qwen3.5-35B-A3B, UD-IQ4_XS, 16,3 ГБ. Transformers 60,2, llama.cpp 61,3 ± 0,5.
Рядом, да. Победителя из этого не сделать: у одного столбца в знаменателе есть prefill, у другого нет. На 27B столбец Transformers выше, хотя его замер как раз тяжелее. Протокол разный, фраза «близко к llama.cpp» в посте честнее, чем заголовок «догнали».
Граница у пути короткая. Упакованные ядра пока только MPS, один интерактивный диалог. Паддинг и батч сырые: вход без паддинга маску сбрасывает, батч с паддингом тот же короткий путь не берёт и может быть медленнее. generate_batch на MPS в посте назван следующим шагом, не текущим. Поддержка формата файла не значит, что packed-ядра есть на CUDA или на обычном процессоре. На Linux-сервере быстрый контур из этого поста не включается. Файл откроется распаковкой, если архитектура в старом списке, но память и скорость будут другие. В apt ничего не приехало. localhost:8000 не надо выставлять на боевой сайт.
Хотите другой GGUF именно в быстром пути, в посте просят issue с чекпоинтом и задачей. Ядра (ggml-quantization, ggml-norm, ggml-attn, ggml-gated-delta-net и свой topk для выбора экспертов) работают с тензорами и не обязаны жить только внутри одного файла .gguf. Картинки, звук и мультимодальность в тексте названы направлением. Сейчас показан текстовый generate.