Transformers теперь запускает квантизированные модели llama.cpp
from_pretrained и начните генерацию на своём компьютере.
Запуск ИИ-моделей на ноутбуке стал гораздо проще, и llama.cpp сыграл в этом важную роль. Его механизм инференса лежит в основе таких локальных ИИ-инструментов, как Ollama, LM Studio и Jan. Наряду с проектами вроде MLX он помог сделать локальный инференс практичным вариантом для повседневного использования.
Недавний пример того, каким может быть локальный ИИ:
Вот где мы сейчас находимся. И, честно говоря, это кажется довольно волшебным 🧙♀️
— Julien Chaumond (@julien_c) 24 апреля 2026 года
Qwen3.6 27B работает внутри агента для программирования Pi через Llama.cpp на MacBook Pro
Для нетривиальных задач в кодовых базах @huggingface это очень, очень близко к использованию последней версии Opus в Claude… pic.twitter.com/lsIxLoUneU
GGUF, разработанный командой llama.cpp, — широко используемый формат для локального инференса. Команда также публикует квантованные контрольные точки в репозитории ggml-org на Hub. Такие издатели, как Unsloth, LM Studio Community и bartowski, также предоставляют готовые контрольные точки GGUF с разными уровнями квантования, чтобы пользователи могли выбрать версию, подходящую для их компьютера. Модели GGUF скачивали миллионы раз.
Мы хотим упростить локальный запуск этих моделей и с помощью transformers. Совместимость полезна только в том случае, если модель удобно запускать. Чтобы приблизить производительность к llama.cpp, мы повторно используем лежащие в его основе ядра ggml через библиотеку kernels и уменьшаем накладные расходы в generate. Изначально мы сосредоточились на локальном инференсе на Apple Silicon, начав с архитектуры Qwen3.5.
Что такое формат файлов GGUF?
GGUF объединяет веса модели и метаданные, включая информацию о токенизаторе и необязательный шаблон чата, в одном файле. Он поддерживает разные уровни квантования, позволяя обменять часть точности на меньший объём памяти. Варианты вроде Q4_K_M используют разные точности тензоров: в основном 4-битные веса, сохраняя чувствительные тензоры с более высокой точностью.
Вот как квантование изменяет размер файла модели Qwen3.5-4B от Unsloth:
| Вариант GGUF | Размер файла | Компромисс |
|---|---|---|
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 описаны доступные типы квантования.
Загрузка GGUF с помощью transformers
Для начала вам понадобятся:
- Mac на Apple Silicon.
- Версия PyTorch, поддерживаемая опубликованными сборками ядер ggml-quantization, обычно два последних выпуска PyTorch.
- Последняя версия transformers (пока ветка main, до следующего выпуска) и совместимая версия
kernels.
pip install -U "git+https://github.com/huggingface/transformers.git" kernels
Чтобы загрузить модель GGUF, передайте её model_id на Hub и имя файла в параметре gguf_file функции from_pretrained.
Дополнительная настройка не требуется: когда веса остаются упакованными на Metal, transformers автоматически загружает совместимые ядра слоя ggml/Metal и использует ggml-org/ggml-attn для реализации внимания. Если это ядро невозможно получить, модель переключается на "sdpa" с предупреждением; также можно принудительно выбрать "sdpa", явно передав attn_implementation="sdpa". Дополнительные варианты загрузки см. в документации по GGUF.
import torch
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
)
Это единственный шаг, специфичный для GGUF. Всё остальное — стандартный API transformers:
messages = [{"role": "user", "content": "Explain why the sky is blue in a few sentences."}]
inputs = tokenizer.apply_chat_template(
messages,
tokenize=True,
add_generation_prompt=True,
return_dict=True,
return_tensors="pt",
).to(model.device)
with torch.inference_mode():
outputs = model.generate(**inputs, max_new_tokens=256)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
Без совместимого ядра квантования загрузчик выполняет обратное квантование модели и использует больше памяти.
Запуск GGUF через предпочтительный интерфейс
Вы также можете использовать ту же контрольную точку с помощью transformers serve, который предоставляет API, совместимый с OpenAI:
pip install -U "transformers[serving] @ git+https://github.com/huggingface/transformers.git" kernels
transformers serve "unsloth/Qwen3.5-4B-GGUF:Qwen3.5-4B-Q4_K_M.gguf"
Аргумент модели использует формат <model_id>:<filename>.gguf: до двоеточия находится репозиторий на Hub (unsloth/Qwen3.5-4B-GGUF), а после — файл для загрузки (Qwen3.5-4B-Q4_K_M.gguf). Это позволяет выбрать конкретный вариант квантования из репозитория, который может содержать несколько вариантов.
Для моделей, чей шаблон чата поддерживает рассуждения, добавьте --reasoning off, чтобы отключить их, или --reasoning on, чтобы включить. Значение по умолчанию, --reasoning auto, следует настройке шаблона чата. Подробнее см. в разделе о параметрах рассуждений.
Вы можете подключить такой клиент, как Jan или Pi, добавив пользовательского провайдера, совместимого с OpenAI, со следующими настройками:
| Настройка | Значение |
|---|---|
| Базовый URL | http://localhost:8000/v1 |
| ID модели | unsloth/Qwen3.5-4B-GGUF:Qwen3.5-4B-Q4_K_M.gguf |
transformers запускает модель на вашем Mac, а клиент предоставляет интерфейс для общения. Эту же конечную точку могут использовать другие клиенты, поддерживающие данный API.
Сравнение производительности с llama.cpp
Наш ориентир для производительности локального инференса — llama.cpp. В приведённом ниже сравнении рассматриваются три контрольные точки GGUF: небольшая плотная модель, более крупная плотная модель и модель со смесью экспертов.
Значения в столбце llama.cpp получены с помощью инструмента llama-bench (сборка 5f55650a7, выпуск b10200, бэкенд Metal из ggml 0.18.0), запущенного как llama-bench -m <file> -p 0 -n 128 -r 3. Он сообщает показатель tg128 — скорость генерации токенов для 128 декодированных токенов, усреднённую по трём повторам, без учёта обработки промпта. В столбце transformers используется generate для генерации тех же 128 токенов из промпта длиной 12 токенов; указано лучшее значение из трёх прогретых запусков, включая заполнение контекста.
Измерения выполнены на MacBook Pro M2 Max с 32 ГБ объединённой памяти, macOS 26.6, PyTorch 2.12.1, kernels 0.17.0, подключённом к сети.
Скрипт бенчмаркаimport time
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
model_id, filename = "unsloth/Qwen3.5-4B-GGUF", "Qwen3.5-4B-Q4_K_M.gguf"
model = AutoModelForCausalLM.from_pretrained(model_id, gguf_file=filename)
tokenizer = AutoTokenizer.from_pretrained(model_id, gguf_file=filename)
inputs = tokenizer("The capital of France is Paris. The capital of Germany is", return_tensors="pt")
inputs = inputs.to(model.device)
with torch.inference_mode():
model.generate(**inputs, max_new_tokens=8, min_new_tokens=8, do_sample=False)
torch.mps.synchronize()
for _ in range(3):
time.sleep(90)
start = time.perf_counter()
model.generate(**inputs, max_new_tokens=128, min_new_tokens=128, do_sample=False)
torch.mps.synchronize()
print(f"{128 / (time.perf_counter() - start):.1f} tok/s")
Для другого столбца:
llama-bench -hf unsloth/Qwen3.5-4B-GGUF:Q4_K_M -p 0 -n 128 -r 3
Transformers близок к llama.cpp на всех трёх контрольных точках. На графике используются описанные выше измерения; он не подразумевает идентичные условия бенчмарка, поскольку измерение transformers включает заполнение контекста, а llama-bench сообщает пропускную способность только декодирования.
transformers и llama.cpp
Когда GGML и llama.cpp присоединились к Hugging Face, мы описали их взаимодополняющие роли: llama.cpp предоставляет основу для локального инференса, а transformers — основу для определения моделей. Поддержка GGUF сближает эти два проекта.
llama.cpp остаётся рекомендуемым нами движком, если вашим приоритетом является эффективный локальный инференс. Его специализированная среда выполнения, управление памятью и широкая поддержка оборудования созданы именно для этой цели. Эта интеграция даёт разработчикам удобный способ работать с теми же контрольными точками GGUF внутри transformers:
- Экспериментировать с GGUF в Python и PyTorch. Исследовать промежуточные активации с помощью хуков, изменять прямой проход модели или создавать прототипы пользовательских слоёв, используя привычные инструменты PyTorch.
- Оценивать модели GGUF. Использовать существующие рабочие процессы оценки transformers для измерения качества квантованных контрольных точек.
- Проверять преобразования в GGUF. Для нас, разработчиков, загрузка исходной контрольной точки и её преобразования в GGUF в transformers упрощает проверку правильности преобразования весов с учётом ошибки квантования.
- Пробовать новые идеи декодирования. Использовать пользовательские обработчики логитов и критерии остановки с
generateили писать собственный цикл генерации на Python. - Дообучать модель из контрольной точки GGUF. Выполнить обратное квантование весов и продолжить работу в стандартном рабочем процессе обучения transformers.
В последнем случае используйте GgufConfig(dequantize=True):
import torch
from transformers import AutoModelForCausalLM, GgufConfig
model = AutoModelForCausalLM.from_pretrained(
"unsloth/Qwen3.5-4B-GGUF",
gguf_file="Qwen3.5-4B-Q4_K_M.gguf",
quantization_config=GgufConfig(dequantize=True),
dtype=torch.bfloat16,
)
За пределами GGUF: ядра ggml для большего числа моделей
Главная возможность заключается в переносе производительности ggml на модели, которые не поддерживаются llama.cpp.
Transformers уже предоставляет реализации этих архитектур на PyTorch. Имея ядра ggml и схемы квантования, доступные в PyTorch, мы можем ускорять поддерживаемые операции без предварительной реализации всей модели в llama.cpp. Это особенно полезно для новых архитектур, исследовательских моделей и пользовательских вариантов, которые могут никогда не получить отдельную реализацию в llama.cpp.
Эта возможность выходит за рамки самого формата GGUF. Ядро работает с тензорами; ему не требуется, чтобы вся модель поступала из файла GGUF. Те же строительные блоки можно интегрировать в другие модели transformers и рабочие процессы загрузки. Это также открывает путь к другим модальностям: модели компьютерного зрения, аудиомодели и мультимодальные модели смогут повторно использовать совместимые ядра внимания, нормализации и матричного умножения без предварительной полной реализации в llama.cpp. Для каждой архитектуры по-прежнему необходимы интеграция и проверка; приведённые здесь начальные примеры GGUF охватывают генерацию текста.
Быстрый локальный инференс с Python и PyTorch
Мы также хотели показать, каких результатов можно достичь, сохраняя модель и цикл генерации на Python. При правильных ядрах и эффективном цикле генерации Python и PyTorch способны обеспечить высокую производительность локального инференса. Ядра выполняют тяжёлые вычисления, а цикл генерации поддерживает загрузку GPU, избегая ненужной синхронизации.
Мы стремились сделать выполнение в eager-режиме быстрым без необходимости использовать torch.compile. Для интерактивного применения нам были нужны быстрый запуск и стабильный поток токенов без пауз на компиляцию или повторной компиляции при изменении размеров входных данных. Два основных элемента этой работы — ядра и сам generate.
Повторное использование ядер Metal из ggml
Ядро — это небольшая программа, выполняющая операцию на GPU. PyTorch предоставляет реализации общего назначения; специализированное ядро может выполнять меньше работы, объединять несколько операций или напрямую считывать квантованные веса в сохранённом формате.
Библиотека kernels позволяет распространять совместимые сборки ядер Metal из ggml на Hub и вызывать их из transformers. Это переносит работу ggml в модель PyTorch, не заменяя модель отдельной средой выполнения для инференса.
| Ядро | Назначение |
|---|---|
ggml-quantization |
Считывает упакованные квантованные веса для матричных операций, включая выбранных экспертов в модели MoE. Оно не даёт расширять всю матрицу весов перед каждой операцией декодирования. |
ggml-norm |
Объединяет операции нормализации, включая центрированную относительно нуля RMSNorm, используемую Qwen3.5 и Qwen3.8. |
ggml-attn |
Предоставляет flash attention Metal из ggml для обработки промпта и декодирования токенов. |
ggml-gated-delta-net |
Ускоряет gated delta network, используемую в слоях линейного внимания гибридных архитектур Qwen3.5 и Qwen3.8. |
topk |
Выбирает экспертов для каждого токена в модели MoE, объединяя маршрутизацию softmax и top-k. Это наша собственная реализация для Metal. |
Первые четыре пакета основаны на ядрах ggml; ядро top-k устраняет отдельное узкое место в маршрутизации MoE. Вместе они уменьшают объём работы GPU, необходимой для каждого сгенерированного токена.
Чтобы показать вклад ядер слоёв, мы сравниваем одни и те же упакованные контрольные точки GGUF с ними и без них. Ядро квантования включено в обеих конфигурациях: его отключение также изменило бы представление весов и измеряло бы другой компромисс.
Согласованная работа CPU и GPU
Более быстрые ядра помогают только тогда, когда у GPU есть работа. Во время генерации CPU планирует операции GPU и управляет циклом, создающим следующий токен. Чтение результата обратно с GPU может заставить CPU ждать завершения поставленных в очередь операций. Даже небольшое ожидание для каждого токена может заметно снизить пропускную способность.
В generate это исправляют два изменения, которые улучшают работу всех моделей transformers, а не только при запуске файлов GGUF:
- Раннее удаление ненужной маски внимания (#48814). Если поддерживаемый вход только с декодером не содержит дополнения, его маску заполнения, состоящую из единиц, можно удалить в начале генерации. Последующему коду внимания больше не нужно многократно проверять эту маску, чтобы определить, можно ли её пропустить. Каузальное внимание при этом сохраняется.
- Отложенная проверка остановки (#47975). На поддерживаемых путях
generateасинхронно копирует решение об остановке и использует его на следующем шаге. CPU может продолжать планировать работу, пока GPU выполняет вычисления. Для потоковой передачи токенов используется тот же подход, а любой дополнительный шаг после достижения условия остановки удаляется из результата.
Эти изменения улучшают цикл генерации вокруг модели, поэтому их польза выходит за рамки GGUF. Они дополняют работу над ядрами: ядра уменьшают стоимость операции, а меньшее количество точек синхронизации позволяет перекрывать планирование CPU и выполнение GPU.
В этих измерениях все ядра слоёв включены; столбцы показывают влияние именно изменений цикла генерации.
Текущие ограничения и следующие шаги
Изначальная цель — один интерактивный диалог на Apple Silicon. Следует учитывать несколько ограничений:
- Упакованный путь инференса пока работает только на MPS. Импорт GGUF через обратное квантование остаётся отдельной возможностью; поддержка формата файлов не означает, что упакованные ядра доступны на каждом устройстве.
- С заполнением и пакетной обработкой ещё нужно поработать. Незаполненные входные данные получают преимущество от описанной выше оптимизации маски. Заполненные пакеты не могут использовать тот же обходной путь и могут работать медленнее. Мы хотим расширить эту работу на
generate_batchв MPS. - Охват архитектур ограничен. Упакованный загрузчик сейчас поддерживает плотную архитектуру Qwen3.5 и архитектуру MoE, включая совместимые контрольные точки Qwen3.8. Добавить поддержку других архитектур относительно просто, и мы будем постепенно расширять охват.
Если у вас есть модель GGUF, которую вы хотели бы использовать в transformers, создайте issue, указав контрольную точку и свой сценарий использования. Это поможет нам расставить приоритеты в поддержке моделей, которые люди запускают локально.
Благодарности
Мы хотели бы поблагодарить Arthur Zucker за начало этой работы и проверку всех моих PR, а также Cyril Vallez за PR, связанные с generate. Мы благодарны Sayak Paul, команде llama.cpp и Bertrand Chevalier за помощь в интеграции ядер. Мы также благодарим Aritra Roy Gosthipaty и Pedro Cuenca за проверку этой записи в блоге, а Lysandre Debut — за руководство проектом.
Модели, упомянутые в этой статье 1
Сообщество
· Зарегистрируйтесь или войдите, чтобы оставить комментарий
Модели, упомянутые в этой статье 1
Переведено автоматически с английского. Оригинал статьи — по ссылке ниже.