|
Professor Seleznov
|
 В llama.cpp добавили поддержку MTP Qwen3.6. Дополнительные слои Multi-Token Prediction позволяют сгенерировать сразу несколько токенов за 1 проход, что ускоряет генерацию в 1.5-2 раза. Качество при этом остается lossless. Для моделей, которые не имеют встроенного MTP, есть альтернативы в лице EAGLE-3 и DFlash. Что такое MTP Multi-Token Prediction (MTP) представляют собой дополнительные слои на выходе модели которые работают со скрытым состоянием этой же модели, эти слои обучаются вместе с моделью и они обучаются предсказывать несколько следующих токенов. Стандартный авторегрессионный трансформер, он же LLM, после прямого прохода имеет выходной слой со скрытым состоянием, которое преобразуют в логиты вероятностей следующего токена. Это только 1 следующий токен, но скрытое состояние может нести информацию о нескольких следующих шагах. Слои MTP представляют собой несколько голов MTP, которые учатся независимо предсказывать t+1, t+2, t+k следующие токены из одного скрытого состояния. Количество голов определяет сколько можно предсказать следующих токенов. В llama.cpp поддержку MTP добавили для Qwen3.6: https://github.com/ggml-org/llama.cpp/pull/22673 Для Gemma4 и других моделей поддержка в разработке. EAGLE-3 Если у модели нет MTP голов, можно применить другой схожий принцип. Скрытое состояние уже содержит информацию о следующих токенах, и если обучить отдельный блок, который научится извлекать из этого состояния информацию о следующих токенах, то можно для любой модели предсказывать k следующих токенов аналогично MTP способу. В методе EAGLE-3 создается и обучается 1 легковесная голова, которая позже навешивается на модель. В отличии от MTP, тут всего 1 голова, которая используется рекурсивно для предсказания k токенов. Для многих моделей уже созданы eagle3 модули, которые нужно скачать отдельно. Качество датасета для обучения eagle3 важно и напрямую виляет на качество работы. Поддержка в llama.cpp в статусе черновика: https://github.com/ggml-org/llama.cpp/pull/18039 DFlash Развивая идеи извлечения токенов из скрытого состояния, и пытаясь обойти ограничения авторегрессивного трансформера, в котором каждый новый токен зависит от предыдущего, был предложен способ блочной диффузии. Параллельно за один раз предсказывается сразу блок токенов. Как и в случае EAGLE-3, DFlash создает и обучает отдельный драфтер, блок диффузий, который генерирует сразу 8-16 токенов, которые постепенно проявляются из шума. Метод похож на способ генерации изображений в Stable Diffusion. Поддержка в llama.cpp в статусе черновика: https://github.com/ggml-org/llama.cpp/pull/22105 Теоретический максимум генерации t/s Генерация LLM зависит линейно от скорости памяти, и теоретический максимум генерации можно прикинуть по формуле:

Скорость памяти делить на количество активных параметров. Есть разные варианты моделей, вроде MoE или Gemma4 E4B, где количество активных параметров не совпадает с размером всей модели, и для скорость t/s важны именно задействованные параметры на каждом шагу. Допустим скорость VRAM GPU 288 Гб/с или 268 GiB/s (4060 Ti 16Гб), и модель Qwen3.6-27B, которая весит 54 GiB, а в кванте UD-Q3 13.5 GiB и влезает в GPU целиком, то максимальная скорость будет 19.8 t/s. Реальная скорость будет ниже, так как происходит деквантование и прочие вычисления, но выше 19.8 t/s поднять работая только с моделью не получится.

