Skip to content

gliclass-v4: Knowledgator's GLiClass v4 2B and 9B (Qwen3.5 decoder-KV) as the new family gliclass-kv (COB-564) - #150

Merged
cobanov merged 6 commits into
mainfrom
cob-564-gliclass-v4
Oct 25, 2026
Merged

cobanov merged 6 commits into
mainfrom
cob-564-gliclass-v4

Conversation

@cobanov

@cobanov cobanov commented Oct 24, 2026

Copy link
Copy Markdown
Member

Adds Knowledgator's GLiClass v4 (COB-564, item 2) as a new family gliclass-kv with the catalog model gliclass-v4: gliclass-v4:2b (knowledgator/gliclass-v4-2b on Qwen3.5-2B, latest) and gliclass-v4:9b (knowledgator/gliclass-v4-9b on Qwen3.5-9B). Text only. No release from this PR.

Why a new family

GLiClass v4 is the decoder-KV architecture (gliclass 0.1.21): a Qwen3.5 decoder reads prompt + state <<SEP>> label₁<<LABEL>> … <<SEP>> in one causal row; the label section's hidden states go through a two-layer bidirectional DeBERTa scorer (text at the final <<SEP>>, labels at their <<LABEL>>s, an MLP per pair), a gated recurrent cell re-runs the scorer for up to three steps and freezes a row once no label's sigmoid probability moves by 0.01, and a learned calibrator rescales every logit (beta · z + bias, beta from the representations and six logit statistics). Nothing of the uni-encoder gliclass (DeBERTa, contract M) carries over but Ollaya's question mapping, and the catalog's family is per model, so it is its own entry. The card says the checkpoint ships without the calibrator; the config says use_calibrator: true and the weights are in the checkpoint, so the port follows the code.

