Gemma-4-E4B · Pruned RU (русский + код) · Q4_K_M (+ MTP draft)
Самый агрессивный из двух опубликованных вариантов прунинга Gemma-4-E4B: из словаря вырезана английская проза вместе с CJK. Латиница сохранена только через кодовый корпус — идентификаторы, ключевые слова, синтаксис и спецсимволы целые, английская художественная проза — нет. Не дистилляция, не файнтюн, не abliteration — веса модели не тронуты вообще. Изменён только токенизатор и две embedding-матрицы.
262 144 → 135 238 токенов (−126 906, −48.4%)
−1.69B параметров словаря
5.13 GB (UD-Q4_K_XL оригинала) → 3.75 GB (эта модель, Q4_K_M)
Перплексия на held-out (GPU): ru +0.0%, code −0.1%, en +53.8% (по замыслу)
🔹 Главная фишка: 3.75 ГБ модель, 128k контекста и MTP-draft — в 6 ГБ видеопамяти
Модель собрана под ноутбушную RTX 3060 6 ГБ. Полноценная 42-слойная Gemma-4-E4B с контекстом 131 072, из которой вырезан почти весь словарь, кроме кириллицы, кодовой латиницы и служебных токенов: 48.4% словаря, −1.69B параметров.
Итог: полные 128k контекста + сама модель + MTP-draft + 2 параллельных слота — влезают в 6 ГБ при KV-кэше
q8_0. Измерено на живом сервере (--ctx-size 128512 -np 2 -ctk q8_0 -ctv q8_0, MTP-draft): 5 397 MiB из 6 144 MiB, генерация 42–51 t/s на коротком контексте, 26–27 t/s на 60k, промпт ~1 400–1 800 t/s.И это единственный вариант, который открывает все 131 072 токенов контекста на 6 ГБ при полном f16 KV-кэше — без квантования кэша вообще (без draft).
Плата — деградация английской прозы: она остаётся достижимой через byte-fallback, но дорожает (PPL +53.8%). Русский, код и кириллица — целые, сегментация совпадает с оригиналом токен-в-токен.
Если нужен живой английский — берите парный вариант Igor282/gemma-4-e4b-pruned-ru-en-Q4_K_M (словарь 190 421, −27.4%, английский lossless).
Откуда эта модель (цепочка происхождения)
Прунить квантованный GGUF нельзя: блоки Q4_K разбиты на суперблоки 32×32, и разрезать такую матрицу «по строкам словаря» посередине блока невозможно. Поэтому прунинг делался на исходной HF-модели, а GGUF собирается и квантуется заново.
| Этап | Артефакт | Источник |
|---|---|---|
| 1. Оригинал (источник весов) | gemma-4-E4B-it, BF16, ~16 ГБ |
google/gemma-4-E4B-it |
| 2. Референсный квант (точка сравнения по размеру) | gemma-4-E4B-it-UD-Q4_K_XL.gguf, 5 126 306 944 B |
unsloth/gemma-4-E4B-it-GGUF |
| 3. Прунинг словаря + embedding-весов | gemma-4-E4B-it с словарём 135 238, 12.03 ГБ |
lossless-прунинг BPE-словаря (метод — ниже) |
| 4. Сборка GGUF + квант | gemma-4-e4b-pruned-ru-f16.gguf → gemma-4-e4b-pruned-ru-Q4_K_M.gguf |
llama.cpp convert_hf_to_gguf.py + llama-quantize |
| 5. Draft для спекуляции | gemma-4-E4B-it-assistant, 4 слоя, hidden 256 |
google/gemma-4-E4B-it-assistant |
| 6. Прунинг draft той же маской | gemma-4-e4b-draft-pruned-ru-f16.gguf |
тот же keep_sorted-mask, что и у target |
Файл-референс gemma-4-E4B-it-UD-Q4_K_XL.gguf совпадает с файлом из
unsloth/gemma-4-E4B-it-GGUF побайтово (5 126 306 944 B, LFS SHA-256 3cf61de1…d7df80) —
то есть это именно unsloth-квант, а не самодельный. Он использовался как точка сравнения по
размеру, а не как источник весов: прунинг шёл от BF16-оригинала.
Почему прунинг словаря, а не слоёв
Словарь Gemma-4-E4B (262 144) участвует в двух матрицах, и обе огромные:
| Матрица | Форма | Параметров |
|---|---|---|
embed_tokens |
262 144 × 2560 | ≈ 0.67B |
per_layer_embeddings (MatFormer / Gemma-3n механизм) |
262 144 × 10 752 | ≈ 2.82B |
Итого ~3.5B из 8B модели (44%) — это словарь. Это самый дешёвый способ похудеть: он не трогает attention и MLP, то есть не трогает логику, код и рассуждение, которые живут в средних слоях. Прунинг attention-слоёв за тот же процент размера всегда стоит качества; прунинг неиспользуемых строк словаря — не стоит ничего, при условии что он сделан корректно.
Gemma-4 использует BPE-токенизатор (514 906 мержей, byte-fallback, маркер ▁) — как Qwen,
поэтому применима классическая логика lossless-прунинга. Идея взята из
Qwen-Tokenizer-Pruner, реализация
собственная, потому что оригинальный скрипт Qwen-специфичен.
Как гарантируется «lossless» для русского и кода
Четыре условия, все проверяются по факту, а не «на глаз»:
Сохраняются все special/служебные токены:
<pad>,<eos>,<bos>,<unk>,<|turn|>,<unused*>, byte-токены<0xNN>— их ровно 6 581, и они не удаляются никогда. Byte-fallback целая, поэтому любой редкий символ (и английское слово, и иероглиф, и эмодзи) остаётся достижимым побайтово, просто дороже по токенам.Сохраняются все токены опорного корпуса: ru + код, без en. Корпус — 35 908 документов, 324.0M токенов (wikipedia для ru, seed-корпус плюс реальный рабочий код для code). Английская проза в опору не входит — это и есть отличие от ru+en варианта.
Замыкание по merges: если оставлен родитель
a+b, то детиaиbобязаны остаться. Иначе BPE-алгоритм теряет часть правил и сегментация «плывёт». Дано:+12 235токенов.Fixed-point проверка: опорный корпус сегментируется старым и новым токенизатором и сверяется токен-в-токен. Результат этого запуска:
Fixed-point iter 0: сегментация совпала для 35908/35908 документов; расхождений-кусков: 0Сходится с первой итерации, расхождений ноль. Это формальная гарантия: для русского и кода новый токенизатор выдаёт ровно те же id, что и оригинал. Никакого «переучивания» модели не требуется, поэтому веса можно не трогать. Для английского такой гарантии нет — он вне опоры, и его деградация заявлена как цель варианта.
Баланс маски:
Старый словарь: 262 144 токенов, 514 906 мержей
После фильтра письменностей: 27 420 (special/bytes 6 581) ← латиница в фильтр не входит
Токены опорного корпуса: 112 203 уникальных, из них новых 95 583
Замыкание по merges: +12 235
Новый словарь: 135 238 (удалено 126 906 = 48.4%)
Мержей сохранено: 302 731 / 514 906
Сэкономлено параметров: 1.69B
HF-модель: 15.25 ГБ → 12.03 ГБ
Числа
Размер и квант
| оригинал | эта модель | |
|---|---|---|
| GGUF-квант | UD-Q4_K_XL | Q4_K_M |
| Файл | 5 126 306 944 B (4.77 GiB) | 3 941 951 552 B (3.67 GiB) |
| BPW | — | 5.40 |
| f16-промежуточный | — | 11 118.44 MiB (16.00 BPW) |
| Время квантования | — | 210 с, 8 тредов |
| Params (по подсчёту llama.cpp) | ~8B | 5 828 696 618 |
| Словарь | 262 144 | 135 238 |
Архитектура не менялась: 42 слоя, hidden 2560, GQA 8/2, sliding window 512, shared KV 18 слоёв,
final_logit_softcapping = 30, контекст 131 072, tied embeddings, general.size_label = 6.6B.
Chat-шаблон — канонический Gemma-4 (с <|turn|>-разметкой, thinking-каналом и полным
tool-calling: <|tool_call> / <|tool_response>).
Перплексия, held-out (не пересекается с опорным корпусом)
GPU, llama-perplexity -ngl 99, сравнение квант-к-кванту (эта модель Q4_K_M против
оригинала Q4_K_M), по 30 документов на язык:
| Корпус | оригинал | эта модель | Δ | токенов |
|---|---|---|---|---|
| ru | 28.06 | 28.05 | +0.0% | 33 255 |
| code | 6.15 | 6.14 | −0.1% | 29 896 |
| en | 64.03 | 98.51 | +53.8% | 5 732 |
Русский и код — lossless с точностью до десятых процента, ровно как обещает fixed-point гарантия. Английский вырос на 54% — это не поломка, а цена варианта: английская проза вырезана из словаря осознанно, модель читает и пишет её через byte-fallback.
Сверка на BF16 (CPU, частичный прогон до перехода на GPU): ru 256.67 → 256.32 (−0.1%), en +22.8% — та же картина, рост английского только на полном held-out корпусе.
Порог стоп-сигнала для «ru и code» — рост PPL больше 10–15%. Здесь роста нет вообще.
Скорость на RTX 3060 Laptop 6 ГБ (замер по API на живом сервере)
Замер — не llama-cli, а llama-server с рабочей спекуляцией: запросы к /completion,
ignore_eos включён, вывод ровно 128 токенов, по два прогона на каждую строку.
Конфигурация сервера, снятая с /v1/models и /props:
model: gemma-4-e4b-pruned-ru-Q4_K_M.gguf ftype Q4_K - Medium
n_vocab 135 238 · n_params 5 828 696 618 · n_ctx_train 131 072
n_ctx 64 256 (на слот)
total_slots 2 → суммарный контекст 128 512
flash-attn on · -ctk q8_0 -ctv q8_0 · MTP draft активен
GPU: 5 397 MiB из 6 144 MiB (включая рабочий стол Windows)
| Контекст в слоте | Prompt eval | Generation |
|---|---|---|
| 53 (короткий чат) | 103–238 t/s | 42–51 t/s |
| 15 603 | 1 828–1 835 t/s | 46–54 t/s |
| 31 403 | 1 631–1 641 t/s | 38–39 t/s |
| 59 478 | 1 379–1 380 t/s | 26–27 t/s |
| 62 503 (предел слота) | 1 350 t/s | — |
Отдельный прогон чистого prompt eval (вывод 1 токен): 853 → 1 479 t/s, 3 328 → 1 803, 15 578 → 1 811, 31 378 → 1 609, 47 178 → 1 465, 59 453 → 1 372, 62 503 → 1 350 t/s.
Что из этого следует:
- 128k контекста на 6 ГБ — работает, с запасом ~750 MiB.
- Генерация на коротком контексте — 42–51 t/s. На 60k проседает до 26–27 t/s — это цена attention по длинному KV, а не предел модели.
- На длинном контексте эта модель быстрее парной ru+en (у той на 60k было 14–23 t/s): softmax логаунта идёт по 135 238 токенам вместо 190 421, и просадка от длинного KV мягче.
- Разброс в колонке Generation реален: он равен разбросу доли принятых черновиков — спекуляция принимала от 12% до 39% draft-токенов в зависимости от текста.
- На кодовом промпте (короткая Python-задача) генерация доходит до ~97 t/s — на предсказуемом тексте спекуляция принимает черновики почти подчистую.
- Prompt eval держится ~1 350–1 835 t/s во всём диапазоне до предела слота.
Скорость на CPU (справочно, из smoke-тестов пайплайна)
Это не GPU. Собрано на той же машине, но на CPU-бэкенде llama.cpp, llama-cli -st,
вывод 100 токенов:
| Конфигурация | Prompt | Generation |
|---|---|---|
| baseline (без draft) | 45.9 t/s | 10.7 t/s |
| + MTP draft | 39.4 t/s | 20.6 t/s |
| ускорение от draft | — | ≈ 1.9× |
Отношение draft/без-draft (≈1.9×) на CPU и на GPU совпало — это и есть главная цифра пайплайна, и она не зависит от бэкенда.
Draft-модель — официальный google/gemma-4-E4B-it-assistant (4 слоя, hidden 256,
nextn_predict_layers = 4), прунутый той же keep_sorted-маской, что и target:
embed_tokens режется по строкам, token_ordering фильтруется и ремаппится, токенизатор
копируется от прунутой target. Итог — словари совпадают id-в-id (llama.cpp требует
совпадения vocab с допуском 128 токенов).
gemma-4-e4b-draft-pruned-ru-f16.gguf
architecture: gemma4-assistant · 4 блока · hidden 256 · size_label 60M
vocab 135 238 = vocab target · 99 282 112 B (94.7 MiB)
Сколько влезает в 6 ГБ (бюджет памяти)
Разобрано по метаданным GGUF (gemma4.attention.*), контрольная точка сверена с реальным
запуском. 6 ГБ = 6 144 MiB.
Почему контекст здесь дешёвый
Архитектура Gemma-4-E4B сама по себе экономит KV-кэш, и прунинг словаря это не трогает:
| Механизм | Что даёт |
|---|---|
shared_kv_layers = 18 |
Последние 18 слоёв переиспользуют KV первых 18 → свой кэш только у 24 слоёв из 42 |
sliding_window = 512 |
20 из этих 24 слоёв — скользящие, их кэш обрезан 512 токенами и не растёт с контекстом |
key_length_swa = 256 |
В скользящих слоях KV вдвое уже, чем в полных (512) |
Из-за этого на полномерный контекст работает только 4 слоя из 42. Остальные либо делят чужой кэш, либо заперты в окне 512.
Целевая модель (ru, Q4_K_M)
Фиксированная часть: веса 3 751 MiB + draft 95 MiB + вычислительный буфер llama.cpp ~250 MiB (оценка) = ~4 096 MiB. На KV-кэш остаётся ~2 048 MiB (без draft — ~2 143 MiB).
При полном контексте 131 072 и np 2:
| KV-квант | target KV | скользящие | draft KV | всего KV | в 6 ГБ? |
|---|---|---|---|---|---|
| f16 | 2 048 MiB | 40 MiB | 2 560 MiB | 4 648 MiB | ❌ нужно 8 744 MiB (аналитически) |
| q8_0 | 1 088 MiB | 21 MiB | 1 360 MiB | 2 469 MiB | ❌ нужно 6 565 MiB (аналитически) |
| q4_0 | 576 MiB | 11 MiB | 720 MiB | 1 307 MiB | ✅ 5 403 MiB, запас 741 MiB |
Проверка расчёта живым запуском
Аналитический расчёт завышает потребление примерно на 1 ГБ, и живой запуск это показал ещё на
парном ru+en варианте. Конфигурация, которая крутится прямо сейчас:
--ctx-size 128512 -np 2 -ctk q8_0 -ctv q8_0 с MTP-draft.
| расчёт | факт | |
|---|---|---|
| Веса + draft + буфер + KV (q8_0, 128 512, np 2) | ~6 565 MiB | ~5 397 MiB занято на GPU |
| Вердикт расчёта | ❌ «не влезает» | ✅ влезает, запас ~750 MiB |
Главная причина расхождения: draft-модель в llama.cpp не резервирует KV-кэш на весь контекст —
у неё кольцевой буфер черновика, и допущение «draft KV = полная длина контекста» неверно. Строки
draft KV в таблице выше поэтому завышены, и именно они портят вердикт.
Практический вывод: q8_0 KV при 128k — рабочий вариант на 6 ГБ, а не только q4_0. Расчётную таблицу оставляю как верхнюю оценку (она не занижает), но решать надо по счётчику памяти, который печатает сам llama.cpp при загрузке.
Максимальный контекст в 6 ГБ
| Конфигурация | KV f16 | KV q8_0 | KV q4_0 |
|---|---|---|---|
| эта модель (ru), с MTP-draft | 78 844 ⚠ | 131 072 ✅ | 131 072 ✅ |
| эта модель (ru), без draft | 131 072 | 131 072 | 131 072 |
| ru+en Q4_K_M, с MTP-draft | 55 014 | 105 154 ⚠✅ | 131 072 |
| ru+en Q4_K_M, без draft | 97 784 | 131 072 | 131 072 |
✅ — строки, подтверждённые живым запуском (128 512 + draft + q8_0 = 5 397 MiB).
⚠ — расчёт завышает потребление на ~1 ГБ из-за draft KV, фактический потолок выше.
Читается просто: это единственный вариант линейки, где 131 072 контекста открываются при полном f16 KV-кэше (без draft) — максимальное качество кэша на 6 ГБ. С draft и квантованным кэшем — те же 128k+ и живой MTP.
Оговорки оценки
- Вычислительный буфер llama.cpp взят как ~250 MiB и не измерялся напрямую: на практике он
зависит от версии сборки,
flash-attn,-b/-ubи числа слотов. - Главная погрешность расчёта — KV черновика. Аналитика резервирует под draft полный
контекст, llama.cpp — кольцевой буфер. Из-за этого таблица завышает потребление примерно
на 1 ГБ и даёт ложный вердикт «не влезает» для
q8_0. Живой запуск 128k + draft показан в разделе выше: влезает с запасом ~750 MiB. np 2удваивает скользящий кэш и draft-кэш (окно 512 считается на слот), а--ctx-sizeпри этом делится между слотами.- Числа предназначены для оценки бюджета, а не как гарантия: точный счётчик памяти печатает
сам llama.cpp при загрузке (
llama_model_load: KV size).
Запуск в llama.cpp
Только target
.\llama-server.exe -m ".\models\gemma-4-e4b-pruned-ru-Q4_K_M.gguf" ^
--ctx-size 131072 --flash-attn on -np 2 -b 512 -ub 512 ^
-ctk q8_0 -ctv q8_0 --host 0.0.0.0 --port 8080
Target + спекулятивное декодирование (то, что даёт ~50 t/s на коротком контексте)
.\llama-server.exe -m ".\models\gemma-4-e4b-pruned-ru-Q4_K_M.gguf" ^
-md ".\models\gemma-4-e4b-draft-pruned-ru-f16.gguf" --spec-type draft-mtp --spec-draft-n-max 3 ^
--ctx-size 128512 --flash-attn on -np 2 -b 512 -ub 512 ^
-ctk q8_0 -ctv q8_0 --host 0.0.0.0 --port 8080
Пути .\models\... — условный пример.
⚠ Про «128k контекста»:
--ctx-sizeв сервере — это сумма на все слоты
-np 2 --ctx-size 128512даёт по 64 256 токенов на слот, а не по 128k. Проверить легко:GET /v1/models→n_ctx: 64256,GET /props→total_slots: 2.То есть «модель работает со 128k контекста на 6 ГБ» означает 128k суммарно при двух параллельных диалогах. Один диалог видит 64k. Чтобы один диалог видел все 128k, нужен
-np 1 --ctx-size 128512— и тогда в 6 ГБ он влезет с тем же запасом, потому что суммарный KV-кэш не изменится.Архитектурный потолок —
n_ctx_train = 131 072; выше него модель не имеет смысла гнать.
⚠
--spec-type draft-mtp, а неdraft-simple. Это не пожелание, а требование: assistant-модель Gemma-4 берёт скрытые состояния target черезembd_state, и сdraft-simplellama.cpp падает наGGML_ASSERT(ubatch->embd_state).⚠
-mdуказывает на draft из этого же репозитория. Чужой, не прунутый assistant не подойдёт: словари не совпадут.
Рекомендованные параметры сэмплирования (записаны в GGUF-метаданные): temp 1.0, top_k 64,
top_p 0.95.
Память
Замерено на этой карте: конфигурация выше (Q4_K_M + draft f16 + KV q8_0, 128 512, np 2)
занимает ~5 397 MiB из 6 144 — с запасом ~750 MiB. Тот же набор при KV q4_0 занимает
заметно меньше. Полный разбор и предельные размеры контекста под каждый KV-квант — в разделе
Сколько влезает в 6 ГБ.
Оригинал в UD-Q4_K_XL (5.13 ГБ) на этой же карте в 128k не влезает в принципе, а в 36k — на грани. Это и была мотивация всей сборки.
Запуск в LM Studio
My Models → + Add Model → Download from Hugging Face →
Igor282/gemma-4-e4b-pruned-ru-Q4_K_M.LM Studio подхватит GGUF и встроенный chat-шаблон из метаданных — отдельный JINJA не нужен.
Speculative decoding: Advanced Configuration → включить спекуляцию и указать draft-файл
gemma-4-e4b-draft-pruned-ru-f16.gguf, тип — MTP (не simple), max draft tokens — 3. Ключи вlmstudio.jsonвыглядят примерно так (имена ключей зависят от версии LM Studio):"speculativeDecoding.enabled": true, "speculativeDecoding.draftModelKey": "Igor282/gemma-4-e4b-pruned-ru-Q4_K_M/gemma-4-e4b-draft-pruned-ru-f16.gguf", "speculativeDecoding.specType": "draft-mtp", "speculativeDecoding.maxDraftTokens": 3Контекст 36k,
flashAttention: true, KV-квантq4_0.
Chat-шаблон ожидает <|turn>role … <turn|>; thinking-канал <|channel>thought … <channel|>
работает штатно, reasoning_content из ответа вручную не вырезается.
Что умеет и что сломалось
Работает как раньше:
- русский проза/диалог — сегментация идентична оригиналу токен-в-токен;
- код (идентификаторы, ключевые слова, синтаксис, спецсимволы) — цел, PPL не изменился;
- tool-calling, thinking-канал, спекулятивное декодирование;
- контекст 131k, vision/audio энкодеры не тронуты (input-модальности целые).
Деградирует — и это и есть цель варианта:
- английская проза. Модель понимает английский и может его писать, но tokens английских слов вырезаны — генерация идёт через byte-fallback, PPL +53.8% на GPU-замере. Живая проверка на сервере: на промпт «напиши рассказ про море по-английски» модель выдала текст-заглушку вместо рассказа — генерация английской прозы действительно деградировала. Для английского диалога нужен парный ru+en вариант.
- генерация на вырезанных письменностях (китайский, японский, корейский, арабский, хинди, тайский и т. п.). Понимать их модель не перестала — она их не может произнести своими токенами и будет раскладывать через byte-fallback, то есть дорого и грязно.
- Редкие символы, математическая и типографская юникод-матрица вне Latin/Cyrillic/Common — аналогично, через байты.
- Output-аудио (any-to-any speech generation) не тестировалось; прунинг словаря на speech-декодер влияет, поэтому для речевой генерации модель не проверена.
Честные ограничения:
- Английский не вырезан полностью: латиница живёт через кодовый корпус, поэтому модель пишет по-английски — с повышенным PPL. Это не «английский сломан», это «английский подорожал».
- Модель не дообучалась (CPT). Прунинг lossless по ru и коду, поэтому дообучение здесь — опциональное улучшение, а не починка.
- Это не «русская модель». Это Gemma-4, у которой убрали 48% словаря, чтобы она влезала в потребительскую видеопамять и не тратила байты на письменности и слова, которые в русско-кодовом рабочем тексте не встречаются.
Файлы
| Файл | Размер | Что это |
|---|---|---|
gemma-4-e4b-pruned-ru-Q4_K_M.gguf |
3 941 951 552 B | target, Q4_K_M, vocab 135 238 |
gemma-4-e4b-draft-pruned-ru-f16.gguf |
99 282 112 B | MTP-draft, f16, vocab 135 238 |
Парный вариант — с живым английским
Igor282/gemma-4-e4b-pruned-ru-en-Q4_K_M: словарь 190 421 (−27.4%), файл 4.24 GiB, английский lossless, те же 128k контекста на 6 ГБ. Там же — полный разбор метода прунинга на примере ru+en маски.
Лицензия и атрибуция
Apache-2.0 — та же лицензия, что у исходной модели, без дополнительных ограничений. Модель отдаётся свободно, использовать можно в том числе в коммерческих проектах.
- Веса: производные от google/gemma-4-E4B-it © Google, Apache-2.0.
- Draft: производный от google/gemma-4-E4B-it-assistant © Google, Apache-2.0.
- Условия использования Gemma (Acceptable Use Policy) остаются в силе: Gemma 4 license.
- Референсный квант для сравнения: unsloth/gemma-4-E4B-it-GGUF.
- Идея lossless-прунинга BPE-словаря: Qwen-Tokenizer-Pruner.
Маска словаря (keep_sorted) воспроизводима детерминированно из опорного корпуса и фильтра
письменностей — то есть любой может собрать свою вариацию процента вырезания и проверить её
fixed-point-инвариантом.