Qwen3.6-27B-UD-Q3_K_XL на 4060 ti 16Гб, 17 t/s при теоретическом максимуме 19.8 t/s На GPU вы ограничены скоростью памяти, а не количеством вычислительных ресурсов, и у вас остаются ресурсы ядер, чтобы за 1 проход перемножать матрицы прямого прохода LLM для параллельных запросов.
 Как работает спекулятивное декодирование Все 3 метода извлекают следующие токены из скрытого состояния основной модели, но как узнать, что эти токены предсказаны правильно? Если принимать их как есть, то равно или поздно выдача будет состоять из низкокачественных токенов. Основная идея спекулятивного декодирования заключается в том, что вы можете сгенерировать k следующих токенов и основная модель, используя общий KV-кэш, запустит k параллельных вычислений, проверяя все предложенные токены на совпадение их тому, что предложила бы основная модель. Тут важно понимать, что основная модель не смотрит на токены черновика последовательно и решает принять их или нет. Основная модель сама сэмплирует k последовательностей параллельно, последовательности создаются из предложенных токенов черновика. Допустим, черновик предложил A, B, C, D, E. Основная модель создает:
- base + A
- base + A + B
- base + A + B + ... + E
И основная модель параллельно генерирует для всех последовательностей новый токен. Если этот новый токен совпадает, то эта цепочка принимается, если и следующий токен совпадает, то более длинная цепочка принимается и так далее. Если черновик предложил цепочку из 3 токенов и вся она была принята, то за 1 проверку мы принимает эти 3 токена + 1 новый, так как для полной цепочки тоже был запущено параллельное выполнение. Даже если будут отвергнуты все токены черновика, то +1 токен будет сгенерирован основной моделью всё равно, поэтому такой вариант не дает просадки скорости, при условии, что запаса вычислительных ресурсов хватает. lossless или токены без потери качества MTP, Eagle3, DFlash должны работать с идентичным качеством. Но как подтвердить, теряется ли качество при использовании спекулятивного декодирования или нет? В интернете бытуют разные мнения на этот счет, и я сам не был уверен до конца в этом вопросе. И чтобы разобраться с этим, заглянем в исходники llama.cpp и посмотрим, как реализован механизм принятия токенов. Нас интересуют файл sampling.cpp, где происходит фаза верификации:
std::vector<llama_token> common_sampler_sample_and_accept_n(struct common_sampler * gsmpl, struct llama_context * ctx, const std::vector<int> & idxs, const llama_tokens & draft, bool grammar_first) { GGML_ASSERT(idxs.size() == draft.size() + 1 && "idxs.size() must be draft.size() + 1"); std::vector<llama_token> result; result.reserve(idxs.size()); size_t i = 0; for (; i < draft.size(); i++) { const llama_token id = common_sampler_sample(gsmpl, ctx, idxs, grammar_first); common_sampler_accept(gsmpl, id, true); result.push_back(id); if (draft != id) { break; } } if (i == draft.size()) { const llama_token id = common_sampler_sample(gsmpl, ctx, idxs, grammar_first); common_sampler_accept(gsmpl, id, true); result.push_back(id); } return result; }
- Для каждого токена draft основная модель вычисляет вероятности.
- Сэмплер выбирает токен.
- Если выбранный токен не совпадает с draft, то цикл сразу прерывается.
- Если совпадают, то токен добавляется в финальную последовательность.
Прерывание при расхождение токенов явно прописано в коде, никак исключений или вероятностей тут не предусмотрено:
if (draft != id) { break; }
Это гарантирует идентичность результата, спекулятивное декодирование сохраняет lossless качество при использовании черновика. Как установить llama.cpp и выжать из неё больше В предыдущей статье я уже писал о том, как установить свежую llama.cpp и как добиться лучших показателей скорости, в целом это простая процедура, но чтобы не повторяться: Выжать больше из локальных LLM. Ollama медленнее llama.cpp в 3 раза. UD_Q4_K_XL лучше чем Q4_K_M, а вес тот же и т.д
 Добавлю только, что вам стоит попробовать не только кванты UD, но и кванты от Bartowski, которые могут показать себя лучше: https://huggingface.co/bartowski Где взять кванты для MTP, Eagle3, DFlash До появления поддержки MTP, из моделей при создании GGUF квантов слои MTP вырезались, чтобы они не занимали лишнее место. Все кванты находятся на https://huggingface.co/ Нужно выбирать модели, которые имеют в имени MTP:

