Технологии

«Совместимость полезна, только если модель приятно запускать»: transformers научился запускать GGUF-кванты llama.cpp

22 сентября 2026 года Hugging Face опубликовала в блоге пост о поддержке GGUF в transformers: квантованные чекпойнты llama.cpp загружаются через обычный from_pretrained, а вычисления идут на ядрах ggml через библиотеку kernels. Первая цель заявлена узко — локальный инференс на Apple Silicon и архитектура Qwen3.5, при этом авторы прямо пишут, что рекомендованным движком для эффективного локального инференса остаётся сам llama.cpp.

«Совместимость полезна, только если модель приятно запускать»: transformers научился запускать GGUF-кванты llama.cpp

Что именно добавили

Hugging Face добавила в transformers возможность эффективно запускать GGUF-модели: теперь можно взять чекпойнт, рассчитанный по памяти ноутбука, и работать с ним через знакомый API — выбрать GGUF на Hub, загрузить его через from_pretrained и генерировать локально. Формат GGUF разработан командой llama.cpp и широко используется для локального инференса; та же команда публикует квантованные чекпойнты в организации ggml-org, а готовые сборки в разных квантах выкладывают Unsloth, LM Studio Community и bartowski. Скачиваний у GGUF-моделей уже миллионы.

Мотивация сформулирована одной фразой: совместимость полезна только тогда, когда модель приятно запускать. Чтобы подтянуть производительность к llama.cpp, авторы переиспользуют его ядра ggml через библиотеку kernels и сокращают накладные расходы в generate. Начальный фокус — локальный инференс на Apple Silicon, начиная с архитектуры Qwen3.5.

Контекст здесь важнее деталей реализации: движок llama.cpp работает внутри Ollama, LM Studio и Jan, а MLX закрывает похожую нишу на платформе Apple. Роли двух проектов Hugging Face описывала ещё в анонсе о присоединении GGML и llama.cpp: llama.cpp даёт фундамент локального инференса, transformers — фундамент определения моделей. Поддержка GGUF эти роли сближает, но не отменяет: llama.cpp остаётся рекомендованным движком, когда приоритет — эффективный локальный инференс, за счёт выделенного рантайма, управления памятью и широкой поддержки железа.

Как загрузить GGUF-модель

Требования перечислены без оговорок: нужен Mac с Apple Silicon; версия PyTorch, поддерживаемая опубликованными сборками ядра ggml-quantization, — обычно это две последние версии PyTorch; последняя версия transformers, пока main, потому что релиза с этой поддержкой ещё нет; и совместимая версия библиотеки kernels. Установка сводится к одной команде: pip install -U "git+https://github.com/huggingface/transformers.git" kernels.

Загрузка отличается от обычной ровно одним аргументом: в from_pretrained передаются идентификатор модели на Hub и имя файла в параметре gguf_file, токенизатору — тот же gguf_file. Дальше идёт стандартный transformers: apply_chat_template формирует вход, model.generate выдаёт продолжение, декодирование — обычным tokenizer.decode. Без совместимого ядра квантизации загрузчик распаковывает модель и расходует больше памяти, поэтому предупреждение об откате стоит читать буквально.

Часть работы берёт на себя загрузчик: если веса остаются упакованными на Metal, transformers сам подтягивает совместимые ядра слоёв ggml/Metal и использует ggml-org/ggml-attn как реализацию attention. Если это ядро получить не удалось, происходит откат на "sdpa" с предупреждением, а принудительно задать "sdpa" можно через attn_implementation="sdpa". Сервировать модель можно тем же путём: transformers serve поднимает OpenAI-совместимый API, а модель указывается как репозиторий и файл через двоеточие — "unsloth/Qwen3.5-4B-GGUF:Qwen3.5-4B-Q4_K_M.gguf". Клиенты вроде Jan или Pi подключаются к этому серверу как к кастомному OpenAI-совместимому провайдеру по адресу http://localhost:8000/v1.

Дообучение от GGUF-чекпойнта тоже предусмотрено: веса распаковываются, и дальше идёт обычный тренировочный процесс transformers. В примере для этого используется GgufConfig(dequantize=True) вместе с dtype=torch.bfloat16.

