Релиз DeepSeek R2 официально отложен и пока R1 не потерял актуальность, попробуем запустить модель на домашнем ПК. Оригинальная DeepSeek R1 имеет размер 700гб, так как она обучалась в fp8, но если бы она обучалась в стандартных f16, её вес был бы 1400гб, а мы попробуем версию в 10 раз меньше. Запустим самый маленький 1.66-битный IQ1_S_R4 квант полноценной модели размером 130гб на игровом ПК, отдельно с 4090 и 4060ti. Загрузим туда очень-очень много контекста и проверим, такой квант всё ещё способен давать разумные ответы или нет.

Как запускать

Почти все массовые локальные модели запускаются одинаковым образом, есть движок llama.cpp и формат этого движка gguf, с различными вариантами квантования, и есть оболочки, которые под капотом запускают тот самый llama.cpp - это и ollama, и LM Studio и все остальные.

Чтобы просто запустить локально любую небольшую модель, достаточно скачать LM Studio, Jan или, если нужен более гибкий функционал, text-generation-webui, потом любым способом скачать gguf файл и запустить всё в пару кликов. Это будет работать локально и займет несколько минут на разобраться. Всё это работает и на nvidia, и на amd, и на intel, там где нет CUDA, отлично работает через Vulkan.

Но сегодня нас интересует кое-что посложнее, запустить настоящую большую DeepSeek R1-0528 размером 671B на домашнем игровом ПК. Это запуск не б/у сервере, не на каком-то специфичном дорогом железе, не на куче видеопамяти, а на обычном ПК.

Запускать будем не обычное квантование вроде Q4_K_M или IQ1_S, и не динамическое квантование UD-...-XL, которое превосходит обычные кванты. Нас интересует sota квантование iq4_ks и R4, которое работает только в ik_llama.

ik_llama.cpp - это форк от llama.cpp, который улучшает производительно на CPU и имеет расширенную поддержку MoE моделей, а так же является создателем передовых новых квантов. Именно через iq4_ks и R4 стало возможно создать на столько маленький квант, который ещё может показывает адекватные результаты и влезает в домашний ПК.

На чём запускать

Нам нужно много памяти, минимум можно попробовать 128гб, сейчас комплект из 4х модулей памяти 48гб DDR5 для домашних ПК стоит в пределах 50к, DDR4 4x32гб в 2 раза дешевле, и в продаже также начали появляться недорогие модули 2x64гб. Можно сказать, что 128/192гб ram это уже вполне доступное железо.

Сами характеристики ПК не на так важны, если там есть 6-8 ядер, важнее объем памяти и наличие 1 GPU, что является ключевым фактором для ускорения. Когда используются именно 4 модуля по 48гб, они плохо держат разгон и не стартуют на XMP, но хватит и того, что они запускаются на базовой частоте 4800.

Характеристики испытуемого ПК:

  • CPU: i7-14700

  • Материнка: GIGABYTE Z790 D AX

  • ОЗУ: 4x 48gb Kingbank DDR5 4800 MT/s

  • GPU: 4060 Ti 16gb, 4090 24gb

Сравним отдельно 4090 и 4060 ti (хотя сейчас уже актуальнее 5060 Ti 16гб, у неё в 1.5 раза быстрее память чем у 4060ti, а стоит столько же), чтобы понять влияние GPU на скорость.

Что запускать? Какой квант?

DeepSeek-R1-0528-IQ1_S_R4 и DeepSeek-V3-0324-IQ1_S_R4

Нас интересует репозиторий ubergarm/DeepSeek-R1-0528-GGUF - пока единственный кто предоставляет готовые кванты для ik_llama. Среди них нам нужен самый маленький размером 130гб - это IQ1_S_R4.

Если не хотите ждать долгих рассуждений от R1, то можно взять V3-0324, для него тоже есть такой квант: DeepSeek-V3-0324-IQ1_S_R4

Квант экстремально малого размера, и замер качества через PPL показывают, что он ощутимо отстает от оригинала.

Чем ниже PPL, тем лучше
Чем ниже PPL, тем лучше

Но надежда есть, так как PPL не лучший показатель, он не отображает реальное качество кванта, и замеры KLD намного лучше отображают как квант далек от оригинала.

Вот сравнение 3 малых квантов: от Bartowski, Unsloth и Ubergarm, каждый со своей версией минимального размера. Все показатели чем ниже, тем лучше. Квант R4 имея самый маленький размер, показывает что обладает каким-то качеством:

KLD отклонения от Q8_0, чем ниже, тем лучше
KLD отклонения от Q8_0, чем ниже, тем лучше
Про разницу PPL и KLD

