Downloads · 30 days
0
LocalLLaMA/Qwen3.5-4B-graft16
Qwen3.5-4B-graft16 is a machine learning model from LocalLLaMA. Use it for the machine learning task on the model card, and read the license before you ship it in a product. It is set up for transformers. The card lists the license as apache-2.0.
A graft of Qwen/Qwen3.5-4B: the model is cut at layer 16 of 32, and only the lower half reads your prompt. The upper half never sees it. Instead, the residual stream the lower half produces becomes a memory, and a sma…
Downloads · 30 days
0
Access
Public
Updated Sep 20, 2026
Repo size
3.9 GB
Likes
2
Public
Click a slice to open those files.
.pt3.9 GB · 100%
From the Hugging Face model README
A graft of Qwen/Qwen3.5-4B: the model is cut at layer 16 of 32, and only the lower half reads your prompt. The upper half never sees it. Instead, the residual stream the lower half produces becomes a memory, and a small per-site adapter maps that memory into the keys and values each upper attention layer would have computed for itself. Generated tokens still pass through all 32 layers, so decoding costs exactly what the original model costs.
Time-to-first-token is 1.8-1.95x faster from 16K tokens up, and decoding is unchanged. 1.81B parameters run per prompt token instead of 4.21B.
| Prompt | Qwen3.5-4B TTFT | graft16 TTFT | Speedup | Prompt tok/s | Decode tok/s | Peak memory |
|---|---|---|---|---|---|---|
| 4K | 0.45 s | 0.32 s | 1.44x | 9,048 -> 13,001 | 26.3 -> 26.5 | 8.59 -> 8.61 GB |
| 16K | 1.90 s | 1.06 s | 1.80x | 8,608 -> 15,494 | 25.4 -> 25.6 | 10.39 -> 10.05 GB |
| 32K | 4.19 s | 2.24 s | 1.87x | 7,818 -> 14,633 | 24.2 -> 24.2 | 12.89 -> 12.22 GB |
| 64K | 9.96 s | 5.18 s | 1.92x | 6,581 -> 12,644 | 21.7 -> 21.5 | 17.89 -> 16.57 GB |
| 128K | 26.24 s | 13.47 s | 1.95x | 4,996 -> 9,731 | 17.9 -> 17.5 | 27.88 -> 25.25 GB |
A40 48 GB, bf16, flash-attention-2, batch 1, greedy, 7 timed reps after 2 warmups per length; median reported
(min and p95 within 1% of it). TTFT is prefill plus the first sampled token. Decode is the steady-state rate over
the next 31 tokens and is the same for both models by construction, since generated tokens still cross all 32
layers. bench_ttft.py, included in this repo, reproduces the table.
Two things this does not buy: the KV cache is the same size (the encoder's own attention keys and values are not compressed, so peak memory is within 1-6%), and short prompts gain least.
This is one instance of a general operation we call model grafting — cutting a trained model, changing how information moves through it, and healing the result with a small amount of continued training. The method, the cost law that predicts how much healing a given change needs, and the study this model came out of are written up here:
Long prompts, where most of the wait is the model reading what you wrote. The graft holds general capability close to its parent and is noticeably weaker at one thing: following chains of references through a long context (see the numbers below). Read it before deploying this in an agent loop that chases references across a long document.
Six benchmarks, greedy decoding, thinking disabled, identical prompts for both models. MMLU 5-shot (50 per
subject), GSM8K 5-shot (400 questions), IFEval (541 prompts), HumanEval-instruct, BFCL v4 non-live AST
(100 per category, official bfcl_eval checker), HashHop (Magic's generator, 25 instances per hop count at
1–4 hops, ~8K-token prompts).
| Benchmark | Qwen3.5-4B | Qwen3.5-4B-graft16 |
|---|---|---|
| MMLU | 0.713 | 0.709 |
| GSM8K | 0.915 | 0.907 |
| IFEval (prompt-level strict) | 0.815 | 0.800 |
| HumanEval | 0.829 | 0.799 |
| BFCL (non-live AST mean) | 0.848 | 0.820 |
| HashHop (~8K prompt) | 0.290 | 0.190 |
| Mean of six | 0.735 | 0.704 |
| HashHop (~33K prompt) | 0.100 | 0.010 |
Where the gaps are:
Speed is measured at the top of this card. The capability cost above is what the 1.8-1.95x prefill speedup buys.
Healed on 500M tokens on a single A40, in two stages (100M, then a re-warmed continuation to 500M):
kd_lambda 1.0), not the data's labels. That is what protects instruction following, tool-call
format and thinking mode, which plain continued pretraining erodes.input_layernorm, k_proj, k_norm, v_proj and RoPE do the rest.One finding worth repeating from the run: the distillation loss on held-out agent data fell monotonically (0.58 → 0.41) over the last 300M tokens while no downstream benchmark improved by more than a point. Going from 100M to 500M tokens moved the six-benchmark mean by 0.7 points. The plateau arrives early; more tokens at this split is not the lever.
This checkpoint is not a drop-in transformers model. The loader it needs, modeling_ced.py, is included in
this repo, and the weights file stores only the parameters that were trained — the trainable_only marker
means the frozen ones are loaded from Qwen/Qwen3.5-4B (recorded in ced_config.json), so the base model has to
be available too.
from huggingface_hub import snapshot_download
from transformers import AutoTokenizer
import sys
d = snapshot_download("LocalLLaMA/Qwen3.5-4B-graft16")
sys.path.insert(0, d) # modeling_ced.py ships with the weights
from modeling_ced import CEDForCausalLM
model = CEDForCausalLM.load(d) # frozen weights come from Qwen/Qwen3.5-4B
tok = AutoTokenizer.from_pretrained("Qwen/Qwen3.5-4B")
prompt = tok.apply_chat_template([{"role": "user", "content": "..."}], tokenize=False,
add_generation_prompt=True, enable_thinking=False)
ids = tok(prompt, return_tensors="pt").input_ids.cuda()
out = model.generate(ids, max_new_tokens=256, eos_id=tok.convert_tokens_to_ids("<|im_end|>"))
print(tok.decode(out[0], skip_special_tokens=True))
generate prefills the prompt through the encoder once, builds the memory, and then decodes through the full
stack. model.cfg.tail (256) is the suffix that bypasses memory.
| File | What it is |
|---|---|
ced.pt | trained parameters only, bf16 (3.9 GB) |
ced_config.json | base model, cut depth, tail, adapter type, memory sites |
trainable_only | marker: frozen parameters come from the base model |
log.jsonl | the full training log, including the fixed-batch KL probes |
meta.json | every argument the run was launched with |
modeling_ced.py | the loader and the graft itself (encoder, adapters, prefix-KV attention, prefill/decode) |
bench_ttft.py | the speed benchmark that produced the table above |
results/*.json | raw benchmark and timing output for this checkpoint and its parent |