Сколько весит один и тот же чекпойнт

GGUF упаковывает веса модели и метаданные — информацию токенизатора и, опционально, chat template — в один файл и поддерживает разные уровни квантования: точность обменивается на размер. Варианты вроде Q4_K_M смешивают точности тензоров, оставляя большинство весов в 4 бита, а чувствительные тензоры — в более высокой точности.

Нагляднее всего это на Unsloth's Qwen3.5-4B: непакованный BF16 занимает 8,42 ГБ, Q6_K — 3,53 ГБ, Q5_K_M — 3,14 ГБ, Q4_K_M — 2,74 ГБ. Рекомендация авторов: начинать с Q4_K_M, а Q5_K_M или Q6_K брать, если памяти больше. Более агрессивное квантование помогает уместить крупные модели, но цена в качестве зависит от модели и задачи — её предлагают измерять на той работе, для которой модель и нужна. Полный список типов описан в документации Hub по GGUF.

Замеры против llama.cpp

Точкой отсчёта выбран llama.cpp, и методика описана так, чтобы её можно было воспроизвести. Сравнивались три GGUF-чекпойнта: небольшая dense-модель, крупная dense-модель и mixture-of-experts. Колонка llama.cpp получена инструментом llama-bench (сборка 5f55650a7, релиз b10200, Metal-бэкенд из ggml 0.18.0) командой llama-bench -m <модель> -p 0 -n 128 -r 3 и показывает метрику tg128 — скорость генерации на 128 декодированных токенах, усреднённую по трём повторам, без обработки промпта. Колонка transformers — это generate, выдающий те же 128 токенов из 12-токенного промпта, лучший из трёх прогретых прогонов, и prefill в него включён.

Железо и стек зафиксированы: MacBook Pro M2 Max, 32 ГБ объединённой памяти, macOS 26.6, PyTorch 2.12.1, kernels 0.17.0, питание от сети. Результат авторы описывают как близкий к llama.cpp на всех трёх чекпойнтах — и сразу оговаривают границу сравнения: условия не идентичны, потому что замер transformers включает prefill, а llama-bench сообщает только скорость декодирования.

В скрипте замера есть деталь, которую легко пропустить: между прогонами стоит пауза в 90 секунд, чтобы машина остыла, — по наблюдению авторов, идущие подряд прогоны теряют 10% и больше.

Ядра ggml и изменения в цикле генерации

Ядро — небольшая программа, выполняющая операцию на GPU: специализированное ядро может делать меньше работы, объединять несколько операций или читать квантованные веса прямо в том виде, в котором они хранятся. Библиотека kernels позволяет распространять совместимые сборки Metal-ядер ggml на Hub и вызывать их из transformers — так работа ggml попадает в PyTorch-модель без замены модели отдельным рантаймом инференса.

Набор ядер перечислен по назначению: ggml-quantization читает упакованные квантованные веса для матричных операций, включая выбранных экспертов в MoE-модели, и не разворачивает всю матрицу перед каждым шагом декодирования; ggml-norm сливает операции нормализации, включая zero-centered RMSNorm из Qwen3.5 и Qwen3.8; ggml-attn даёт Metal-реализацию flash attention из ggml для обработки промпта и декодирования токенов; ggml-gated-delta-net ускоряет gated delta network в слоях линейного attention гибридных архитектур Qwen3.5 и Qwen3.8. Пятое ядро, topk, — собственная Metal-реализация Hugging Face: оно выбирает экспертов для каждого токена в MoE-модели и решает отдельное узкое место маршрутизации, объединяя softmax и top-k.

Вклад ядер слоёв измеряли сравнением одинаковых упакованных GGUF-чекпойнтов с ними и без них, причём ядро квантизации оставлено включённым в обоих конфигурациях: его отключение изменило бы представление весов и измеряло бы другой компромисс.