What is in the PR

  • convert/ollaya_convert/families/gliclass_kv/: ref.py (the gliclass package in fp32; the question mapping is the uni-encoder family's), layout.py (the port: context cut to the window minus the label section, the label section tokenized as one string), export.py (the Qwen3.5 trunk from llm_common/qwen35.py, upstream's scorer, the recurrence with its halting as torch.where, and the calibrator, all in one graph; every weight referenced in the repo's model.safetensors), goldens.py, parity.py.
  • Rust: ollaya_decision::gliclass_kv (rows, with tests; reuses gliclass::Call and Mode), ollaya_runner::gliclass_kv (the engine: six inputs, calibrated label logits out), examples/parity_gliclass_kv.rs, engine.rs registration. parity_run maps the family's hyphen to the example's underscore.
  • Catalog entry, docs/families/gliclass-kv.md, site/content/library/gliclass-v4.md, the overlay, a README row.

Contract: input_ids [rows, seq] (a multiple of 64), section_pos / section_mask [rows, len] (the label section), label_pos / label_mask [rows, k] (indices within the section), sep_pos [rows] → logits [rows, k] (calibrated) and steps [rows]. calibration.json is the identity. Window 8,192 tokens (the card's usage; the pipeline's default is 1,024). weights_in_memory is bf16.

Gate (K2 for the 2B, K2 with the CPU goldens for the 9B), choso-wsl

The eager graph matches upstream's fp32 model to 9.5e-6 (2B) / 2.0e-6 (9B) on the calibrated logits, with identical halting steps.

Export parity, 2B (families/gliclass_kv/parity.py, ONNX Runtime CPU vs PyTorch fp32 on the RTX 4090; 127 requests, 633 questions): rows, markers, sections and halting steps identical, 100 % argmax agreement vs fp32 and vs the public pipeline (161 questions, probabilities within 1.0e-7); logits ≤ 1.9e-4, probabilities ≤ 4.8e-5. Not run for the 9B: its fp32 reference (38 GB) and the ONNX session do not fit choso's RAM together; the goldens and the runtime gate below cover it.

Runtime (parity_gliclass_kv through parity_run; goldens of 97 requests, 483 questions, the 9B's reference on the CPU):

calls and rows decisions max Δ calibrated logit max Δ probability (p99) forward
2b, CUDA 483 / 483 identical 100 % 5.7e-4 1.5e-4 (9.7e-6) 88 ms/request
2b, CPU 483 / 483 identical 100 % 9.4e-5 2.5e-5 (5.4e-6) 3.2 s/request
9b, CUDA 483 / 483 identical 100 % 2.5e-5 5.0e-6 (3.8e-6) 531 ms/request
9b, CPU 483 / 483 identical 100 % 2.8e-5 4.2e-6 (3.0e-6) 16.0 s/request

The 2B's CUDA maximum is one noul row whose three recurrent steps compound the fp32 differences of the DeltaNet scan and the calibrator; the decisions are identical and the p99 is 1e-5. Records: results/runs/2026-10-24-parity-rtx4090-{cuda,cpu}.json (the same files PR #149 adds its strands rows to; whichever merges second needs the file from that branch). cargo test --workspace, clippy and the convert tests pass.

Registry

Manifests for gliclass-v4:2b, :9b and latest are in the PR. Blobs stay on the parity machine, choso-wsl:~/Developer/ollaya/registry/blobs:

tag layer blob bytes
2b config sha256-e6c756a8edb33105f48b4120b986ab323cb4fa0865f9b08cf799726813a6d33f 680
2b graph sha256-cce70796f1a86a4c58f92bfa203e9b635d5f358754c3f8321ca6448b30a153e3 12,452,900
2b decision.json sha256-422d5d5104c9b58861e5ee7d9d519dbb9508fd3225c6e6410494ad00a946ff3b 2,950
2b, 9b calibration.json (identity) sha256-adff5b1a27453e8af1c026fc3e78cf05f8a7554808fee8b4e4ea573841339389 177
2b, 9b license sha256-94fbb5a513de008bdd3a8c15fa7cc7c3545650fe94660a3f09f64a3215497ec5 10,424
9b config sha256-0d925cb28a846226a16b6fc3f20b5fc3d81f94b2f8d991d5a571c090a8207199 476
9b graph sha256-c438ccbc300d85ce504df702cea73f2bb32088d6eda7250665add834fbc7f31b 16,198,745
9b decision.json sha256-b4a305e27719318875f8c5998c494105e78fe54f2ea94503c607ca5071dd3e85 2,950
Weights point at Hugging Face (model.safetensors, 4.73 GB and 17.97 GB).

bench_plan (exit 0, nothing blocked) lists both tags as new with the full set, typed public latency-cuda latency-cpu, about 88 minutes each. Not run from this PR (the bench machine is in use).

Not in scope

The vision path (images in the context), few-shot examples, the streaming KV-cache pipeline (an exact-result speedup upstream).

🤖 Generated with Claude Code

https://claude.ai/code/session_0176yK678pQMu7HqGmfCnDAd

cobanov and others added 6 commits October 25, 2026 13:41
…b, -9b) as a new family gliclass-kv: reference, layout port, export with the recurrent scorer and calibrator in the graph, goldens, export parity, Rust rows, engine and parity example (COB-564)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0176yK678pQMu7HqGmfCnDAd
… (COB-564)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0176yK678pQMu7HqGmfCnDAd
…y doc and library page (COB-564)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0176yK678pQMu7HqGmfCnDAd
…(CUDA and CPU) and the 9b numbers in the docs (COB-564)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0176yK678pQMu7HqGmfCnDAd
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0176yK678pQMu7HqGmfCnDAd
…section's <<SEP>> ends (COB-564)

The row builder gave the context at least one token however long the labels were, so a request
with a 50k-token noul instruction or 255 long choices produced a row past the 8,192 window and the
graph's section axis. It is now TooManyOptions when the label section is >= max_len, as the
uni-encoder family does; the goldens' longest section is 1,917 tokens, so no golden changes. The
builder also checks that the section starts and ends with special_tokens.sep instead of assuming it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0176yK678pQMu7HqGmfCnDAd
@cobanov
cobanov force-pushed the cob-564-gliclass-v4 branch from f368e97 to ebe194d Compare October 25, 2026 10:44
@cobanov
cobanov merged commit 58981d6 into main Oct 25, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant