Transformers вече стартира квантизирани модели на llama.cpp
from_pretrained и започнете да генерирате на собствената си машина.
Стартирането на AI модели на лаптопа ви стана много по-лесно, а llama.cpp има голям принос за това. Неговият inference engine захранва локални AI инструменти като Ollama, LM Studio и Jan. Наред с проекти като MLX, той помогна локалното извеждане да се превърне в практична възможност за ежедневна употреба.
Ето скорошен пример за това как може да изглежда локалният AI:
Ето докъде сме стигнали в момента. И няма да лъжа — усещането е доста вълшебно 🧙♀️
— Julien Chaumond (@julien_c) 24 април 2026 г.
Qwen3.6 27B работи в coding агента 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 GB | Неквантизирана референция |
Q6_K |
3.53 GB | По-висока точност от по-малките варианти |
Q5_K_M |
3.14 GB | Баланс между размер и точност |
Q4_K_M |
2.74 GB | Практична отправна точка за локално извеждане |
Препоръчваме да започнете с Q4_K_M, а след това да опитате Q5_K_M или Q6_K, ако разполагате с повече памет. По-агресивната квантизация може да помогне на по-големите модели да се поберат, но компромисът с качеството зависи от модела и задачата. Оценявайте го върху работата, която действително искате моделът да изпълнява. Документацията за GGUF в Hub описва наличните типове квантизация.
Зареждане на 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 като реализация на attention. Ако това ядро не може да бъде извлечено, моделът преминава към "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, следва настройката по подразбиране на шаблона за чат. Вижте опциите за reasoning за подробности.
Можете да свържете клиент като 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 токена, като е използван най-добрият от три предварително загрети стартирания и е включен prefill.
Измерено на MacBook Pro M2 Max, 32 GB унифицирана памет, 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 включва prefill, докато llama-bench отчита пропускателна способност само при декодиране.
transformers и llama.cpp
Когато GGML и llama.cpp се присъединиха към Hugging Face, описахме допълващите им роли: llama.cpp предоставя основа за локално извеждане, докато transformers предоставя основа за дефиниране на модели. Поддръжката на GGUF сближава двете.
llama.cpp остава препоръчваният от нас engine, когато приоритетът ви е ефективното локално извеждане. Неговият специализиран runtime, управлението на паметта и широката хардуерна поддръжка са изградени именно за тази цел. Тази интеграция дава на разработчиците удобен начин да работят със същите GGUF контролни точки в transformers:
- Експериментирайте с GGUF в Python и PyTorch. Проверявайте междинните активации с hooks, променяйте forward pass на модела или създавайте прототипи на персонализирани слоеве чрез познатите инструменти на PyTorch.
- Оценявайте GGUF модели. Използвайте съществуващите си работни процеси за оценяване в transformers, за да измервате качеството на квантизираните контролни точки.
- Проверявайте GGUF конверсиите. За нас като разработчици зареждането на оригиналната контролна точка и нейната GGUF конверсия в transformers улеснява проверката дали теглата са конвертирани правилно, като се отчита грешката от квантизацията.
- Изпробвайте нови идеи за декодиране. Използвайте персонализирани обработващи елементи на logits и критерии за спиране с
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. С налични в PyTorch ggml ядра и схеми за квантизация можем да работим за ускоряване на поддържаните им операции, без първо да реализираме целия модел в llama.cpp. Това е особено полезно за нови архитектури, изследователски модели и персонализирани варианти, които може никога да не получат специализирана реализация в llama.cpp.
Тази възможност се простира отвъд самия GGUF формат. Ядрото работи с тензори; то не изисква целият модел да идва от GGUF файл. Същите градивни елементи могат да се интегрират в други модели на transformers и работни процеси за зареждане. Това отваря път и към други модалности: модели за компютърно зрение, аудио модели и мултимодални модели биха могли да използват повторно съвместими ядра за attention, нормализация и матрично умножение, без първо да разполагат с пълна реализация в llama.cpp. Всяка архитектура все пак изисква интеграция и валидиране; първоначалните GGUF примери тук обхващат генерирането на текст.
Бързо локално извеждане с Python и PyTorch
Искахме също да покажем докъде можем да стигнем, като запазим модела и цикъла за генериране в Python. С правилните ядра и ефективен цикъл за генериране Python и PyTorch могат да осигурят висока производителност при локално извеждане. Ядрата поемат тежките изчисления, докато цикълът за генериране поддържа GPU зает, като избягва ненужната синхронизация.
Фокусът ни беше да направим eager изпълнението бързо, без да изискваме torch.compile. За интерактивна употреба искахме бързо стартиране и постоянен поток от токени, без паузи за компилация или повторна компилация при промяна на входните форми. Двата основни елемента на тази работа са ядрата и самият generate.
Повторно използване на Metal ядрата на ggml
Ядрото е малка програма, която изпълнява операция върху GPU. PyTorch предоставя реализации с общо предназначение; специализираното ядро може да извършва по-малко работа, да комбинира няколко операции или да чете директно квантизирани тегла в съхранения им формат.
Библиотеката kernels ни позволява да разпространяваме съвместими компилации на Metal ядрата на ggml в Hub и да ги извикваме от transformers. Така работата на ggml се пренася в модела на PyTorch, без моделът да се заменя с отделен inference runtime.
| Ядро | Какво прави |
|---|---|
ggml-quantization |
Чете пакетирани квантизирани тегла за матрични операции, включително избраните експерти в MoE модел. Избягва разгръщането на цялата матрица с тегла преди всяка операция по декодиране. |
ggml-norm |
Обединява операциите по нормализация, включително RMSNorm с център в нулата, използвана от Qwen3.5 и Qwen3.8. |
ggml-attn |
Предоставя flash attention на ggml за Metal при обработка на подкани и декодиране на токени. |
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 файлове):
- Премахване на ненужната attention mask в началото (#48814). Когато поддържан вход само за декодер няма padding, неговата маска за padding, съставена от единици, може да бъде премахната в началото на генерирането. Последващият код за attention вече не трябва многократно да проверява тази маска, за да определи дали може да бъде пропусната. Causal attention се запазва.
- Отлагане на проверката за спиране (#47975). При поддържаните пътища
generateкопира решението за спиране асинхронно и го използва на следващата стъпка. CPU може да продължи да планира работа, докато GPU изпълнява операции. Поточното предаване на токени използва същия подход, а всяка допълнителна стъпка след условието за спиране се премахва от резултата.
Тези промени подобряват цикъла за генериране около модела, така че ползата им се простира отвъд GGUF. Те допълват работата по ядрата: ядрата намаляват цената на дадена операция, докато по-малкият брой точки на синхронизация позволява припокриване на планирането от CPU и изпълнението на GPU.
При тези измервания всички ядра на слоевете остават активирани; лентите изолират промените в цикъла за генериране.
Текущи ограничения и следващи стъпки
Първоначалната цел е един интерактивен разговор върху Apple Silicon. Има няколко ограничения, които трябва да имате предвид:
- Пакетираният път за inference засега е само за MPS. Импортирането на GGUF чрез деквантизиране остава отделна възможност; поддръжката на файловия формат не означава, че пакетирани ядра са налични на всяко устройство.
- Padding и batching все още изискват работа. Входовете без padding се възползват от описаната по-горе оптимизация на маската. Пакетите с padding не могат да използват същия пряк път и може да имат по-ниска производителност. Искаме да разширим работата до
generate_batchвърху MPS. - Покритието на архитектурите е ограничено. Пакетираният loader в момента обхваща плътните и MoE архитектурите Qwen3.5, включително съвместимите контролни точки 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
Преведено автоматично от английски. Оригиналната статия е на връзката по-долу.