qwen3.6 mtp Тоже самое с Eagle3 и DFlash:

qwen3.6 eagle3 и dflash Для Qwen3.6 MTP можно взять кванты от Unsloth:
Как запустить MTP В актуальной llama.cpp по умолчанию включен режим fit, который автоматически всё настроит для максимально эффективной загрузки GPU, поэтому настраивать fa, ncmoe, ngl не требуется. Имеет смысл выбирать ncmoe или cmoe, или настроить -fitt N (указать сколько свободной VRAM оставлять в МБ при автонастройке), если вам нужна VRAM для других задач. Модель со слоями MTP по умолчанию запуститься как обычная модель, чтобы активировать MTP нужно явно указать --spec-type draft-mtp, и через --spec-draft-n-max 4 указать сколько следующих токенов предсказывать. Размер draft-n-max стоит подбирать в диапазоне 2-6. \llama-server -m "Qwen3.6-27B-MTP-UD-Q4_K_XL.gguf" --spec-type draft-mtp --spec-draft-n-max 4 -fitt 4096 Сравнение скорости У Qwen3.6 есть 4 головы MTP, поэтому эффективное значение должно быть около 4. Во всех случаях промпт с реддита:
Write a single HTML file with a full-page canvas and no libraries. Simulate a realistic side-view of a moving car as the main subject. Keep the car visible in the foreground while the background landscape scrolls continuously to create the feeling that the car is driving forward. Use layered scenery for depth: nearby ground, roadside elements, trees, poles, and distant hills or mountains should move at different speeds for a natural parallax effect. Animate the wheels spinning realistically and add subtle body motion so the car feels connected to the road. Let the environment pass smoothly behind it, with repeating but varied scenery that makes the movement feel believable. Use cinematic lighting and a cohesive sky, such as sunset, dusk, or daylight, to enhance atmosphere. The overall motion should feel calm, immersive, and realistic, with a seamless looping animation.
 Qwen3.6 27B UD-Q4_K_XL. Результат для кода в диапазоне draft-n-max от 1 до 7:

MTP --spec-draft-n-max 4 ускоряет задачи программирования в 2 раза Спекулятивное декодирование работает для разных задач по разному, поэтому нет универсального значения для MTP. Проведем тестирование перевода, сочинение истории на русском и работа в агенте, задание перегнать старый opengl проект в html.

3 типа задач на Qwen3.6-27B-UD-Q4_K_XL с MTP и без Для Qwen3.6-35B-A3B-UD-Q4_K_XL, где активных параметров всего 3B, эффективность не столь выражена, для кода всего 33%:

Уровни MTP от 1 до 7 для Qwen3.6-35B-A3B-UD-Q4_K_XL На переводе ускорение незначительно, а вот на сочинении истории идет деградация:

3 типа задач на Qwen3.6-35B-A3B-UD-Q4_K_XL с MTP и без Замеры проводились на 5090, на другом оборудовании цифры и проценты будут другими. Выводы MTP работает лучше чем черновая маленькая модель, так как обучалось вместе с моделью и новые токены извлекаются из скрытого состояния оригинальной модели, и само MTP весит меньше черновых моделей. Ускорение зависит от архитектуры модели, от количества активных параметров:
- Для Dense, где все параметры активные, дает более стабильное ускорение, даже на задачах творчества, для программирования ускорение достигает 2х раз, для творческих задач 33%, для перевода 83%.
- Для MoE моделей с малым числом активных параметров MTP работает не столь эффективно, 33% для кода, для перевода 12% и неожиданное - деградация на творческий задачах на 10%.
Для моделей которые не имеют MTP есть альтернативные варианты: EAGLE-3 и DFlash. Изучение исходного кода llama.cpp подтвердило, что качество генерации не страдает и остается lossless при включении MTP.-Источник
|