Instructions to use mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("feature-extraction", model="mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless") messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# Load model directly from transformers import AutoTokenizer, AutoModel tokenizer = AutoTokenizer.from_pretrained("mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless") model = AutoModel.from_pretrained("mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless", device_map="auto") messages = [ {"role": "user", "content": "Who are you?"}, ] inputs = tokenizer.apply_chat_template( messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt", ).to(model.device) outputs = model.generate(**inputs, max_new_tokens=40) print(tokenizer.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- llama.cpp
How to use mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless with llama.cpp:
Install (macOS, Linux)
curl -LsSf https://llama.app/install.sh | sh # Start a local OpenAI-compatible server with a web UI: llama serve -hf mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless:Q8_0 # Run inference directly in the terminal: llama cli -hf mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless:Q8_0
Install from WinGet (Windows)
winget install llama.cpp # Start a local OpenAI-compatible server with a web UI: llama serve -hf mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless:Q8_0 # Run inference directly in the terminal: llama cli -hf mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless:Q8_0
Use pre-built binary
# Download pre-built binary from: # https://github.com/ggerganov/llama.cpp/releases # Start a local OpenAI-compatible server with a web UI: ./llama-server -hf mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless:Q8_0 # Run inference directly in the terminal: ./llama-cli -hf mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless:Q8_0
Build from source code
git clone https://github.com/ggerganov/llama.cpp.git cd llama.cpp cmake -B build cmake --build build -j --target llama-server llama-cli # Start a local OpenAI-compatible server with a web UI: ./build/bin/llama-server -hf mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless:Q8_0 # Run inference directly in the terminal: ./build/bin/llama-cli -hf mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless:Q8_0
Use Docker
docker model run hf.co/mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless:Q8_0
- LM Studio
- Jan
- Ollama
How to use mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless with Ollama:
ollama run hf.co/mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless:Q8_0
- Unsloth Desktop
- Pi
How to use mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless with Pi:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless:Q8_0
Configure the model in Pi
# Install Pi: npm install -g @earendil-works/pi-coding-agent # Add to ~/.pi/agent/models.json: { "providers": { "llama-cpp": { "baseUrl": "http://localhost:8080/v1", "api": "openai-completions", "apiKey": "none", "models": [ { "id": "mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless:Q8_0" } ] } } }Run Pi
# Start Pi in your project directory: pi
- Docker Model Runner
How to use mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless with Docker Model Runner:
docker model run hf.co/mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless:Q8_0
- Lemonade
How to use mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless with Lemonade:
Pull the model
# Download Lemonade from https://lemonade-server.ai/ lemonade pull mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless:Q8_0
Run and chat with the model
lemonade run user.qwen3.5-4l-vocab40k-en-ko-headless-Q8_0
List all available models
lemonade list
- Hermes Agent
How to use mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless with Hermes Agent:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless:Q8_0
Configure Hermes
# Install Hermes: curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash hermes setup # Point Hermes at the local server: hermes config set model.provider custom hermes config set model.base_url http://127.0.0.1:8080/v1 hermes config set model.default mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless:Q8_0
Run Hermes
hermes
- Atomic Chat
- OpenClaw
How to use mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless with OpenClaw:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless:Q8_0
Configure OpenClaw
# Install OpenClaw: npm install -g openclaw@latest # Register the local server and set it as the default model: openclaw onboard --non-interactive --mode local \ --auth-choice custom-api-key \ --custom-base-url http://127.0.0.1:8080/v1 \ --custom-model-id "mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless:Q8_0" \ --custom-provider-id llama-cpp \ --custom-compatibility openai \ --custom-text-input \ --accept-risk \ --skip-health
Run OpenClaw
openclaw agent --local --agent main --message "Hello from Hugging Face"
- Qwen3.5 4L Headless Backbone (40k EN/KO)
Qwen3.5 4L Headless Backbone (40k EN/KO)
A headless four-layer text backbone distilled from Qwen3.5-0.8B, with a vocabulary cut to English and Korean. It has no language-model head, no classification head, no labels and no thresholds. You attach a head and train it.
Korean translation of this page: README_KO.md.
This card is the record of how it was made. It walks the whole path — prompting the original model, turning it into a classifier, removing layers, changing precision, cutting the vocabulary, and cutting it again by rule — and shows what each step measured. We are not arguing that this model is better than anything. We built it, we measured it beside other models, and where we have a guess about why a number moved we say it is a guess.
| Root model | 4 layers, hidden 1024, 39,866 vocabulary entries |
| Parameters | 120,639,808 |
| BF16 weights | 241,285,160 bytes (230.1 MiB) |
| Q8_0 GGUF | 124 MiB |
| Head | none — score.weight is absent |
| Upstream | Qwen/Qwen3.5-0.8B, 24 layers, 248,320 rows |
| Distillation data | 4,096 unlabeled WikiText-103 rows, no task labels |
| Language scope | English and Korean. Han characters fall back to byte tokens. |
The root changed on 2026-09-20. It used to be a 128,000-entry, 210.9M-parameter, 402.29 MiB model. Everything in this card that is labelled "128k" describes that previous root, which remains available at the git revision before that commit. What you download today is the 39,866-entry model in the table above. Sections 5 and 6 explain why it changed.
The repository was also renamed on 2026-09-20, from
qwen35-standalone4l-classification-basetoqwen3.5-4l-vocab40k-en-ko-headless. The old URL redirects, butfrom_pretrainedon the old id is reported not to follow that redirect, so pinned code should be updated. The old id now holds a weightless stub pointing here.vocab40krounds the 39,866 entries; the three unpublished grid siblings would bevocab15k,vocab24kandvocab72k.
How it compares
Every arm below was trained, calibrated and profiled under one frozen protocol on one split, so the columns are like-for-like. There are 65 of them: 46 encoder arms across ten families, 15 earlier Qwen arms, and the four task-blind vocabulary grid arms, of which this root is one.
| metric | this root | rank | best in the field | field median |
|---|---|---|---|---|
| macro F1 (3 seeds) | 0.5721 ± 0.0087 | 3 / 65 | 0.5781 | 0.5121 |
| document p50 | 30.4 ms | 52 / 65 | 5.7 ms | 11.2 ms |
| peak process-tree RSS | 1,937 MiB | 40 / 65 | 1,601 MiB | 1,880 MiB |
| BF16 weight file | 230 MiB | 31 / 65 | 22 MiB | 238 MiB |
| GPU joules per document | 5.01 | 52 / 65 | 0.81 | 2.14 |
Read together: it is at the top on quality and in the bottom third on time and energy. Nothing in the measurement resolves the top of the quality column — six arms sit within one median seed SD (0.0117) of the best, and this root is one of them, so its third place is a tie, not a win. The cost columns are not a tie: the fastest arm in the field answers a document in 5.7 ms against this root's 30.4 ms, and spends 0.81 J against 5.01 J.
Inside that six-arm tie the comparison is narrower and more favourable. roberta-base reaches
0.5524 at 11.1 ms and 238 MiB; the best encoder is cheaper on every cost axis and 0.020 lower on
quality, against SDs of 0.0143 and 0.0087. Among the tied arms, qwen35-taskfree-base8l-v248k
matches the quality at 73.8 ms, 802 MiB and 11.31 J — this root reaches the same band at 30.4 ms,
230 MiB and 5.01 J.
Our reading: this is a 262,144-position decoder stack being used to read 256-token windows, and most of the arms it loses to on time are encoders built for exactly that shape. We think the architecture is what costs the time, not the vocabulary cut or the depth — but we have not isolated that, so it stays a guess. What the cut and the depth did move is the file and the energy, and those are visible in the panels above.
Everything in this table is the opened calibration split. One fixed-protocol test evaluation was run on 2026-09-23 against a re-split this project had used before — not a never-seen holdout — and it is reported in full, decreases included, under The final test.
Run it in a container
Two stacks, both CPU-only, both with the dedicated no-KV runtime compiled inside the image and nothing fetched at run time.
Getting the files. The weights and the two runtime archives are stored with Git LFS. A
git clone made without git-lfs — the macOS default — leaves ~130-byte pointer files in their
place, and both containers stop and say so. Either fetch with the Hub CLI, which needs no git-lfs:
hf download mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless --local-dir qwen3.5-4l
or install git-lfs first (macOS brew install git-lfs, Debian/Ubuntu apt install git-lfs; Git for
Windows already includes it), run git lfs install, and clone. An existing clone is repaired in
place with git lfs pull.
docker/ — see it work now.
tar -xzf bundle/runtime_source.tar.gz -C bundle/
docker compose up --build # then http://127.0.0.1:8787
Be clear about what is in it: not this root, but this root's backbone carrying a 14-label SemEval-2020 Task 11 head — the same 39,866-entry tokenizer, byte-identical in vocabulary, merges and added tokens. It is a worked example of what the root becomes once a head is attached, not a model to judge real documents with. Measured there: attention KV allocation 0 MiB, native RSS 173 MiB, 56.4 ms of compute on a 28-token window, 4 threads.
docker-train/ — the same idea with your own labels. Your data in, a Q8
classifier and a serving container out.
python make_base.py # assembles base/ from this repository's own weights
tar -xzf vendor/runtime_source.tar.gz -C vendor/
docker compose run --rm validate
docker compose run --rm train-cpu
docker compose run --rm export
docker compose up model
On Windows, run the same lines in PowerShell with Docker Desktop. Cloning with Git for Windows is
fine: this repository's .gitattributes turns off line-ending conversion, so the files arrive
byte-identical and the containers' own hash checks pass. A clone made before 2026-09-22 has CRLF
line endings and needs to be cloned again.
On the four bundled example documents that runs in a couple of minutes and ends at
"Please refund this purchase and return the order." → refund 0.999 and
"배송 상태를 알려주세요." → shipping 0.998, because the vocabulary keeps every Hangul-carrying
token. Every export checks its own specialised-versus-generic output parity; on that run it was
0.0 on logits, 0.0 on probabilities and zero label disagreements.
Neither container runs the published root as it stands — the root is headless, so both work with a head attached on top of it.
Usage
from transformers import AutoModel, AutoTokenizer
model_id = "mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless"
tokenizer = AutoTokenizer.from_pretrained(model_id, trust_remote_code=False)
backbone = AutoModel.from_pretrained(model_id, trust_remote_code=False)
The weight file contains no score.weight. Outputs are hidden states; they have no class
meaning until you train a head.
Check the tokenizer against your text first
The vocabulary is cut, so token counts differ from the upstream model, and they differ unevenly by language. Measured on one sentence each:
| upstream 248k | this root | ratio | |
|---|---|---|---|
| English | 9 | 12 | 1.33x |
| Korean | 16 | 16 | 1.00x |
| Chinese | 8 | 58 | 7.25x |
| Japanese | 10 | 44 | 4.40x |
Korean is unchanged because the cut rule keeps every Hangul-carrying token regardless of its id. Every Han token in the upstream vocabulary sits above id 65,536 and none survives, so Chinese and Japanese encode as bytes. Compute scales with token count, so if your text is CJK this is not the right root for you.
len(tokenizer("a representative document from your data")["input_ids"])
Train a head
git clone https://huggingface.co/mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless
cd qwen3.5-4l-vocab40k-en-ko-headless/distillation
python -m pip install -e .
qwen35-distill finetune \
--checkpoint .. \
--train examples/multilabel_train.jsonl \
--labels examples/labels.json \
--mode multilabel \
--output my-classifier-4l
The packaged platform has six commands — validate-dataset, extract-text, materialize,
materialize-classifier, finalize-base, distill — and none of them cuts a vocabulary.
Running the ladder from the upstream teacher reproduces the depth reduction and gives you a
four-layer model with the full 248,320-row embedding, which is a different artifact from this
root. If you want this vocabulary, start from this root.
If you train a head, the threshold is yours to pick
One thing the benchmark's own tables show clearly, and this card had not: the decision threshold the protocol lands on is unstable, and at whatever it lands on the head over-predicts.
Across three seeds of one arm the frozen 18-point grid selected 0.10, 0.15 and 0.30, and for another it selected 0.05, 0.20 and 0.20 — a 3x and a 4x range for one model on one split, from nothing but the seed. The task-blind arms behave the same way: across the thirteen task-blind runs of the final test the selected threshold spans 0.05 to 0.45.
And every run predicts more positives than the data contains. 34.4% of the (document, label) pairs are actually positive; the selected thresholds put predictions at 41% to 71%. One control run returns 1.34 false positives for every true one. That is what a threshold picked to maximise macro F1 on a 14-label problem with rare labels does — macro F1 rewards getting something right on the rare labels, and the cheapest way to do that is to say yes more often.
None of this is a defect in the backbone; it is a property of the frozen protocol's selection rule and of the split's label sparsity. It is here because it is the part of these numbers most likely to surprise you when you attach your own head: do not inherit these thresholds, and calibrate on your own data for the precision/recall balance your application wants.
Architecture
Qwen3.5-0.8B, 24 layers upstream
└─ 24→8 knowledge distillation layers (0,4,6,11,13,16,20,23)
└─ 8→6 layers (0,1,3,4,6,7)
└─ 6→4 layers (0,2,3,5)
└─ vocabulary cut 248,320 → 39,866 rows
└─ this root 4L · hidden 1024 · headless
| Layer types | linear_attention → full_attention → linear_attention → full_attention |
| Hidden / FFN | 1,024 / 3,584 |
| Runtime class | Qwen3_5TextModel, Transformers 5.13.0 |
| Distillation objective | hidden-state and interface representation matching; no logit KD, no cross-entropy, no labels |
| Recurrent scalars | 192 frozen during KD; the remaining parameters trained |
How the layer maps were chosen, stated plainly. They were selected by ranking candidate maps
on labeled SemEval-2020 Task 11 data — the 6→4 map is rank 1 of all fifteen four-of-six
candidates by train macro F1. Older config.json files in this repository record
selection_basis: "predeclared_architecture_only_default", which is wrong; the current root
records semeval_ranked_inherited. Because the distillation boundary targets are a function of
the map, that choice is in the weights. Cutting the vocabulary does not remove it.
Where the parameters actually live
The two cuts had to happen in that order, and the reason is arithmetic rather than judgement.
At 24 layers the embedding table is a third of the model. Cutting to 4 layers removes 84% of the layer stack — 498M parameters down to 80M — and touches the embedding not at all, so the model only falls from 752M to 334M and the embedding's share climbs from 33.8% to 76.1%. After the depth cut, three quarters of what remained was a lookup table for 248,320 entries that this model would never need, which is what made the vocabulary the next thing worth cutting.
Cutting it to 39,866 brings the embedding back to 33.8% of the model — the same balance the 24-layer teacher had — and the model to 120.6M. The two grid points below this root keep going: at 15,380 entries the embedding is 16% and the layer stack is nearly everything left.
What the ladder itself cost
Distillation is three stages, and the tables in this repository record each one's loss.
Read as totals the sequence is not monotone: 24→8 drops sixteen layers for 0.176, 8→6 drops two for 0.034, and then 6→4 drops two for 0.047 — 39% more than the step before it for the same number of layers. Read per layer removed it is monotone and rising: 0.0110, 0.0169, 0.0235. The last two layers cost 2.1x per layer what the first sixteen did.
We do not read a mechanism into that. It is one run per stage with no spread, on a matching objective that never sees a label, and the interface loss is larger than the final loss at every stage, which is what one would expect when the boundaries being matched are the constraint.
The path, step by step
Everything below is measured. Each section is a situation we assumed, and it names the data and hardware we used to set that situation up. We were not building for a particular task, so we did not hold one dataset fixed across the whole path: section 1 assumes an edge board answering short documents and uses a 3-class internal set for it; sections 2 onward assume a desktop GPU doing multi-label document work and use a 14-label SemEval-derived set. Read each section as its own scenario — the arms inside it share data, protocol and host, which is what makes the comparison inside it mean something.
1. Prompting the original model
Situation assumed: an edge board classifying short documents under a hard memory ceiling. The data we used for it: legacy37, 3 classes, 37 documents, internal labels, not human gold. Hardware: UNO Q edge board. Measured 2026-07-15.
| arm | accuracy | macro F1 | artifact | one document | memory |
|---|---|---|---|---|---|
| P0 prompt, Q4_0 generation | 62.16% | 0.2556 | 507.2 MB | warm p50 8.57 s | ≥2.441 GiB, OOM at request 31 |
| C24 24L backbone + 3-logit head, Q6_K | 94.59% | 0.9571 | 629.7 MB | cold 21.32 s | 1,454.7 MiB |
| C8Q 8L distilled + head, F16/Q6_K | 97.30% | 0.9781 | 395.3 MB | cold 11.60 s | 1,192.0 MiB |
P0 returned a valid label for all 37 documents and returned scam_attempt for all 37 of them.
That is a class collapse, not a low score spread over classes: the set is 23 scam_attempt /
11 normal / 3 scam_news_edu, so 62.16% is exactly the majority-class rate. P0 also hit the
2.441 GiB cgroup limit after 31 consecutive requests and took the production runner down with it.
C8Q against C24 on the same runtime: 37-row wall 421.24 s → 145.07 s, cold-one 21.32 s → 11.60 s. The paired bootstrap interval on the quality difference includes zero, so we read it as quality not collapsing under the compression rather than as an accuracy win.
What the numbers are measured at: P0's memory is a container cgroup including the router and model server; the classifier numbers are a single foreground process peak. The classifier per-row wall is an amortized batch including model load, not a persistent HTTP p50. Different layers, so read the memory column as “what the board had to hold,” not as a like-for-like delta.
Our reading: generation costs a system prompt and a decode loop per request, and in this situation that was the binding constraint before accuracy ever was. Everything after this section is a classifier.
2. As a classifier: 24 layers, 248k vocabulary
Situation assumed: a desktop GPU assigning many labels to long documents. The data we used for it: SemEval-2020 Task 11, 14 labels, 56-document opened calibration split. Hardware: RTX 5070 Ti. Protocol: 5 epochs, window 256 stride 128, batch 1 with 32-step accumulation, AdamW lr 2e-5, BF16, labelwise-max aggregation, 18-point threshold grid. Three seeds (41/42/43), SD is a three-sample SD.
| macro F1 | p50 | peak RSS | weight file | GPU J/doc | |
|---|---|---|---|---|---|
| 24L, 248k, BF16 | 0.5487 ± 0.0228 | 221.3 ms | 2,037 MiB | 1,435 MiB | 33.37 |
This is the starting point for everything below: the full-depth, full-vocabulary backbone with a freshly initialised 14-label head.
3. Removing layers
Same vocabulary, same protocol, same seeds. Only depth changes.
| depth | macro F1 | p50 | weight file | GPU J/doc |
|---|---|---|---|---|
| 24L | 0.5487 ± 0.0228 | 221.3 ms | 1,435 MiB | 33.37 |
| 8L | 0.5686 ± 0.0212 | 73.8 ms | 802 MiB | 11.31 |
| 6L | 0.5627 ± 0.0241 | 52.0 ms | 720 MiB | 7.98 |
| 4L | 0.5602 ± 0.0391 | 29.7 ms | 637 MiB | 4.71 |
Quality across the four depths spans 0.0199. The SDs span 0.0212 to 0.0391. The spread is inside the run-to-run variation, so we do not read an ordering here. Latency moves 7.5x and energy per document moves 7.1x over the same range.
Our reading: on this task the depth we removed was not carrying quality that this measurement can see. We do not know whether that holds on a harder task; we have only measured this one.
4. Changing precision
4 layers, 248k vocabulary, same heads. BF16 runs on CUDA; the two GGUF precisions run on CPU with 4 threads — two different deployment situations. The latency column therefore reads within a row's own host, not down the column; the file and quality columns read down the column.
| precision | macro F1 | p50 | peak RSS | weight file |
|---|---|---|---|---|
| BF16 (CUDA) | 0.5602 ± 0.0391 | 29.7 ms | 2,082 MiB | 637 MiB |
| F16 GGUF (CPU) | 0.5592 ± 0.0398 | 783.8 ms | 2,075 MiB | 648 MiB |
| Q8_0 GGUF (CPU) | 0.5612 ± 0.0356 | 696.6 ms | 1,781 MiB | 349 MiB |
Q8_0 halves the file against BF16 and moves quality by +0.0010, far inside the SD.
Our reading: 8-bit quantization of this backbone did not cost anything we could detect. We later saw a hint that quantization costs a little more once the vocabulary is cut — 0.0015 to 0.0068 across three cut arms, against roughly zero for the two full-vocabulary arms — but at three seeds that is not separable either, and we record it as an observation, not a finding.
5. Cutting the embedding to 128k
The embedding dominates a four-layer model: 254,279,680 of 334,096,704 parameters, 76.11%. Depth reduction only ever touched the other 24%.
| macro F1 | p50 | Q8_0 file | |
|---|---|---|---|
| 4L, 248k | 0.5602 ± 0.0391 | 29.7 ms | 349 MiB |
| 4L, 128k | 0.5637 ± 0.0321 | 29.5 ms | 218 MiB |
No layer weights changed; only embedding rows were remapped.
Where those 128,000 entries came from. v128k_remap_manifest.json records
source_128k_classifier_tokenizer pointing at models/semeval-propaganda. The vocabulary was
carried over from the SemEval propaganda classifier's tokenizer by token string, and the rule
that chose those 128,000 entries is not documented anywhere in this repository. A root described
as task-free therefore had a vocabulary selected with the task in view. That is why there is a
section 6.
It also cost Korean. Korean-capable tokens fell from 10,927 to 2,137, and the same Korean text costs +92.17% more tokens under the 128k vocabulary.
6. Cutting the original embedding by rule: English and Korean
This is the current root. The rule was written down and frozen before anything was built, and no vocabulary size was chosen on task results — four sizes were built, all four were measured, and one is published on cost grounds.
The rule. Keep the top N entries by upstream BPE id, plus every token that carries Hangul (by Unicode script property, plus byte fragments whose bytes lie entirely in the Hangul ranges), plus the 256-byte alphabet, plus the 33 added tokens. At N = 32,768 that is 39,866 entries.
The whole 24→8→6→4 ladder was re-distilled from the upstream teacher once per grid point, so these are trained at their vocabulary rather than cut afterwards.
| N | entries | macro F1 | BF16 p50 | Q8_0 file | GPU J/doc |
|---|---|---|---|---|---|
| 8,192 | 15,380 | 0.5699 ± 0.0160 | 37.2 ms | 98 MiB | 6.00 |
| 16,384 | 23,551 | 0.5781 ± 0.0032 | 33.8 ms | 106 MiB | 5.40 |
| 32,768 | 39,866 | 0.5721 ± 0.0087 | 30.4 ms | 124 MiB | 5.01 |
| 65,536 | 72,455 | 0.5778 ± 0.0079 | 30.0 ms | 159 MiB | 4.73 |
The four span 0.0081 in quality against a largest SD of 0.0160. We do not read an ordering. An earlier build of the same grid, discarded for an unrelated defect, put them in a different order, which is the clearest evidence we have that the order is noise.
Cost is monotone and has a corner. 64k→32k buys 22.0% of the file for 1.2% BF16 latency; 32k→16k buys 14.1% for 11.2%; 16k→8k buys 8.2% for 10.2% and 11.1% more energy. We published 32k because it is the last step where the file saving is larger than what it costs, not because it scored best — it did not.
Cutting the vocabulary makes inference cost more, not less. That reads backwards, and the mechanism is the tokenizer. Fewer entries means the same text is spelled with more and shorter tokens, and the protocol reads a document in fixed 256-token windows, so more tokens means more windows and more forward passes. Measured on the same 56 documents with each grid point's own tokenizer:
| N | entries | tokens/doc | windows/doc | BF16 p50 | GPU J | Q8_0 p50 | Q8_0 file |
|---|---|---|---|---|---|---|---|
| 8,192 | 15,380 | 1,629 | 12.27 | 1.239x | 1.269x | 1.346x | 98 MiB |
| 16,384 | 23,551 | 1,462 | 10.89 | 1.124x | 1.143x | 1.154x | 106 MiB |
| 32,768 | 39,866 | 1,345 | 10.04 | 1.012x | 1.060x | 1.103x | 124 MiB |
| 65,536 | 72,455 | 1,264 | 9.36 | 1.000x | 1.000x | 1.000x | 159 MiB |
From the largest grid point to the smallest the window count rises 1.311x, and the three measured costs come in at 1.239x, 1.269x and 1.346x — the window count predicts all three to within a few percent, on the same backbone at the same depth under the same protocol, with only the vocabulary different.
So the cut buys file size and pays for it in compute. The smallest grid point is the smallest file in the family and also the slowest and the most energy-hungry member of it. The corner at 32k in the table above is where those two curves cross.
Scope: English news documents. The ratio depends on how well your text is covered by the entries that survived, so Korean, and especially CJK, will not move the same way — see the tokenizer table under Usage.
Beside other models
61 arms, one frozen protocol, 183 trainings, 549 calibrations and 549 isolated inference profiles, completed 2026-09-19. Families: Qwen3.5 (13), Qwen2.5 (2), and ten encoder families (46) — BERT, RoBERTa, XLM-R, DeBERTa-v3, mDeBERTa-v3, ALBERT, ELECTRA, MPNet, ModernBERT, DistilBERT, mBERT, multilingual MiniLM. The four vocabulary grid arms of section 6 were trained later under the same protocol, split and seeds, which brings the field to 65.
Top twelve by BF16 three-seed mean:
| # | arm | macro F1 | SD | p50 ms | file MiB | J/doc |
|---|---|---|---|---|---|---|
| 1 | qwen35-taskblind-base4l-N16384 (23,551) |
0.5781 | ±0.0032 | 33.8 | 198 | 5.40 |
| 2 | qwen35-taskblind-base4l-N65536 (72,455) |
0.5778 | ±0.0079 | 30.0 | 294 | 4.73 |
| 3 | qwen35-taskblind-base4l-N32768 (39,866) — this root |
0.5721 | ±0.0087 | 30.4 | 230 | 5.01 |
| 4 | qwen35-taskblind-base4l-N8192 (15,380) |
0.5699 | ±0.0160 | 37.2 | 182 | 6.00 |
| 5 | qwen35-taskfree-base8l-v248k |
0.5686 | ±0.0212 | 73.8 | 802 | 11.31 |
| 6 | qwen35-specialized4l-v128k |
0.5685 | ±0.0133 | 29.8 | 402 | 4.67 |
| 7 | qwen35-taskfree-base4l-v128k |
0.5637 | ±0.0321 | 29.5 | 402 | 4.61 |
| 8 | qwen35-taskfree-base6l-v248k |
0.5627 | ±0.0241 | 52.0 | 720 | 7.98 |
| 9 | qwen35-taskfree-base8l-v128k |
0.5611 | ±0.0050 | 74.3 | 567 | 11.21 |
| 10 | qwen35-taskfree-base4l-v248k |
0.5602 | ±0.0391 | 29.7 | 637 | 4.71 |
| 11 | qwen35-taskfree-base6l-v128k |
0.5568 | ±0.0245 | 51.7 | 485 | 8.16 |
| 12 | roberta-base |
0.5524 | ±0.0143 | 11.1 | 238 | 2.24 |
The first four rows are the vocabulary grid, and their spread of 0.0081 is smaller than the largest SD among them. The median three-seed SD across all 65 arms is 0.0117 and six arms sit within one such SD of the top, so this ordering is not resolved by the measurement. Across the same field, macro F1 spans 0.093 while document p50 spans 40x (5.7 ms to 228.9 ms), the weight file spans 64x (22 MiB to 1,435 MiB) and GPU energy spans 43x (0.81 J to 34.76 J).
Three arms — mdeberta-v3-4l, deberta-v3-base-6l, deberta-v3-base-8l — score exactly
0.475627, which is the macro F1 of an all-positive predictor on this split. They failed to train
under the shared protocol. We report them rather than dropping them.
On cost the encoders are ahead of us at this depth: roberta-base-6l runs at 7.4 ms and 157 MiB
against our 29.5 ms and 402 MiB at the time of that table, with a quality difference of 0.021
against SDs of 0.014 and 0.032.
Full table: benchmark/full/61_arm_bf16.csv.
What the whole field looks like
Both panels hold the same 65 arms; only the cost axis changes. The dashed line is the Pareto
frontier — the arms that no other arm beats on quality and cost at the same time. Seven arms hold
it on latency, eight on energy, and the RoBERTa family holds the cheap half of both. This root
sits at the top of the quality axis and pays about 2.7x the latency of roberta-base to be there.
Laid beside each other, the spans are the finding. Across the whole field macro F1 moves 1.2x — 0.4756 to 0.5781 — while the weight file moves 64.4x, GPU energy 42.9x and document latency 40.3x. The quality span is narrower than even that suggests: its bottom is exactly the all-positive predictor, so the entire field fits inside 0.093 of usable range above a model that answers yes to everything. Peak RSS barely moves, because it is dominated by the harness rather than by the weights.
Every encoder family was cut to 4, 6 and 8 layers under the same protocol, and its own full-depth
checkpoint was kept beside them. In 7 of the 12 families the best arm is not the deepest one.
bert-base, mbert and electra-base peak at 4 layers, minilm-multilingual at 6, mpnet-base
and mdeberta-v3 at 8. The five families where depth does pay — roberta, xlmr, deberta-v3,
albert, modernbert — pay for it in latency and file size. Three arms did not train at all under
the shared protocol and sit exactly on the floor.
This is an observation about this task at this resolution. It is not a claim that depth does not matter, and it is not why this root has four layers — section 3 is.
The last figure is the honest summary of the ordering. The median three-seed SD across the field is 0.0116, and six arms sit within one such SD of the best. Several SD bars are wider than the whole gap between the top ten arms. The ranking is reported because it was measured, not because the measurement resolves it.
The final test
On 2026-09-23 one fixed-protocol evaluation was run against the SemEval-2020 Task 11 v2 test re-split, under a preregistration frozen before any test byte was read.
It is not a never-seen holdout. This project has used the v2 split before, and the frozen
document records prior test exposure as true. It is not the official leaderboard test, there is
no secure-harness attestation, and nothing in it establishes Korean quality or general
intent-classification behaviour. An earlier draft of the preregistration wrongly claimed the split
had never been opened; that draft is kept beside the corrected one rather than deleted.
What the freeze fixed in advance: the roster of eleven arms, the decision threshold for every
arm/seed/precision — each read from the arm's existing calibration report and never recomputed on
test — the aggregation rule, the metric set, and the rule that every result would be published
whatever it said. The archived payload had to match SHA-256 ed224269dc… before extraction. It
did. 31 of 31 jobs completed, 55 documents, 14 labels, zero failures, no training and no threshold
search. All 31 per-run metrics were then recomputed from the saved probabilities by a separate
standard-library implementation and agreed with the evaluator.
| Arm | Precision | Calibration | Test | Δ |
|---|---|---|---|---|
| task-blind 4L N=32,768 — this root's classifier | BF16 | 0.5719 ± 0.0092 | 0.5988 ± 0.0103 | +0.0269 |
| task-blind 4L N=32,768 | Q8_0 | 0.5849 (seed 41) | 0.5862 (seed 41) | +0.0013 |
| task-blind 4L N=65,536 | BF16 | 0.5775 ± 0.0076 | 0.5832 ± 0.0047 | +0.0057 |
| task-blind 4L N=16,384 | BF16 | 0.5787 ± 0.0021 | 0.5379 ± 0.0496 | −0.0408 |
| task-blind 4L N=8,192 | BF16 | 0.5699 ± 0.0160 | 0.5555 ± 0.0070 | −0.0144 |
| historical 4L v128k | BF16 | 0.5637 ± 0.0321 | 0.5747 ± 0.0183 | +0.0110 |
| historical 8L v248k | BF16 | 0.5686 ± 0.0212 | 0.5718 ± 0.0213 | +0.0032 |
| historical 4L v248k | BF16 | 0.5602 ± 0.0391 | 0.5707 ± 0.0081 | +0.0105 |
| historical 24L v248k | BF16 | 0.5487 ± 0.0228 | 0.5612 ± 0.0102 | +0.0125 |
| roberta-base 12L | BF16 | 0.5524 ± 0.0143 | 0.5883 ± 0.0109 | +0.0359 |
| roberta-base 6L | BF16 | 0.5427 ± 0.0139 | 0.5839 ± 0.0152 | +0.0411 |
| all-positive predictor | — | 0.4756 | 0.4866 | — |
Quantization cost 0.0013 macro F1 on test, which is the same "nothing measurable" that section 4 saw on calibration, now seen on a split the thresholds were not fitted to.
The +0.0269 is not a result about this model. Nine of the eleven arms moved up, the eleven-arm mean move is +0.0085, and the two RoBERTa references moved up the most of all. Whatever makes this test split score higher than the calibration split applies to the whole board, so the move is a property of the split pair rather than of any arm on it.
Neither does any arm separate from another. This root's mean ± SD interval overlaps roberta-base
(0.5883 ± 0.0109), roberta-base-6l (0.5839 ± 0.0152) and both historical 4L arms. Where it does
clear an interval it clears N=65,536 by 0.0006 and N=16,384 by 0.0010 — gaps one to two orders of
magnitude below the spread of the design that produced them.
Two arms moved down. N=8,192 fell 0.0144, and N=16,384 fell 0.0408 while carrying the widest spread in the table.
Every arm and every seed lands inside a 0.103-wide band on 55 documents, and for most arms the distance between their own three seeds is larger than the distance to their neighbours. N=16,384 is the extreme: one seed at 0.5951 against two near 0.51.
Per label, F1 tracks support almost monotonically — Loaded_Language at 45 supporting documents
scores 0.918, and the six labels with 14 or fewer land between 0.446 and 0.481. Recall exceeds
precision on all fourteen labels: at the frozen thresholds, which run from 0.05 to 0.3, the head
over-predicts everywhere. A head trained for a deployment would be calibrated differently.
The v2 split is now retired from tuning and model selection under this protocol. v1 was not opened, and its never-used status is not asserted.
Evidence: REPORT.md lines 1–89, RESULTS.json lines 1–742 (SHA-256
0d2da5e2e8b250671bf3b19be0e7bce49f14fe492f9c71ce75717b0eee02dec7), PER_LABEL.csv, and the
frozen preregistration (SHA-256 85ec92a0be…). All six are registered in this project's research
store, and the run is recorded there as artifact_final_test_20260923_v1.
Two things we checked because they would have changed the reading
Does the host matter?
Our first cut-versus-source latency ratios used the benchmark's frozen rows as the denominator and made every cut look slower. Re-measuring the source arm on the same host in the same run gave a host factor of 1.0525 (BF16), 1.0448 (F16), 1.1059 (Q8_0), with RSS ratios at 0.99. On the corrected same-host basis the ratios inverted: a 64k cut runs at 0.966x to 0.991x of the source p50 depending on precision, and at 0.977x its BF16 energy per document.
| cut | Q8_0 p50 vs source | Q8_0 RSS vs source |
|---|---|---|
| v64k | 0.966x | 0.680x |
| v32k | 1.036x | 0.623x |
| v16k | 1.167x | 0.598x |
Does the 262k context help?
The protocol reads every document in 256-token windows, which caps a 262,144-position decoder at the same window as a 512-position encoder. The median calibration document is 1,034 tokens and 96% fit inside 4,096, so we tested it twice.
Reading at a longer window, same trained heads, seed 41:
| window | this family (262k ctx) | ModernBERT (8,192) | RoBERTa (514) | DeBERTa-v3 (512) |
|---|---|---|---|---|
| 256 | 0.5816 | 0.5126 | 0.5381 | 0.5330 |
| 512 | 0.5633 | 0.4904 | 0.5509 | 0.5376 |
| 1,024 | 0.5722 | 0.4855 | — | — |
| 2,048 | 0.5408 | 0.4795 | — | — |
| 4,096 | 0.5480 | 0.4788 | — | — |
An encoder with an 8,192 context loses 0.0338 over 256→4,096 and the 262k model loses 0.0336.
Training the head at window 4,096 instead of 256, three seeds:
| training window | macro F1 |
|---|---|
| 256 / stride 128 | 0.5721 ± 0.0087 |
| 4,096 / stride 2,048 | 0.5446 ± 0.0062 |
Confound we did not resolve: a 4,096 window yields 280 training windows where 256 yields thousands, so the 50%-overlap windowing was also acting as augmentation. This comparison changes the context and the amount of training signal at the same time.
Our reading: we do not think context length is what is moving numbers on this task, mostly because the 8,192 encoder tracks the 262k model so closely. We are not claiming long context is useless in general.
And what does a wider window cost?
Quality was one question. Time is another, and this card did not have it, so it was measured afterwards: backbone forward pass only, BF16 on one RTX 5070 Ti, every window of a document submitted as one batch, real token lengths from the same 56 documents.
| window | windows/doc | positions pushed | padding | ms/document |
|---|---|---|---|---|
| 256 | 10.04 | 143,872 | 2.6% | 15.90 |
| 512 | 4.80 | 137,728 | 5.7% | 15.76 |
| 1,024 | 2.29 | 131,072 | 14.4% | 15.94 |
| 2,048 | 1.30 | 149,504 | 38.0% | 18.61 |
| 4,096 | 1.04 | 237,568 | 66.6% | 29.84 |
A wider window does not buy speed. Going from 256 to 1,024 removes 77% of the windows and costs the same 15.9 ms, because a 50% stride pushes roughly twice the document's tokens through the model whatever the window is — the work is conserved, only its shape changes. Past 1,024 it gets worse rather than better: the median document is 1,034 tokens, so at 4,096 two thirds of every forward pass is padding, and the bill nearly doubles.
Put beside the quality table above, that is the whole answer for this task: a wider window costs accuracy, and past 1,024 it costs time as well. Neither is an argument about long context in general — it is what a 262k-position decoder does when it is asked to read 1,000-token news articles.
This is a micro-benchmark of the backbone forward pass, run on 2026-09-24, outside the frozen
61-arm protocol. Its absolute milliseconds are not the benchmark's document p50 — no head, no
tokenisation, no data loading — and they should not be compared with the tables above. The ratios
between window sizes are the measurement. Scripts and results: benchmark/cost_probe/.
The dedicated runtime
Alongside the model there is a runtime built only to run it. It is not needed to use this root — everything in this card was measured without it — but it is the other half of the work, so here is what it is.
What it is
The runtime is a vendored copy of llama.cpp
(ggml-org/llama.cpp) with a pinned patch set applied at
prepare time by prepare_runtime.py. Every patch is anchored to an exact source string and the
preparer raises if an anchor has moved, so the build either reproduces exactly or fails loudly.
The tree is vendored by per-file SHA-256 in a 1,389-file bundle manifest rather than pinned to an
upstream commit, so it rebuilds byte-for-byte but does not name an upstream revision.
It builds exactly one binary, qwen35-classifier, with everything a generator needs switched off:
no server, no examples, no tools, no CUDA, BLAS, Metal, Vulkan or OpenMP, no backend loading.
The x86 target needs AVX2, FMA, F16C, BMI2, SSE4.2 and AVX-512 F/CD/VL/DQ/BW/VNNI; a CPU with
only AVX-VNNI is not supported.
What of it you can actually get. The full v3 tree lives in the research repository's
deployment/ directory, which is not published anywhere, so treat any path starting
deployment/ in this card as a label for where a thing was measured, not as a link. What is
published is the part you can use: the two relaxed sources in runtime/, and a
complete buildable copy of the patched tree inside
docker-train/vendor/, which is what that stack compiles its native
binaries from.
Why a separate runtime at all
A classifier is not a generator, and most of what a decoding runtime does is wasted on it. There is no sampling loop, no second token, nothing to carry between calls, and only one row of the last hidden state ever reaches the head. Four things follow, and they hold at every specialisation level from 2 up:
- No KV cache is allocated. Attention KV allocation is 0 MiB. The window is read once.
- The 14-label head runs inside the graph. Logits come out of the C++ side; there is no Python or torch round trip in the request path.
- Weights are packed once, at first call, into a signed-offset layout and then held: 99,696,640 bytes persistent, with a maximum input scratch of 2,064,384 bytes.
- Idling is free. The thread pool is created with
poll=0, so a loaded, idle service registers 0 CPU ticks in half a second.
What each specialisation level turns on
Q35_SPECIALIZE_LEVEL stages the whole thing, 0 being the stock path and 12 the x86 default.
Every step is a real switch, so each can be measured against the one below it.
| level | what it adds |
|---|---|
| 1 | concat partitioned by channel instead of by ne2, for the classifier's [tokens+3, 6144] input |
| 2 | specialised tail: inp_out_ids is never built, the last block carries only the final 4 token rows, and the head takes one row by view instead of ggml_get_rows |
| 3 | last-layer Q / gate / output projection computed for 4 query tokens only; K and V keep the whole window, the causal mask is sliced to match, RoPE positions untouched |
| 4 | hand-written 4x4 Q8 GEMM |
| 5 | packed Q8 kernel |
| 6 | the v2 reference path: packed weights carried in signed-offset form |
| 7 | aarch64 NEON Q8 kernel (present in the source; not built or tested here) |
| 8 | Q8 tiles at arbitrary column counts, not only multiples of four |
| 9 | 8 + a persistent thread pool that sleeps between requests |
| 10 | 9 + recurrent state kept hot row by row through decay, delta, update and output |
| 11 | 10 + a 4x8 ZMM Q8 kernel. Measured slower, so not adopted |
| 12 | 10 + a dedicated AVX-512 routine for the 128-wide recurrent state |
Why it stays exact
None of this is an approximation. The packed kernel folds the unsigned-to-signed shift into a
single per-matrix correction and then uses _mm512_dpbusd_epi32, which preserves the reference
path's eight int32 lanes and its float accumulation order. The tail narrowing drops rows the head
provably never reads. The recurrent rewrite keeps every vector reduction in its original order.
The check that matters: across 56 documents and 511 windows, level 12 against the level 6 reference gave a maximum absolute logit difference of 0.0, a maximum absolute probability difference of 0.0, and zero window-level and document-level label disagreements. Reload, A/B/A, thread counts 1/3/4 and the live service path all matched as well.
What the specialisation buys
On the 128k model, level 12 against level 6 — same binary, same weights, same input:
| tokens in window | level 6 | level 12 | change |
|---|---|---|---|
| 33 | 15.76 ms | 12.29 ms | −22.1% |
| 64 | 20.96 ms | 19.85 ms | −5.3% |
| 129 | 50.84 ms | 37.44 ms | −26.3% |
| 233 | 92.14 ms | 70.47 ms | −23.5% |
| 256 | 77.33 ms | 73.36 ms | −5.1% |
The two small rows are the ones whose token counts sit badly against the 4-wide tile; 256 tokens coming out cheaper than 233 is the same effect read from the other side.
Making it accept a cut vocabulary
As shipped it refused any vocabulary other than 128,000, at two places:
| where | check | effect |
|---|---|---|
qwen35-classifier.cpp:47 |
llama_vocab_n_tokens(...) != 128000 |
throws "this runtime supports only Qwen3.5 4L / 128k / 14-label classification" |
src/models/qwen35.cpp |
GGML_ASSERT(n_layer == 4 && n_embd == 1024 && tok_embd->ne[1] == 128000) |
aborts inside sched_reserve() at level >= 2 |
Both are model-identity guards, not computational dependencies. The specialised tail reads
n_embd, n_cls_out, n_seqs and n_tokens and never the embedding row count, and the
hand-written Q8 kernels operate on hidden 1024. So the port relaxed the vocabulary equality only
(tok_embd->ne[1] == 128000 becomes > 0), in a copy of the source tree; architecture, depth
and width are still enforced, and all five binaries of the frozen v3 build still match their
recorded SHA-256. The two relaxed sources are in runtime/.
After the port, all three cuts load at full specialisation and produce bit-identical output to the generic path:
| cut | tokens in probe | level 0 | level 12 | change |
|---|---|---|---|---|
| v16k | 33 | 14.94 ms | 10.68 ms | −28.5% |
| v32k | 25 | 11.47 ms | 7.36 ms | −35.8% |
| v64k | 23 | 10.32 ms | 7.26 ms | −29.7% |
Running it
The container in docker/ is the shortest path, and
docker-train/ builds its native binaries from this same patched tree. Both are
described at the top of this card.
Before you reach for it
- It needs a GGUF with a classification head. This root is headless, so train a head and convert that checkpoint first. The runtime cannot run the published root as it stands.
- The port is a development port. It has not been through the v3 validation scripts
(
validate_runtime.py,validate_reload.py,validate_threads_service.py); the bit-identity above is the port's own parity check, not that suite. - x86 AVX-512 VNNI only in practice. The NEON path exists in the source but was not built or tested, and the aarch64 / A53 variants are unmeasured.
- The timings here are single-sentence native-compute medians on a host with a desktop session running, not the benchmark's isolated document-level protocol. Token counts differ per cut for the same sentence, so compare within a row, not across rows.
- No number anywhere else in this card came from this runtime. Those all came from the
benchmark's own
profile_inference.py.
What this does not tell you
- The one test evaluation is not a holdout. The 2026-09-23 run above used the v2 re-split,
which this project had used before; its frozen preregistration records that prior exposure.
Every other number on this card is the opened 56-document calibration split, and there is no
test.jsonlin this repository. - The evaluation is small. 56 documents, 14 labels, and the rarest label appears in 6 of them, so one fourteenth of macro F1 rides on six samples. This is the most likely source of the 0.005–0.039 seed SDs.
- The scenarios are ours, and they are few. The numbers in sections 2–6 come from one situation — English SemEval-derived propaganda technique detection on a desktop GPU — and section 1 comes from one other. This root is not built for either of them; they are the situations we happened to set up to watch the compression. Korean appears only as tokenization parity, with no Korean evaluation anywhere here.
- Three seeds are run-to-run variation, not a confidence interval.
- The layer maps were chosen on labeled data. Disclosed above, not repaired.
- Quality figures belong to fresh-head transfer checkpoints, not to this headless root. The root's downstream quality is NR until you train a head.
- Long-context, calibration, robustness, fairness and production safety are not established.
- Do not use this as a fact checker, a safety oracle, or an autonomous decision maker.
Files
| path | what |
|---|---|
| root | the 39,866-entry headless backbone |
root_manifest.json |
its vocabulary rule, layer maps, hashes, why this grid point |
benchmark/full/ |
the 61-arm table, the grid, the same-host cuts |
benchmark/cost_probe/ |
the tokenisation-cost and window-cost measurements, scripts and results |
benchmark/figures/ |
the twenty-two figures above |
runtime/ |
the two relaxed sources of the dedicated runtime, and its own README |
docker/ |
a container that runs the backbone with a head on it, CPU only |
docker-train/ |
train a classifier on your own data, convert, serve |
models/semeval-propaganda/ |
a separate lineage, not a child of this root |
distillation/ |
the reusable ladder platform |
v128k_remap_manifest.json |
the one surviving record of the superseded 128k root |
SHA256SUMS |
sha256 of every file in this repository |
License and attribution
Apache-2.0 for the released code and model artifacts, subject to upstream model and data terms. Qwen3.5 is attributed to Qwen. Transformers, PyTorch, Hugging Face Hub, WikiText and SemEval remain the work of their respective authors. No WikiText or SemEval source records are redistributed.
The WikiText dataset page carries a license wording discrepancy — metadata lists CC BY-SA 3.0
and GFDL while the prose says CC BY-SA 4.0. Check
Salesforce/wikitext directly.
- Downloads last month
- 757






















docker model run hf.co/mp-juuuns/qwen3.5-4l-vocab40k-en-ko-headless:Q8_0