CoolFace
Modelpublic

md-bogush/Qwen3.5-4B-onnx-int4-cuda

sourceHugging Faceapache-2.0updated 2mo agoView on Hugging Face
0likes11downloads
Model Card

Qwen3.5-4B — ONNX int4 (CUDA, onnxruntime-genai)

int4-сборка Qwen/Qwen3.5-4B для onnxruntime-genai на CUDA EP. Собрана для голосового ввода в Dispatch, где модель работает корректором диктовки: расставляет пунктуацию и регистр и возвращает англоязычные технические термины, которые распознаватель речи свёл к похожим по звучанию русским словам.

У onnx-community genai-сборок Qwen3.5 нет: Qwen3.5-4B/2B/0.8B-ONNX опубликованы в формате transformers.js без genai_config.json, а единственная с genai (9B) весит 15.9 ГБ неквантованной. Эта сборка закрывает пробел.

Отличие от сборки по умолчанию: prune_lm_head

Собрано с prune_lm_head=true — перед LM head вставляются Gather+Unsqueeze, так что на префилле считается только логит последней позиции: вход MatMul [B,1,H] вместо [B,S,H]. Для корректора это принципиально: задача копирующая, выходных токенов десятки, а логиты на всю длину промпта — главный потребитель памяти (при vocab 248 320 это ~607 КБ на позицию).

Замер на RTX 5080, CUDA EP, 23 фразы, дайджест сессии 2000 символов (промпт 1185 токенов):

без `prune_lm_head`**с `prune_lm_head`**
VRAM, пик9528 МиБ4674 МиБ
VRAM, прирост от загрузки к пику+4941 МиБ+1148 МиБ
p50 на фразу0.168 с0.152 с
качество (свой набор)19/2319/23
размер на диске3.5 ГБ3.5 ГБ

Качество и вес не меняются, пик VRAM падает вдвое. Побочно снимается класс отказов, где корректор молча умирал на max_length cannot be greater than model context_length: контекст здесь 262 144 токена.

Как собрано

python -m onnxruntime_genai.models.builder \
  -m Qwen/Qwen3.5-4B -o <out> -p int4 -e cuda -c <cache> \
  --extra_options exclude_embeds=false hf_token=false prune_lm_head=true

Собрано билдером onnxruntime-genai 0.14.1. Важные детали:

  • —`exclude_embeds=false` обязателен. Qwen3.5 — VL-модель, и по умолчанию билдер делает декодер на inputs_embeds, несовместимый с append_tokens. Без флага получается бесполезная сборка, и это выясняется только при запуске.
  • —`hf_token=false` — иначе билдер падает с LocalTokenNotFoundError даже на публичном репозитории.
  • —`onnx_ir` и `onnxscript` ставить отдельно — в зависимости onnxruntime-genai они не входят, билдер падает на импорте.
  • —Билдером 0.15.0-dev эту модель собирать нельзя. Он принудительно добавляет Gather в список квантуемых типов и загоняет в int4 то, что обязано остаться точным: таблицу эмбеддингов и кэши RoPE (model.rotary_emb.cos_cache / sin_cache). Тригонометрические значения в диапазоне [-1, 1] на 16 уровнях int4 разрушают позиционное кодирование — модель генерирует мусор (декодируется в '�'), и ошибка ничем не сигнализируется. Проверять сборку стоит по типам: у корректной model.embed_tokens.weight — FLOAT16, rotary_emb.*_cache — FLOAT, узлов GatherBlockQuantized нет.

Что проверено и отброшено: matmul_mixed_precision=last_matmul:int8,linear_attn:int8 — на 0.5 ГБ больше, медленнее, качество то же; shared_embeddings=true вместе с int4 разделяет lm_head и таблицу эмбеддингов, то есть кладёт эмбеддинги в int4 с тем же результатом, что выше.

Архитектура

Гибридная: full_attention чередуется с linear_attention (GatedDeltaNet, рекуррентное состояние вместо KV-кэша). 32 слоя, hidden 2560, 16 голов внимания, 4 KV-головы, head_size 256, vocab 248 320, контекст 262 144.

Лицензия

Производная от Qwen/Qwen3.5-4B, лицензия Apache-2.0 сохраняется. Веса не переобучались — только экспорт в ONNX и int4-квантование весов.