Отдельная часть работы — сам цикл generate, и её эффект шире GGUF. Первое изменение (PR #48814) убирает маску attention, когда padding нет: у поддерживаемого decoder-only входа из одних единиц маска удаляется в начале генерации, чтобы код attention не проверял её на каждом шаге; каузальность при этом сохраняется. Второе (PR #47975) откладывает проверку остановки: решение копируется асинхронно и потребляется на следующем шаге, поэтому CPU продолжает планировать работу, пока считает GPU, а лишний шаг за пределами условия остановки срезается из результата. Эти изменения улучшают цикл вокруг модели, поэтому полезны всем моделям transformers, а не только GGUF-файлам: ядра снижают стоимость операции, меньшее число точек синхронизации позволяет планированию CPU и исполнению GPU перекрываться.

Что пока не работает

Границы перечислены самими авторами. Упакованный путь инференса пока работает только на MPS, а импорт GGUF через распаковку остаётся отдельным вариантом: поддержка формата не означает, что упакованные ядра доступны на любом устройстве.

Padding и батчинг требуют доработки: входы без padding выигрывают от оптимизации маски, а батчи с padding такого ярлыка не получают и могут работать медленнее; расширение на generate_batch на MPS заявлено как следующий шаг. Покрытие архитектур тоже ограничено: упакованный загрузчик пока охватывает dense- и MoE-архитектуры Qwen3.5, включая совместимые чекпойнты Qwen3.8, а другие архитектуры будут добавлять постепенно. Тех, у кого есть GGUF-модель, которую хочется использовать в transformers, просят открыть issue с чекпойнтом и описанием сценария — это влияет на приоритеты.

Почему это важно

Практическая ценность здесь не в самой загрузке файла, а в том, что квантованный чекпойнт перестаёт быть чёрным ящиком отдельного рантайма. В transformers с ним можно экспериментировать на языке Python и PyTorch: смотреть промежуточные активации через hooks, менять forward pass, прототипировать свои слои. Можно оценивать качество квантованных чекпойнтов существующими пайплайнами оценки, проверять корректность конвертации — загрузить исходный чекпойнт и его GGUF-версию рядом, — пробовать новое декодирование через собственные logits processors и циклы генерации или дообучаться от GGUF-чекпойнта.

Вторая, более широкая часть заявки — ядра ggml для моделей, которых в llama.cpp нет. У transformers уже есть PyTorch-реализации таких архитектур, и ядро работает с тензорами, а не с форматом файла: те же строительные блоки можно встраивать в другие модели и загрузчики, а это открывает путь к vision-, audio- и мультимодальным моделям, которым нужны совместимые ядра attention, нормализации и матричного умножения. Оговорка та же: каждая архитектура требует отдельной интеграции и валидации, а первые примеры с GGUF касаются только генерации текста.

Над постом работали Marc Sun, Arthur Zucker и Lysandre. В благодарностях отдельно указаны Arthur Zucker, инициировавший работу и ревьюивший все PR, Cyril Vallez — за изменения в generate, Sayak Paul, команда llama.cpp и Bertrand Chevalier — за интеграцию ядер, Aritra Roy Gosthipaty и Pedro Cuenca — за ревью публикации, а Lysandre Debut — за руководство проектом.

Проверка редакции: 22 сентября 2026 года все официальные страницы, на которые опирается этот разбор, отдавали HTTP 200 — сам пост в блоге Hugging Face, документация transformers по GGUF, оба pull request, анонс о присоединении GGML и llama.cpp, документация библиотеки kernels, страница ядра ggml-org/ggml-quantization и карточка модели unsloth/Qwen3.5-4B-GGUF.

Первоисточники

  1. Hugging Face — Marc Sun, Arthur Zucker, Lysandre, «Transformers now runs llama.cpp quants» (22 сентября 2026)
  2. Hugging Face — Transformers documentation: Quantization, GGUF
  3. GitHub — huggingface/transformers, PR #48814 «[generate] Drop attention mask early without padding»
  4. GitHub — huggingface/transformers, PR #47975 «[generate] stop synchronizing the accelerator on every decode step»
  5. Hugging Face — «GGML and llama.cpp join HF to ensure the long-term progress of Local AI»
  6. Hugging Face — Kernels: документация библиотеки ядер
  7. Hugging Face — ядро ggml-org/ggml-quantization для чтения упакованных квантованных весов
  8. Hugging Face — модель unsloth/Qwen3.5-4B-GGUF, использованная в примерах и замерах