В работе Accuracy is Not All You Need (https://arxiv.org/abs/2407.09141) показали, что KLD лучше отображает корреляцию между ошибками квантования и метрикой KLD, чем PPL, так как PPL скрывает ошибки квантования из-за усреднения.

PPL (Perplexity) - это степень неуверенности модели в предсказании токена, чем ниже, тем увереннее модель. PPL усредняет логарифмические вероятности по всем токенам, поэтому ошибки, например, завышение вероятности одних токенов и занижение других, могут компенсировать друг друга - в результате PPL близок к оригиналу, хотя результат искажен. Ещё PPL слабо реагирует на ошибки в редких токенах, важных для генерации разнообразных ответов.

KLD (KL Divergence) измеряет расхождение между распределениями исходной и квантованной моделей для каждого токена, потом суммирует расхождения для всех токенов. Тут ошибки никак не компенсируются друг другом, отклонения в вероятностях редких и частых токенов одинаково повлияют на итог. Это куда лучше позволяет оценить потери при квантовании, и если оптимизировать квантование под минимизацию KLD, то в среднем это улучшает кванты.

Замеры скорости памяти

Скорость памяти прямо пропорциональна скорости генерации.

2 модуля памяти, даже модули 48гб, обычно хорошо разгоняются и держать XMP 6400, на такой частоте можно получить почти 100гб/с.

Но так как нам нужно больше памяти, то замерим скорости работы на 4х модулях. Скорость чтения на 4x DDR5-4800 равна 70 Гб/с, это не очень быстро, это ближе к скорости хорошей DDR4, чем к DDR5-6400, но этого должно хватить.

Проверим скорость Gemma3, генерация только на CPU, в стандартном кванте Q4_K_M. Запуск обычной llama.cpp на Windows 10, планировщик не оптимизирован на работу с малыми и большими ядрами:

.\llama-bench -m "gemma-3-12b-it-Q4_K_M.gguf" -t 4 -t 6 -t 8 -t 20 -t 28

Gemma3 12B Q4_K_M
Gemma3 12B Q4_K_M

.\llama-bench -m "gemma-3-27b-it-Q4_K_M.gguf" -t 4 -t 6 -t 8 -t 20 -t 28

Gemma3 27B Q4_K_M
Gemma3 27B Q4_K_M

pp - это promp processing, он же prefill. В pp входит системный промпт и вся история диалога. До тех пор пока контекст не закэширован, всё будет считаться от самого начала.

tg - это token generation, генерация новых токенов, обычно все обращают внимание только на этот показатель, но на огромном контексте pp будет так же важен.

Как запускать R1 671B на одной GPU и за счёт чего ускорение

Память не очень быстрая, даже Gemma3 12B весом 7гб еле выходит за границу комфортности, которая составляет 5 t/s. В таких условия нам нужно запустить квант весом 130гб имея для ускорения всего 1 GPU и на сколько это вообще возможно.

Gemma3 это dense-модель, то есть сплошная, для каждого нового токена нужно обойти все параметры модели. DeepSeek V3/R1 - это MoE модель, где на каждом шагу использует только часть параметров.

MoE это сокращение архитектуры Mixture of Experts, в таких моделях количество параметров (B) всей модели больше чем количество активных параметров (AxB) необходимых для каждого нового токена. Например, модель Qwen3-235B-A22B имеет всего 235B параметров и на каждом шагу из них только 22B будут активными.

У R1 количество параметров 671B, активных параметров 37B. Всего 61 слой, 3 слоя общих, которые используются на каждом шагу, остальные слои экспертов, они выбираются роутером на каждом шагу разные, поэтому нельзя просто загрузить 37B в vram.

Если модель целиком влезает в память, то скорость инфересна будет примерно равна dense-модели размером 37B, а именно в районе 2 t/s. Это генерация на такой скорости памяти, была бы память быстрее, то и генерация была бы быстрее. 2 t/s мало, нужна помощь от GPU, но если просто выгрузить 10 слоев в vram, то будет ситуация, когда только первые 3 слоя полезны, остальные будут выпадать лишь иногда.

Выгрузив часть слоев ускорение есть, но совсем не существенное, нужно больше:

Решение: override-tensor.

Чтобы получить существенное ускорение, нужно чтобы GPU полноценно участвовала в работе на каждом шагу и решение тут в том, чтобы на GPU выгрузить только веса внимания, которые легкие и на каждом шагу важны. А экспертные ffn каждого слоя оставить на CPU.

В llm трансформерах модель состоит из слоев, каждый слой состоит из 2х видов тензоров: внимания (attn, attention) и полносвязной сети (ffn, feed forward network). Посмотреть структуру модели можно на huggingface если нажать на конкретный квант.

18-й слой deepseek r1, самые тяжелые тензоры это ffn, а важные attn намного легче
18-й слой deepseek r1, самые тяжелые тензоры это ffn, а важные attn намного легче

Тензоры внимания весят не очень много, и в таком низком кванте они влезают даже в 10гб видеопамяти, оставляя достаточно места для контекста, а ffn экспертов и составляют основной объем модели.

--override-tensor или -ot

Для того, чтобы разнести разбить слой на тензоры в llama.cpp и ik_llama добавили параметр -ot или --override-tensors , через него указывают какие тензоры отправятся на CPU (или другие устройства, например, вторую GPU) используя regexp синтаксис.

Нужно выгрузить все легковесные тензоры на GPU, а тяжелые, которые упрощенно называют MoE-параметрами, на CPU. Чтобы это сделать, нужно сначала выгрузить все слои на видеокарту через параметр -ngl 999 , а потом указываем какие надо перенаправить в обычную память.

MoE-параметры это те, которые имеют в имени exps, то есть эксперты, поэтому нужно просто указать один из синонимов, который выберет всех exps:

-ot exps=CPU

-ot ".ffn_.*_exps.=CPU"

-ot "([0-9]+).ffn_.*_exps.=CPU"

Точные имена тензоров, чтобы составить правильный regexp
Точные имена тензоров, чтобы составить правильный regexp

Теперь на видеокарте остались какие-то тензоры каждого слоя, и gpu на каждом шаге будет участвовать в генерации, за счет этого и происходит ускорение. И это в основном подходит только для MoE моделей.

И, обычно, если осталась свободная VRAM, или не нужно так много контекста, или есть вторая видеокарта, то можно больше тензоров оставить выгруженными на видеокарте.

-ot "blk\.([4-9])\.ffn.*=CUDA0" -ot "blk\.(1[0-5])\.ffn.*=CUDA1" -ot exps=CPU

Так мы оставим 4-9 слои целиком на 1 GPU и 10-15 слои на 2 GPU. Для rocm или vulkan будут свои синонимы названия устройств, вроде Vulkan0 вместо CUDA0.

В кванте IQ1_S_R4 для тензоров ffn up|gate|down использованы тензоры R4, которые оптимизированы для работы на CPU, их нужно автоматически конвертировать в IQK квант пригодные для GPU. Для этого ik_llama надо скомпилировать с флагом -DGGML_CUDA_IQK_FORCE_BF16=1

Запускаем DeepSeek R1 671B IQ1_S_R4

У ik_llama нет готовых бинарников, как у llama.cpp, поэтому нужно будет собрать её из исходников, делается это по той же инструкции как и llama.cpp, поэтому сложность не должно возникнуть.

Если одна GPU, вместо -j28 указать ваше количество ядер или потоков:

git clone https://github.com/ikawrakow/ik_llama.cpp
cd ik_llama
cmake -B ./build -DGGML_CUDA=ON -DGGML_BLAS=OFF
cmake --build build --config Release -j28
cd build/bin

Если планируется использовать несколько GPU:

git clone https://github.com/ikawrakow/ik_llama.cpp
cd ik_llama
cmake -B ./build -DGGML_CUDA=ON -DGGML_BLAS=OFF -DGGML_SCHED_MAX_COPIES=1 -DGGML_CUDA_IQK_FORCE_BF16=1
cmake --build build --config Release -j28
cd build/bin

Если не указать -DGGML_SCHED_MAX_COPIES=1, то будет перерасход видеопамяти с использованием -ot. -DGGML_CUDA_IQK_FORCE_BF16=1 нужен для выгрузки на GPU ffn тензоров, но не всегда дает ускорение, иногда замедляет работу, зависит от модели видеокарты.

После этого можно запустить llama-server:

./llama-server -m "DeepSeek-R1-0528-IQ1_S_R4-00001-of-00003.gguf" -mla 3 -fa -ctk q8_0 -amb 512 -fmoe -ot exps=CPU -ngl 99 -b 4096 -ub 4096 -t 20 -c 8192

По адресу http://127.0.0.1:8080/ будет доступен вполне удобный веб-клиент:

2184 токенов размышления, модель долго искала подвох, ответ 87 токенов. Скорость 7 t/s
2184 токенов размышления, модель долго искала подвох, ответ 87 токенов. Скорость 7 t/s

Мы получили 7 t/s, это в 2 раза выше, чем мы получали когда вынесли только начальные слои на GPU, хотя в обоих случая количество занимаемой памяти подобрано одинаково, что показывает, что подход через -ot работает.

Подробнее про параметры запуска:

-m - путь до файла модели, у huggingface есть ограничение на размер файла 50гб, поэтому файлов будет несколько разбитых по шаблону 00001-0000x.gguf. Для запуска нужно указывать только 1 файл.

-fa - flash attention, способ эффективнее считать контекст, тратя меньше памяти, при правильной реализации математически идентичен обычному attention.

-amb 512 - переиспользовать буфер для вычисления K*Q, размер в mb, можно увеличить, если хватает памяти.

-fmoe - fused moe, объединяет up, gate и act операции, немного ускоряя вычисления.

-mla 3 - включить mla

-ctk q8_0 - квантование только k-кэша контекста, считается, что он почти не страдает от квантования, в отличии от v-кэша.

-ngl 99 - выгрузить все слои на GPU.

-ot exps=CPU - отправить все тензоры где в имени exps на CPU.

-b 4096 -ub 4096 - оптимизация размеров батчей, может ускорить вычисление pp.

-t 20 - использовать все ядра, что есть, по умолчанию 8.

-c 8192 - задать размер контекста 8к, по умолчанию 4к.

Дополнительные параметры:

-rtr - если запуск CPU only, то при загрузке конвертировать веса в оптимизированные для работы на CPU, отключает mmap

-ser 6,1 - умное уменьшение количества экспертов, ускоряет работу за счет небольшого снижения качества.

-ts 24,16 - если установлены две gpu, то можно распределить по ним слои в заданной пропорции, полезно для dense-моделей, если используется -ot, то лучше не использовать.

Бенчмарк скорости DeepSeek R1

Теперь можно заняться более точным измерением скорости генерации как новых токенов, так и уже существующего контекста. У ik_llama есть удобный инструмент для замера скоростей модели на указанной длине контекста.

Сравним 3 варианта, вначале на типичном контексте 4к, параметры -b и -ub по-умолчанию:

  • CPU only (скорость памяти 72 гб/с)

  • Ускорение через 4060 ti 16гб (скорость памяти 288 гб/с)

  • Ускорение через 4090 24гб (скорость памяти 1008 гб/с)

N_KV - размер контекста.

T_PP - время генерации PP.

S_PP - скорость генерации pp.

S_TG - скорость генерации tg.

CPU only, скрываем все CUDA устройства через CUDA_VISIBLE_DEVICES="":

CUDA_VISIBLE_DEVICES="" ./llama-sweep-bench -m "DeepSeek-R1-0528-IQ1_S_R4-00001-of-00003.gguf" -mla 3 -fa -amb 512 -fmoe -ctk q8_0 -t 28 -c 4096

4060 ti:

CUDA_VISIBLE_DEVICES="1" ./llama-sweep-bench -m "DeepSeek-R1-0528-IQ1_S_R4-00001-of-00003.gguf" -mla 3 -fa -amb 512 -fmoe -ot exps=CPU -ngl 99 -ctk q8_0 -t 28 -c 4096

4090:

CUDA_VISIBLE_DEVICES="0" ./llama-sweep-bench -m "DeepSeek-R1-0528-IQ1_S_R4-00001-of-00003.gguf" -mla 3 -fa -amb 512 -fmoe -ot exps=CPU -ngl 99 -ctk q8_0 -t 28 -c 4096

Разница между 4060 ti и 4090 не соответствует разнице производительности и скорости памяти. Всё потому, что на GPU уходит всего ~10 гб тензоров и при маленьком батче разница между видеокартами не так заметна.

Теперь замерим контекст 32к, увеличим размер батчей, параметры -b 4096 -ub 4096. Увеличивая размер батчей, мы увеличим скорость PP, что важно при работе с большим контекстом, когда обработка 100к может занимать почти час на 25 t/s.

4060 ti:

4090:

Тут разница на подготовку промпта между картами уже видна лучше, скорость памяти 1 Тб/с против 288 Гб/с - всё-таки разница большая. На скорости 1 Тб/с для такого небольшого объема данных уже и количество ядер может быть важным.

На 4090 скорость tg немного упала, это не критично, зато скорость pp выросла почти в 10 раз до 200-300 t/s. С такой скоростью уже можно обрабатывать огромные контексты. На 4060 ti pp вырос всего в 1.5-2 раза, это тоже не плохо, обработка 32к контекста займет 15 минут, а дальше она закэшируется и будет работать моментально.

Параметром -ser 6,1 можно компенсировать потери tg от -ub 4096 -b 4096, сохраняя 300 t/s на pp, и на коротком контексте возвращая 8 t/s на tg:

Как вместить огромный контекст в одну GPU?

Как получить ускорение через GPU разобрались. Теперь нужно разобраться как вместить огромный контекст в тот небольшой объем vram, который остаётся.

У DeepSeek R1 максимальный контекст 160к, это очень много, и в обычном виде на это требуется сотни гб памяти. Например, согласно исследованию Quantitative Analysis of Performance Drop in DeepSeek Model Quantization, для всего лишь 32к контекста нужно 400гб памяти на обычной llama.cpp.

Согласно тому же исследованию, качество кванта UD-Q2_K_XL (это динамическое квантование от Unsloth, главная особенность этого квантования в том, что важные тензоры оставлены в очень высоком качестве, а менее важные квантуются сильнее, за счет этого общее качество остается на высоком уровне) по сравнению с оригиналом FP8 падает всего на несколько процентов в бенчмарках:

Только актуальная версия UD-Q2_K_XL занимает уже 233гб, а не 212гб, слишком много, но можно попробовать запустить её с ssd, это критично снизит скорость PP, но для средних контекстов всё еще может работать, например, 32к ждать пришлось минут 40:

DeepSeek-R1-0528-UD-Q2_K_XL запуск частично с ssd, обработка 32к контекст, скорость 2.3 t/s
DeepSeek-R1-0528-UD-Q2_K_XL запуск частично с ssd, обработка 32к контекст, скорость 2.3 t/s

Кванты от ubergram по сути такое же динамическое квантование, только с использованием более продвинутых IQK и R4 квантов.

Но возвращаясь к тому, как получилось, что мы уже и тестировали и запускали 32к и для этого не понадобилось 400гб памяти, как это работает?

MLA и Внимание

Контекст так много весит, потому что он полностью связан, и постоянно динамически вычисляет важность токенов через механизм внимания. В отличии от таких статистических предсказателей следующего токена, вроде цепей Маркова, где предыдущее значение контекста не играет роли, играет только предыдущий токен, если это цепь 1 порядка, в трансформерах механизм внимания на каждом шагу работает со всей последовательностью, чтобы постоянно вычислять важность токенов и перепроверять, что ответ правильный.

Механизм Внимания - это квадратичная сложность O(n²) и по памяти и по времени, где n-длина последовательности, каждый токен взаимодействует с каждым токеном, поэтому расход памяти так быстро растет. Чтобы справиться с этим, придумывают различные математические оптимизации, один из них это Flash Attention - используя математические трюки с матрицами уменьшается требования к памяти без потерь качества, результат идентичен обычному attention.

Или другой подход - Sliding Window Attention (SWA), когда токену ограничивают область видимости, например каждый токен видит вокруг себя только вокруг себя в пределах окна, которое обычно 4096 токенов, за счёт этого можно обрабатывать очень длинные последовательности используя мало памяти, но взамен теряется информация вне окна, что можно частично компенсировать различными техниками.

В DeepSeek пошли другим путем, они попытались изменить сам подход к attention. Обычно для оптимизации внимания используют, например, Grouped Query Attention (GQA) или Multi-Query Attention (MQA), эти методы являются вычислительными оптимизациями стандартного механизма без фундаментального изменения архитектуры. Вместо такого подхода в DeepSeek разработали MLA (Multi-Head Latent Attention), где роль для внимания играет не токен, а латентный вектор.

MLA - это обучение скрытых или латентных векторов вместе с основной моделью, эти вектора учатся улавливать ключевые концепции и паттерны в данных. Головы внимания в MLA взаимодействуют не напрямую с токенами, а с этими латентными векторами. За счет этого получается "ужать" KV-кэш в 25 раз сохраняя оригинальное качество:

MLA представили в DeepSeek V2: https://arxiv.org/abs/2405.04434
MLA представили в DeepSeek V2: https://arxiv.org/abs/2405.04434

И причина, почему в том исследовании им потребовалось 400гб для 32к контекста в том, что на момент исследования в llama.cpp не была реализована поддержка MLA.

Кроме MLA, у DeepSeek есть ещё одна интересная технология - MTP.

MTP - это их реализация спекулятивного декодирования, способ переложить часть работы по предсказываю следующего токена на маленькую модель, это работает, когда продолжение слова или фразы уже очевидно. Если зайти на официальный репозиторий deepseek-ai, то размер модели будет 685B, а не 671B. Как раз 14B это модуль MTP.

Model size: 685B
Model size: 685B

MTP, в отличии от обычного спекулятивного декодирования, тоже обучалось вместе с модель. По их замерам точность принятия токенов от MTP 85-90%, что дает ускорение основной модели в 1.8 раза.

В llama.cpp сейчас есть реализация спекулятивного декодирования, но она сделана по другому, там в качестве маленькой модели нужна полноценная модель того же семейства. Например, Gemma3 27B в качестве помощника может использовать только что-то из своих младших моделей, вроде Gemma3 1B.

Сколько нужно памяти под контекст используя MLA

В ik_llama, чтобы включить использование mla нужно добавить параметр -fa -mla 3 .

В llama.cpp все виды внимания (mla, swa) используемые конкретной моделью включаются через -fa автоматически, если их поддержка уже добавлена в ядро.

Тензоры оставленные с -ot для кванта IQ1_S_R4 примерно равны 10.5гб, под нужны ОС, включая браузер, на 4090 уходит около 1.7гб. В итоге есть примерно 5гб и 11гб под контекст на 4060ti и 4090 соответственно. Перебором параметра -c можно найти количество контекста, которое влезает в этот объем. Чем больше размер батча, тем больше под них нужно памяти.

Размер батчей стандартный -b 2048 -ub 512:

Тензоры выгружены на GPU, b/ub стандартные -b 2048 -ub 512
Тензоры выгружены на GPU, b/ub стандартные -b 2048 -ub 512

Размер батчей -b 4096 -ub 4096:

Тензоры выгружены на GPU, под контекст остатки памяти, -b 4096 -ub 4096
Тензоры выгружены на GPU, под контекст остатки памяти, -b 4096 -ub 4096

Как вариант, можно не выгружать тензоры на GPU совсем, оставляя всю память под контекст. Для этого надо указать-ngl 0. Максимальные 160к без квантования требуют примерно 14гб при стандартном размере батчей и 18гб при 4096.

Запустим тот же бенчмарк, контекст 160к, все тензоры теперь на CPU:

CUDA_VISIBLE_DEVICES="0" ./llama-sweep-bench -m "DeepSeek-R1-0528-IQ1_S_R4-00001-of-00003.gguf" -mla 3 -fa -amb 512 -fmoe -ot exps=CPU -ngl 0 -ctk q8_0 -t 28 -c 163840 -ngl 31 -b 4096 -ub 4096

Скорость tg упала до скорости 2.85 t/s и соответствует "CPU only". Но с ростом контекста она быстро упадет до 1, а под конец и до 0.5 t/s. А скорость PP осталась как и была на GPU, около 200-300 t/s, но под конец 160к она упадёт до 75 t/s.

Теперь проверим "сложение" двух GPU. Ожидание от двух карт такое, что можно разместить количество контекста сумме их отдельных размеров, то есть в 126к. Но на практике объединив две GPU можно уместить всего 110к с квантованием q8_0, что не совпадает с расчетами. Дело в том, что единственная польза от двух карт в том, что мы разделяем тензоры, отправляя 6гб на gpu1 и 3гб на gpu2, тем самым освобождая память для контекста, а сам контекст создается на двух GPU почти пропорционального размера, то есть не получится сложить 40к + 86к. Логика работы двух GPU и контекста мне пока не понятна.

Замер скорости двух GPU и контекста 110к:

./llama-sweep-bench -m "DeepSeek-R1-0528-IQ1_S_R4-00001-of-00003.gguf" -mla 3 -fa -amb 512 -fmoe -ot exps=CPU -ngl 99 -ts 20,10 -b 4096 -ub 4096 -ctk q8_0 -t 28 -c 112640

4090 + 4060ti, 110k контекста
4090 + 4060ti, 110k контекста

Скорости pp просели из-за медленной 4060, но всё еще пригодны для использования и не заставляют ждать слишком долго.

Загружаем модель и проводим тесты

Теперь когда теории достаточно, как ускориться и как вместить огромный контекст понятно, осталось придумать как его проверить. Кодовой базы на 100к у меня нет, а работать с чужой - сложно оценить результат.

Ещё один из вариантов проверить такой большой контекст - это взять знакомую книгу и протестировать модель на ней, сможет ли модель пересказать всю книгу, выдать какие-то факты из начала, середины и конца, и в целом по ответам будет понятно, модель способна обработать такой контекст или она просто выбирает случайные подходящие слова и выдает какой-то не особо связный ответ. Часто маленькие модели (7b, 12b, 14b) уже на контексте в 32к зацикливаются и просто выдают бесконечно одинаковый токен.

Нужна книга, которой точно нет в обучающем датасете, например, Лабиринт Отражений. Текст книги нужно целиком вставить в системный промпт, тогда это будет контекст модели, а потом позадавать вопросы по нему. Это будет не RAG, который разбивает текст на кусочки, создаёт векторную БД и разбивает данные на кусочки, и по ключевым словам достает эти кусочки и подмешивает их в контекст, такие кусочки не связаны друг с другом единым вниманием, поэтому такой подход подходит для документации, но не для связной книги. И это не какой-то похожий подход, это будет чистый контекст.

Проверка модели с пустым системным промптом в своем "базовом" виде. Она ничего не знает о Лабиринте Отражений, но что-то знает про цикл Дозоры, что совсем не помогает.

Пустой системный промпт
Пустой системный промпт

Стоит подсчитать количество токенов из которого состоит книга, для этого можно воспользоваться утилитой llama-tokenize, и нужно указать модель, так как у всех моделей разные токенизаторы.

linux:

./llama-tokenize -m "DeepSeek-R1-0528-IQ1_S_R4-00001-of-00003.gguf" -f book.txt | wc -l

windows powershell:

.\llama-tokenize.exe -m "DeepSeek-R1-0528-IQ1_S_R4-00001-of-00003.gguf" -f book.txt | Measure-Object -Line | Select-Object -ExpandProperty Lines

Размер книги получился 215к токенов, не влезает ни в 110к, ни даже в максимальные 160к. В таком случае, когда фактический контекст больше размера -c подключается context shift, он обрезает часть токенов и это сильно снижает точность и качество. Проверим как это работает, а потом урежем контекст книги.

В системный промпт скопирован текст всей книги, context shift включается автоматически, попросим пересказать сюжет:

Первое, что удивляет, это то, что настолько экстремально квантованная модель что-то отвечает на таком огромном контексте.

Второе - то, она отвечает что-то связное, описание в целом пересказано верно, ключевая особенность героя передана верно, ключевой момент с Неудачником описан верно. Но видно, что данные из первой половины книги не учтены, это работа context shift.

Посмотреть на то, как context shift даёт негативный эффект, можно спросив какую-то конкретную вещь из книги. Например, попросить точно процитировать эпиграф из самого начала. В ответе полностью выдуманный текст, ничего общего с оригиналом:

context shift не позволяет точно цитировать
context shift не позволяет точно цитировать

Урежем книгу до 100к токенов, теперь текст полностью влезает в контекст. Зададим тот же вопрос, и да, теперь ответ правильный:

110к, без context shift, цитата верна
110к, без context shift, цитата верна

Проверим способность MLA и модели работать не только с началом и концом, возьмём цитату из середины и попросим модель найти откуда эта цитата, описать что происходит в этот момент и процитировать диалог целиком.

В каком месте книги кто-то сказал "Звёзды слишком яркие."? Процитируй весь диалог

100к контекста, цитирование из середины книги, диалог дословно верный
100к контекста, цитирование из середины книги, диалог дословно верный

Описание событий правильное, цитирование диалога правильное. Ошибка только в том, что это глава 010, а не 011. Возможно модель сбивает то, что нумерация глав в книге представлена в двоичном виде.

160к контекста на одной 4090

Осталось только проверить максимальный контекст для DeepSeek R1, который составляет 160к. Контекст PP будет считаться на GPU, а новые токены ответа TG на CPU, и сколько останется памяти, догрузим GPU целыми слоями.

Если в предыдущем эксперименте скорости были довольно комфортные, то тут скорость будет очень низкой. Это больше про посмотреть, остаётся ли ответ модели разумным на максимальном контексте в таком экстремальном квантовании.

./llama-server -m "DeepSeek-R1-0528-IQ1_S_R4-00001-of-00003.gguf" -mla 3 -fa -amb 512 -fmoe -ot exps=CPU -ngl 18 -ts 24,0 -ctk q8_0 -ctv q8_0 -b 4096 -ub 4096 -t 28 -c 163840

Удалось выгрузить 18 слоев, для большей экономии включил -ctv q8_0, по идее это не должно сказаться на качестве, а несколько гб высвободиться.

Загрузка контекста успешно проходит до 128к и тут мы сталкиваемся с первой проблемой:

```cpy.cu:573: GGML_ASSERT(ggml_nbytes(src0) <= INT_MAX) failed```

Логи прогрузки 160к контекста
Логи прогрузки 160к контекста

Из-за размера батча 4096 мы достигли и превысили лимит INT_MAX. Возможно это исправить или нет, пока не ясно, поэтому придется снизить -b -ub, что замедлит скорость PP, но на данный момент это единственный способ загрузить столько контекста. Уменьшая размер батча, высвободиться видеопамять, значит можно выгрузить больше слоев на GPU, что может немного поможет при генерации ответа:

./llama-server -m "DeepSeek-R1-0528-IQ1_S_R4-00001-of-00003.gguf" -mla 3 -fa -amb 512 -fmoe -ot exps=CPU -ngl 30 -ts 24,0 -ctk q8_0 -ctv q8_0 -b 2048 -ub 2048 -t 28 -c 163840

Скорость PP под конец упала до 75 t/s, а скорость генерации составила всего 0.5 t/s:

Зато можно убедиться, что на 160к модель всё еще не утратила связь с контекстом, и сам ответ правильный в пределах этих 160к, так как развязка осталась в оставшихся 55к.

Чтобы воспользоваться всем контекстом и выгрузить все слои на GPU не хватило буквально 4-5гб, поэтому эту работу оставим для 5090, а для 4090 пределом будет 80к.

Бонус. Огромный контекст на LLama 4 и Gemma3

DeepSeek не единственная модель, в которую можно загрузить много контекста, но единственная у которой есть MLA. Для других моделей стандартным решением является SWA - скользящее окно внимания.

Недавно в llama.cpp добавили поддержку SWA, которое позволяет обрабатывать огромные последовательности требуя мало памяти. Качество должно быть ниже чем у MLA, так как уходя за пределы окна происходит очистка SWA-кэша и, после определенной фиксации в пределах окна, забывание токенов данных, но проверить всё равно можно. Этой поддержки пока нет в ik_llama, поэтому запускать надо на llama.cpp.

SWA работает и для Llama 4 и для Gemma3, включается через -fa, есть возможность включить --swa-full, в этом режиме размер кэша SWA равен полному контексту, очищения SWA кэша не происходит, но памяти потребует намного больше. При использовании SWA context shift автоматически отключается.

У Llama 4 Scout (108B-A17B) размер контекста 10м, У Llama 4 Maverick (401B-A17B) - 1м. Этого хватит, чтобы вместить книгу целиком. У Gemma3 27B только 128к, но она славится тем, чем её контекст очень тяжелый, поэтому SWA должен с этим помочь.

Gemma3 это Dense модель, а Llama 4 это MoE, у Scout 16 экспертов, а у Maverick 128. У Llama 4 есть много общих слоев, поэтому эти модели выдают очень хорошую скорость при использовании -ot, а Gemma3 в целом легковесная.

Для начала посмотрим сколько токенизаторы Llama 4 и Gemma3 найдут токенов у книги:

./llama-tokenize -m "Llama-4-Maverick-17B-128E-Instruct-UD-Q3_
K_XL-00001-of-00004.gguf" -f "labir_otra.txt" | wc -l

> 182120

.\llama-tokenize.exe -m "gemma-3-27b-it-Q4_K_M.gguf" -f "labir_otra.txt" | Measure-Object -Line | Select-Object -ExpandProperty Lines
> 190549

177к токенов у Llama4 и 186к у Gemma3. Видимо у них токенизатор лучше подходит для текста на русском, но как это скажется на качестве пока не ясно.

Модели запускаем через llama.cpp, логика такая же как и раньше, только нужно убрать параметры которых нет в llama.cpp. Модели не запускаются с -fa, если квантование кэшей не синхронно, поэтому придётся указать и -ctv q8_0:

Llama 4 Maverick

Максимально можно вместить 350к контекста в стандартных размераз ub/b и примерно 210к для -ub 3072 -b 3072

Квант UD-Q3_K_XL.

./llama-server -m "Llama-4-Maverick-17B-128E-Instruct-UD-Q3_K_XL-00001-of-00004.gguf" -fa -ctk q8_0 -ctv q8_0 -c 215040 -ot exps=CPU -ngl 99 -ts 24,0 -t 28

Модель правильно собрала вместе части, но в 3 части 8 глав, а не 4. Видимо модель сбивает нумерация в бинарном виде, даже если явно ей это сказать.

Проверим точность цитирования из середины. Цитата точная, описание не совсем точное. Глава указана правильно, а название части модель выдумала по тому, где находятся герои, правильное название "ЧАСТЬ ВТОРАЯ. ЛАБИРИНТ":

Llama 4 Maverick, книга целиком в контексте
Llama 4 Maverick, книга целиком в контексте

Попробуем запутать модель, она должна понять, что пивной ларёк это на самом деле бар и упомянуть про "Ждите отстоя пены".

llama 4 maverick, ответы правильные
llama 4 maverick, ответы правильные

С этим модель справляется успешно. То есть в пределах SWA окна у модели нет проблем.

Теперь вопрос на контексте 32к, чтобы на нём же протестировать более маленькие модели.

В какой момент была включена Roll Over Beethoven? Что произошло дальше?

Llama 4 Maverick, правильный ответ для тестирования
Llama 4 Maverick, правильный ответ для тестирования

Тут модель дает правильный ответ, всё это в пределах одной главы.

Небольшой тест 4060 ti, в неё влезает 70к контекста с ub/b 4096. Обработка pp очень медленная, но генерация ответа вполне быстрая.

4060 ti с контекстом 70к, скорость pp низкая, скорость tg нормальная
4060 ti с контекстом 70к, скорость pp низкая, скорость tg нормальная

Ответ правильный правильный, так как ответ снова в пределах абзатца.

4060 ti с контекстом 70к, модель правильно нашла место
4060 ti с контекстом 70к, модель правильно нашла место

Llama 4 Scout

Maverick в целом справляется, если знать особенности SWA. Но вот у Scout с этим намного хуже, ответы хаотичные и случайные, иногда попадающие куда надо. Видимо сказывается тот факт, что у Maverick 128 экспертов, а у Scout только 16.

Квант UD-Q4_K_XL.

Тот же вопрос в пределах 32к. Ответ не правильный по своей логике, Scout пишет, что герой уже в ресторане и там и взял диск, что не правильно, и после заказывает такси, чтобы поехать в тот самый ресторан, где он уже находится по версии модели. Видно, что модель слабее Maverick и не может удерживать события даже в своем ответе.

Gemma3

У Gemma3 возникает проблема на длинном контексте без квантования. Если контекст длиннее 32к, то вместо ответа получается зацикленный токен, эта проблема возникает только без квантования kv-кэша. В пределах 32к контекста ответы более менее нормальные, 32к контекст занимает 2.5гб без квантования.

Если включить квантование -ctk q8_0 -ctv q8_0, то полный 128к контекст обрабатывается и занимает ~7гб, но модель слишком маленькая, всего 27B, поэтому на таком огромном контексте она показывает себя плохо.

Квант Q8_0, модель не удерживает даже 32к контекста и выдумывает ответ из обрывков фраз книги, про то, что герой вернулся в реальность. Возможно, это проблема реализации SWA, но включение --swa-full не помогает, что говорит о том, что это проблема модели.

Gemma3 27b, 32к, ответ не правильный
Gemma3 27b, 32к, ответ не правильный

Если не хочется возиться с консолью

oobabooga / text-generation-webui

Пока text-generation-webui единственный клиент, где реализовали поддержку кастомных параметров командной строки, включая -ot. Тут нет поддержки ik_llama, но для UD-квантов DeepSeek или Llama 4 подойдет. Правда размер контекста тут ограничен 128к.

OpenAI API, Jan и Cherry Studio

При запуске llama-server у ik_llama или llama.cpp создается не только веб-клиент, но и openai compatiable api, поэтому можно пользоваться любым клиентом, который умеет подключаться к openai. Два клиента с открытым кодом, который так умеют: Jan и Cherry Studio.

К url веб-клиента нужно просто добавить /v1 и получить адрес api, который можно использовать в любом софте, в том числе и таком как Cline или Continue для разработки.

Вывод

  • Для ускорения MoE моделей (LLama 4, DeepSeek, Qwen3) нужен параметр -ot и одна GPU, это позволит получить ощутимое ускорение

  • Чтобы вместить огромный контекст в небольшой объём памяти нужно использовать MLA для DeepSeek и SWA для Llama 4 / Gemma3

  • 4060 ti позволит вмещать 32к контекста в DeepSeek и 70к в Llama 4 Maverick, но обрабатывает долговато, медленная память сказывается

  • 4090 способна вместить 80к и обрабатывать контекст на комфортной скорости 200-300 t/s, в Maverick способна вместить 210к

  • Даже на максимальном для R1 контексте 160к мини-квант отвечает нормально

Эксперимент с огромным квантом прошел лучше чем задумывался, ожидания были, что где-то уже после 8к, максимум 32к, модель совсем перестанет связно отвечать и будет много артефактов. Но не стоит ожидать, что такой маленький квант будет соответствовать качеству оригинала, хотя он и справляется лучше, чем многие другие модели.

Комментарии (6)


  1. m0xf
    29.06.2025 09:01

    Есть ли возможность увеличить контекст, используя несколько GPU?


  1. Shannon Автор
    29.06.2025 09:01

    Среди MoE моделей вчера вышла новинка Hunyuan 80B-A13B с 256к контекстом, добавление поддержки в llama.cpp в работе.


  1. Moog_Prodigy
    29.06.2025 09:01

    А что для пользователей 3060 (12) с многоядерными зионами и озу 128 ? Есть ли свет в конце туннеля? У меня полноценный дипсик работал со скоростью 0.1 t/s с подкачкой с nvme...Не, ну работал то нормально, правда неделю ждать приходилось.


    1. Shannon Автор
      29.06.2025 09:01

      А что для пользователей 3060 (12) с многоядерными зионами и озу 128 ? Есть ли свет в конце туннеля?

      Ну 130гб квант с -ot exps=CPU и -ctk q8_0 -ctv q8_0 в теории должен в притык влезть и сколько-то контекста вместить, у меня не точные цифры про 10гб, точнее это с небольшим запасом, на деле там меньше.

      Фактически 1-битная модель, это на самом деле 1.58-битная модель - это минимально возможный размер, когда есть 3 значения: -1, 0, 1, меньше уже нельзя создать квант. Даже 1-bit BitNet, это тоже 1.58-битные llm. Так что даже если эту 1.66-битную уменьшить на это чуть-чуть, особо выигрыша по размеру не будет.


  1. fleur_de_lys_felix
    29.06.2025 09:01

    Спасибо! (Лайк и подписка).

    Есть ли разница и повышение хэшрейта аж на 30% при переходе с Windows на Linux?

    Справедливо ли всё написанное для AMD/Radeon? Для каких карт ROCm есть? Можно заставить работать? Не факт что соберётся? Не взлетит? RX 7600 XT 16GB?

    Б/у-шные Tesla с Ali?

    Где находится предел выгоды перехода с 3090/4090 на "профессиональные" Radeon Pro W7000 (W7800/w7900)?

    Начали ли появляться ARM-платы (одноплатники со встроенными процессорами) c PCIe-4.0/5.0 способные задействовать больше 1ТБ памяти? Больше 100 ГБ?

    Б/у AMD Epyc по-прежнему наше "всё" - если хотим впихнуть взрослый ДикПик в оперативу целиком?


  1. evgeniy_kudinov
    29.06.2025 09:01

    Спасибо, полезная статья и подталкивает уже начать собирать своего «домового». Вопрос знатокам: если взять GeForce RTX 5060 Ti 16GB + материнку с PCIe 5.0 + AMD Ryzen 9 9950X3D + 192 ГБ DDR5, будет ли комфортно использовать модели, например, Q4 32b, а также подобные, как в статье? Перебирал разные варианты, и ниже 300 т.р. не выходит c 2мя GPU картами.