Опубликовано
EXL3 на 8 ГБ: вечер с ExLlamaV3, который закончился кодбуком
Автор: Гусев Николай [портфолио]
Темы: llm, exllamav3, quantization, moe, vram

Замерить хотел одно, нашёл другое. Разбираю вечер с ExLlamaV3 на RTX 4060 8 ГБ: почему в 8 ГБ VRAM не влезает даже 2-битная 35B MoE, при чём тут кодбуки mcg и mul1, и почему движок молчит, когда фича не работает.
Но сначала про маршрут, который привёл к ExLlamaV3. Стартовой точкой был Strata - движок, который громко обещает запуск 125B-модели на игровой карте. Обещание проверялось живым инсталлятором, и он расставил всё по местам: Strata я отложил до лучших времён. Об этом - первый раздел.
Strata: честная проверка перед покупкой билета
Strata запускает Qwen3.8-Flash-Next - MoE на 125 миллиардов параметров - на одной потребительской видеокарте плюс системная память. Инсталлятор сам проверяет железо, сам предлагает размер модели. Выглядит слишком хорошо, чтобы быть правдой, поэтому начал с проверки:
py -3 setup.py --check
GPU: NVIDIA GeForce RTX 4060, 8.0 GB VRAM - [ok]
RAM: 32 GB - [ok]
CPU: AMD Ryzen 5 8400F, AVX-512 - [ok]
Q2_0 needs ~48 GB RAM: does not fit
IQ2_XS needs ~48 GB RAM: does not fit
IQ3_XXS needs ~60 GB RAM: does not fit
IQ3_S needs ~62 GB RAM: does not fit
IQ1_M needs ~32 GB RAM: fits in the low-RAM mode
(the GPU holds ~13% of its experts, the rest stays in RAM)
Инсталлятор честен: единственный вариант для 32 ГиБ памяти - Coder (IQ1_M), экспертно-прунутая версия от ISTA-DASLab, где из 512 экспертов на слой оставлены 256, отобранных под код и агентов. По данным авторов она сохраняет 91.3% SWE-bench Verified и 98.7% LiveCodeBench оригинала. Замахиваться на 125B захотелось, но в low-RAM режиме GPU держит лишь 13% экспертов - остальное крутит процессор из RAM, и заявленные 55 токенов в секунду мерились на карте с 12 ГиБ. Для моей восьмёрки оценок нет, и смутные ожидания скорости не радовали.
Решил по-честному: прежде чем качать 30 ГиБ, посмотреть, не даст ли тот же класс железа больше другой путь. Так на сцену вышел ExLlamaV3 - и оказался интереснее.
Что я хотел проверить
В прошлых материалах прошёл три стратегии локального запуска больших моделей на скромном железе: плотная модель целиком на видеокарте, MoE с выгрузкой экспертов на процессор и pruned MoE вроде Strata Coder, где половину экспертов просто вырезали. Стратегия номер два выглядела самой честной для моей коробки: RTX 4060 на 8 ГБ, Ryzen 5 8400F с AVX-512, 32 ГБ памяти.
ExLlamaV3 - движок от turboderp, формат квантизации EXL3 построен на QTIP: кодирование весов кодовыми словами с таблицами, от 1.4 до 6 бит на вес. Фирменная фишка - выгрузка экспертов MoE на CPU: -mcl держит routed-экспертов первых N слоёв в системной памяти, рабочий процесс крутит их на процессоре, а горячие эксперты при предзаполнении контекста стримятся на видеокарту. Для процессора с AVX-512 - как раз мой случай.
План был простой: скачать Qwen3.5-35B-A3B в 2.00bpw, 10.17 ГиБ весов (размер проверен через API HuggingFace, не с карточки), запустить с выгрузкой и замерить токены в секунду.
Что случилось на самом деле
Контрольный прогон без выгрузки упал ожидаемо: 10.17 ГиБ весов против 6.6 ГиБ свободной видеопамяти, движок ответил "Insufficient VRAM in split for model and cache". Это было предсказуемо, для того и прогон.
Дальше - нет. С флагом -mcl 20 (половина из 40 слоёв на CPU) падение то же самое. Вербозный лог показал причину, и она не про память:
!! model.language_model.layers.0.mlp: experts are not mul1, CPU offload skipped
!! model.language_model.layers.1.mlp: experts are not mul1, CPU offload skipped
... и так все 40 слоёв
Выгрузка требует экспертов в кодбуке mul1, а релиз turboderp собран с кодбуком mcg. Движок об этом сообщает одной строкой в verbose-логе и молча оставляет эксперты на видеокарте. Никаких предупреждений в обычном режиме, никакой ошибки - только падение по памяти на полпути загрузки, как будто выгрузки и не было.
Как это устроено внутри
Проверил по исходникам установленной версии 1.5.3. Проверка в block_sparse_mlp_cpu.py: если кодбук экспертов не mul1, слой просто не регистрируется на выгрузку. Требование жёсткое: mul1-кодбук, K не больше 8, активация silu или gelu - несоответствующие слои остаются на GPU без шума.
Кодбук - это таблица квантования, по которой веса кодируются кодовыми словами QTIP. mcg и mul1 - разные таблицы, и рабочий процесс CPU-выгрузки умеет разворачивать только mul1. Пересобрать релиз под другой кодбук средствами движка нельзя - это переквантование из исходных весов, то есть закачка BF16-модели на 70 ГиБ и часы конверсии.
Перебор альтернатив
Моделей в формате EXL3 на HuggingFace много: по поиску больше тысячи репозиториев, у одного turboderp их 81, от Qwen до DeepSeek-V4.1-Flash в десятке сборок. Проблема не в выборе, а в том, что совместимость с конкретным сценарием нигде не написана. Проверил через API HuggingFace релевантные мне ветки:
Qwen3.5-35B-A3B-exl3: все ветки (2.00, 2.54, 3.00, 4.00 bpw) - кодбук mcg
Qwen3-30B-A3B-exl3: старый формат v0.0.1 (mul1), но минимум 2.25bpw = 8.75 ГиБ
Qwen3.8-27B-exl3 SC: mul1, но модель плотная - выгружать нечего
Старый конвертер (версия формата 0.0.1) писал mul1 по умолчанию, новые релизы конвертируются с mcg. Отсюда расклад: старый формат совместим с выгрузкой, но старые файлы не бывают меньше 8.75 ГиБ; новые файлы компактнее, но несовместимы. Плотная 27B в mul1 выгрузку не требует - ей нужен GPU целиком, а 7.8 ГиБ весов в мои 6.6 свободных не влезают без вариантов.
Из тысячи репозиториев под сценарий "8 ГБ VRAM и выгрузка экспертов на CPU" кандидатов я в первый вечер не нашёл - но это не значит, что их нет. Кодбук пишется в quantization_config.json каждого релиза, значит весь массив можно прогнать скриптом: вытащить кодбук, размер и архитектуру, отфильтровать по трём фильтрам и получить шорт-лист. Я сделал это вручную для пяти моделей; массовый парсинг всего EXL3-каталога - задача на следующий вечер, и кандидаты уже есть: свежие кванты Flash-Next у turboderp идут с mul1, а у andrevp нашёлся прунутый вариант с abliteration бонусом.
Итог первого вечера: на 8 ГБ со случайным релизом EXL3 не запускается. С подобранным - запустится, и проверка одного поля перед закачкой отделяет первое от второго.
Постскриптум: рабочий случай существует
Пока разбирался, знакомый с сервером на двух RTX 5080 выбрал себе EXL3-релиз по той же логике - и попал. Его выбор: andrevp/Qwen3.8-Flash-Next-exl3-3.05bpw-heretic, 79 ГиБ весов Qwen3.8-Flash-Next (125B MoE, 6 миллиардов активных на токен) с abliteration от Heretic. Проверил его quantization_config.json на уровне тензоров:
кодбук: mul1, формат 1.4.4
тензоры эксперта: .suh, .svh, .mul1 (у моей mcg-модели: .mcg, 30971 штуки, ноль .mul1)
Probe движка при загрузке ищет тензор .mul1 у эксперта - у его релиза он есть на каждом эксперте каждого слоя. CPU-offload пройдёт проверку на всех 48 слоях. Это не везение, а правило: у turboderp ветки квантов Flash-Next кодбук mul1, mcg пошёл в другие модели - моя 35B-A3B, GLM-сборки.
Раскладка по его железу тоже сходится: из 79 ГиБ весов n-gram таблица на 33 ГиБ остаётся lookup-таблицей на SSD, в VRAM плюс RAM нужно уложить 50 ГиБ (47 экспертов и 3 прочего). Две 5080 дают 32 ГиБ, остальное берёт системная память, выгрузка холодных экспертов через -mcl с кодбуком mul1 работает как задумано. Отказы у heretic-сборки падают с 70 до 5 из сотни, качество по MMLU-Pro не деградирует - и всё это сайдкаром в 0.7 МБ поверх неизменённых весов.
Мораль та же, но теперь с двух сторон. Один и тот же формат на одном и том же движке: у одного релиза фича работает, у другого молча не работает - различие живёт в одном поле quantization_config.json.
Что это значит
CPU-выгрузка в ExLlamaV3 - рабочая технология для MoE на картах 12-24 ГиБ, где модель влезает почти целиком и выгружать надо пару слоёв, либо для релизов, собранных с mul1. Сформулировано это нигде не было: README говорит "CPU offloading - allows large MoE models to run with limited GPU resources", подробности требуют чтения исходников.
Практическое следствие, если у вас похожая ситуация. Перед закачкой проверьте кодбук релиза:
curl -sL https://huggingface.co/<repo>/resolve/<branch>/quantization_config.json | grep codebook
Поле "codebook": "mul1" - выгрузка будет работать. "mcg" - движок молча оставит экспертов на видеокарте, и вы узнаете об этом по падению загрузки. Ссылка на квант в карточке модели ничего не говорит о совместимости - проверяется только quantization_config.json.
Что осталось на потом
Strata Coder на моём железе всё ещё не запускался - и после ExLlamaV3 выглядит не тупиком, а самым осмысленным следующим шагом: инсталлятор сам проверяет железо и сам говорит, что влезет, скрытых несовместимостей уровня кодбуков там нет. Это отдельный вечер и отдельный замер: 117 миллиардов параметров на игровой карте против 35B в ExLlamaV3, который не запустился. Скорость обоих упирается в системную память, но у Strata хотя бы честно написано, какой ценой.
Русский язык у Qwen3.5-35B-A3B остался под вопросом - модель не загрузилась ни разу. Отдельное упражнение на будущее: взять mul1-релиз поменьше и пройтись по русскоязычным промптам, QTIP на низких битах интересен именно качеством на редких языках.
