Text Generation
Transformers
Safetensors
Chinese
English
baihu_ssa
sparse-attention
subq
ssa
long-context
supervised-fine-tuning
transfer-learning
commercial-license-required
conversational
Instructions to use ZichenAI/BaiHu-V1-Flash with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use ZichenAI/BaiHu-V1-Flash with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="ZichenAI/BaiHu-V1-Flash") messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# pip install -U transformers accelerate # Load model directly from transformers import AutoModelForCausalLM model = AutoModelForCausalLM.from_pretrained("ZichenAI/BaiHu-V1-Flash", device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use ZichenAI/BaiHu-V1-Flash with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "ZichenAI/BaiHu-V1-Flash" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "ZichenAI/BaiHu-V1-Flash", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/ZichenAI/BaiHu-V1-Flash
- SGLang
How to use ZichenAI/BaiHu-V1-Flash with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "ZichenAI/BaiHu-V1-Flash" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "ZichenAI/BaiHu-V1-Flash", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "ZichenAI/BaiHu-V1-Flash" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "ZichenAI/BaiHu-V1-Flash", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Docker Model Runner
How to use ZichenAI/BaiHu-V1-Flash with Docker Model Runner:
docker model run hf.co/ZichenAI/BaiHu-V1-Flash
File size: 18,556 Bytes
e282eae 8449246 e282eae 8449246 a427608 e282eae 8449246 e282eae 8449246 e282eae 8449246 e282eae a427608 e282eae a427608 e282eae | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 | ---
library_name: transformers
pipeline_tag: text-generation
language:
- zh
- en
license: other
license_name: baihu-custom-license
license_link: https://huggingface.co/ZichenAI/BaiHu-V1-Flash/blob/main/LICENSE.custom.md
base_model: Qwen/Qwen3-0.6B-Base
base_model_relation: finetune
tags:
- sparse-attention
- subq
- ssa
- long-context
- supervised-fine-tuning
- transfer-learning
- commercial-license-required
- text-generation
---
# BaiHu-V1-Flash
**BaiHu-V1-Flash** is an SSA (Sparse-attention + SubQ) retrofit of `Qwen/Qwen3-0.6B-Base`,
fine-tuned on **1,294 bilingual multi-turn dialogues** whose purpose is **generalizing from a
single worked example** β the model is shown one worked rule / format / method in the first
turn, and later turns ask it to reuse that rule on a *new* case it has never seen.
- Base model: `Qwen/Qwen3-0.6B-Base` (28 layers / 16 Q heads / 8 KV heads / head_dim 128 / 32K context / tied embeddings)
- Parameters: **598.8M** (2.75M are SSA-only modules)
- Architecture: SSA β every layer runs three attention paths (shared / local / sparse-SubQ), each with its own softmax, then summed
- Training data: 1,294 synthetic dialogues (English 693 / Chinese 601), six transfer types
- License: **free for personal use; a paid license is required for commercial use** (see [License](#license))
> **Revision note.** This repository previously hosted the base-pretrain checkpoint of the
> same name (5.0M tokens of continued pretraining, no instruction tuning, no P0 revision).
> It has been **replaced** by the checkpoint described here. The two are not interchangeable:
> this one uses the revised shared branch (Β§2) and is a supervised fine-tune.
---
## 1. Revisions in this release
| | earlier release (now replaced) | **this release** |
|---|---|---|
| Shared path | one mean vector for the **entire prefix** | **one compressed vector per completed block** (P0 revision) |
| Training | continued pretraining, 5.0M tokens of web text | supervised fine-tune, 1,294 transfer dialogues (assistant-token loss) |
| Behaviour | base LM | follows a rule / format established earlier in the dialogue |
Both revisions share the same SSA architecture, base weights and license.
## 2. Architecture
Every layer keeps the base model's MLP / RMSNorm weights and replaces full attention with
an SSA layer built from three parallel paths:
| Path | Role | Complexity |
|---|---|---|
| `shared` | every query attends over **one compressed summary vector per completed block** | `O(TΒ·T/B)` |
| `local` | dense causal attention over the most recent window | `O(TΒ·w)` |
| `sparse` (**SubQ**) | only 4 of 16 query heads produce block scores, shared across the head group; real attention is computed only for the selected top-k blocks | `O(TΒ·kΒ·B)` |
### The P0 revision (this release)
The previous revision compressed the **whole prefix into a single mean vector**. A query
could only see one undifferentiated global average, the single-element softmax made that
average inject at full weight, and during 5.0M tokens of continued pretraining the learned
gate **shrank instead of growing** (0.0100 β 0.0129 β 0.0122) β i.e. the optimizer actively
suppressed the branch.
This release implements the design the project always documented: **one compressed key/value
per completed 64-token block**, so queries do a genuine softmax over blocks and "which block
matters" becomes learnable. Cost is unchanged (`O(TΒ·T/B)`; block summaries are still computed
once per layer).
### Hyperparameters
| Parameter | Value | Meaning |
|---|---|---|
| `ssa_block_size` | 64 | block size B |
| `ssa_top_k` | 8 | blocks selected by the sparse path |
| `ssa_local_blocks` | 2 | local window = 3 Γ 64 = 192 tokens |
| `ssa_num_subq_heads` | 4 | SubQ heads, r = 16 / 4 = 4 |
| `ssa_router_dim` / `ssa_compress_dim` | 128 / 128 | router subspace / summary width |
## 3. Training
**Data.** 1,294 synthetic multi-turn dialogues (en 693 / zh 601), each 6β12 messages: the
first user turn gives a worked case or a rule, a later user turn introduces a new instance
that can only be handled by reusing it, and the last turn pushes generalization one step
further. Six `transfer_kind`s are covered: `rule_induction`, `analogy_transfer`,
`format_transfer`, `counterfactual`, `cross_domain`, `teaching_loop`.
Split (stratified by language Γ kind, seed 0): **train 1,165 sessions / 345.7K tokens**
(assistant 188.8K), **val 129 sessions / 38.2K tokens** (assistant 20.5K).
**Recipe.** Loss is computed on assistant tokens only; sequences are padded to a multiple of
the SSA block size (64); rendering uses the tokenizer's own chat template
(`apply_chat_template`, `add_generation_prompt=False`, i.e. the final assistant turn carries
Qwen3's empty `<think></think>` block).
| Item | Value |
|---|---|
| Epochs / steps | 3 / 219 |
| Tokens seen | 1.15M |
| Batch | 4 Γ grad-accum 4 (effective 16) |
| Optimizer | AdamW, lr 2e-5, cosine to 10%, 20-step warmup, wd 0.1 |
| Precision | bfloat16 |
| Hardware / time | RTX 4090D 24GB β **6.9 min**, ~2950 tok/s, 19.6GB peak |
| Val loss / ppl | 2.534 β 1.646 β 1.539 β **1.537 / 4.65** (converged after ~2 epochs) |
To make the SSA-only modules trainable, the two output projections and the shared gate are
re-initialised to a small non-zero scale (0.01) before training β starting from the exact
donor checkpoint would freeze all 2.75M of them at zero gradient.
## 4. Evaluation
### 4.1 Held-out transfer loss (129 unseen sessions, 20,520 assistant tokens)
Both models evaluated with the identical script, mask and batching.
| Metric | Qwen3-0.6B-Base | **BaiHu-V1-Flash** | Change |
|---|---|---|---|
| loss | 2.5339 | **1.5376** | β39.3% |
| **ppl** | **12.603** | **4.654** | **β63.1%** |
| ppl (en) | 14.836 | 5.118 | β65.5% |
| ppl (zh) | 10.542 | 4.187 | β60.3% |
Per transfer type (ppl):
| Kind | Base | BaiHu-V1-Flash |
|---|---|---|
| rule_induction | 7.27 | 2.69 |
| analogy_transfer | 23.69 | 9.08 |
| format_transfer | 18.58 | 5.72 |
| counterfactual | 9.13 | 3.67 |
| cross_domain | 16.40 | 6.51 |
| teaching_loop | 8.26 | 2.95 |
The improvement holds in **all 8 slices** (2 languages Γ 6 kinds + overall).
### 4.2 Generation samples (12 prompts, one per language Γ kind, greedy)
- **Base**: **7/12 outputs collapse into repeated symbols** (`βββ`, `Π°ΡΠΈΠΎΠ½`,
`.TRAILING`), the rest are off-topic or hallucinated β the base model has never seen this
dialogue format.
- **BaiHu-V1-Flash**: **0/12 degenerate**; it reuses the rule/format from earlier turns and
is correct on most samples (`B12A`, `100, 50, 25, 12.5, β¦`, format rewrites). Arithmetic
errors remain β a 0.6B model with 1.3K training dialogues still slips on multi-step math.
### 4.3 Standard benchmarks (lm-evaluation-harness)
Both checkpoints were scored with `lm-evaluation-harness` **0.4.13** in one environment, with
the same prompts, batch size and dtype (`bfloat16`), over the **entire evaluation split** of
every task β 2,376 arc_easy / 1,172 arc_challenge / 10,042 hellaswag / 1,838 piqa / 1,267
winogrande examples, with no `--limit` subsampling β so the two columns are like-for-like. The
metrics are the harness's 0-shot numbers, scored in raw-completion mode (no chat template),
which is what a base checkpoint supports.
| Task | Metric | Qwen3-0.6B-Base | **BaiHu-V1-Flash** | Ξ |
|---|---|---|---|---|
| arc_easy | acc_norm | 0.5791 Β±0.0101 | **0.5939 Β±0.0101** | +0.0148 |
| arc_challenge | acc_norm | 0.3848 Β±0.0142 | **0.3882 Β±0.0142** | +0.0034 |
| hellaswag | acc_norm | 0.5385 Β±0.0050 | **0.5507 Β±0.0050** | +0.0122 |
| piqa | acc_norm | 0.6997 Β±0.0107 | **0.7084 Β±0.0106** | +0.0087 |
| winogrande | acc | 0.5856 Β±0.0138 | **0.6062 Β±0.0137** | +0.0206 |
| unweighted mean | | 0.5575 | **0.5695** | +0.0119 |
**How to read this.** All five deltas are positive, but each is between 0.3Ο and 1.5Ο of its
own standard error, so the defensible claim is **"no regression in general capability"**, not
"the fine-tune made the model smarter". The movement is also not an SSA effect: a control run
of the *dense* `Qwen3-0.6B-Base` fine-tuned with the identical data and recipe lands within
Β±0.005 of BaiHu-V1-Flash on every one of these tasks (arc_easy 0.5951, arc_challenge 0.3908,
hellaswag 0.5511, piqa 0.7111, winogrande 0.6014). What this release did buy is the transfer
behaviour of Β§4.1βΒ§4.2 β a 63% perplexity drop on held-out dialogues and no degenerate
generations.
Caveats, stated plainly:
- These are **English** benchmarks. This harness build ships no C-Eval / CMMLU / C3 tasks, so
Chinese capability is not measured above; the held-out split of Β§4.1 (which contains both
languages) is the only Chinese-side evidence in this card.
- The Hub checkpoint stores float32 weights and the harness ran it at `bfloat16` (see Β§5.2).
The two agree to within 0.002 on every task listed, so precision is not driving the table.
### 4.4 Inference cost and speed (RTX 4090D, bfloat16)
Measured with the project's own `scripts/bench_resources.py` on the card that trained the model
(RTX 4090D 24 GB, no other process on it), bfloat16, `torch.no_grad()`, greedy decoding of 64
tokens after a prefill of 1,024 / 2,048 / 4,096 tokens, identical script for both models.
**The checkpoint exactly as shipped** (`ssa_force_full_window: true` β see below), 1,024-token
prefill:
| Metric | Qwen3-0.6B-Base | BaiHu-V1-Flash |
|---|---|---|
| Parameters (M) | 596.0 | 598.8 |
| Peak memory, prefill (GB) | 1.63 | **1.52** |
| Peak memory, generate (GB) | 1.78 | 2.01 |
| Prefill latency = TTFT (s) | 0.025 | 1.48 |
| TPOT β time per output token (ms) | 17.3 | 68.4 |
| Decode throughput (tok/s) | 57.8 | 14.6 |
| Attention FLOPs per token (GFLOPs) | 0.118 | 0.202 |
| Attention keys read per query (vs full attention) | 1.00Γ | 1.72Γ |
| GPU utilization mean (%) / power mean (W) | 15.5 / 69.8 | 20.0 / 69.0 |
How both quantities scale with context (same run, every cell in the order *base β ours*):
| Prefill | TTFT (ms) | TPOT (ms) | Decode (tok/s) | Keys read per query | Generate peak (GB) |
|---|---|---|---|---|---|
| 1,024 | 25 β 1,477 | 17.3 β 68.4 | 57.8 β 14.6 | 1.00Γ β 1.72Γ | 1.78 β 2.01 |
| 2,048 | 39 β 2,113 | 19.8 β 78.7 | 50.4 β 12.7 | 1.00Γ β 1.45Γ | 2.35 β 2.82 |
| 4,096 | 72 β 3,610 | 21.4 β 108.8 | 46.7 β 9.2 | 1.00Γ β 1.24Γ | 3.53 β 4.43 |
Stated plainly:
- **There is no speed advantage at any length tested.** Prefill (TTFT) is 50β59Γ slower in
wall-clock β 1.48 s vs 25 ms at 1 K β decode is 3.9Γ slower at 1 K and 5.1Γ at 4 K, and peak
memory is comparable (slightly *lower* for this model during prefill, slightly higher during
generation).
- **The sparse path is not actually saving anything here.** This checkpoint is trained *and
released* with `ssa_force_full_window: true`: the dense local window always covers the entire
causal prefix, so the shared and sparse paths are **additive on top of full attention** instead
of replacing part of it. Hence keys read per query above 1.00Γ.
- **Read the 1.72Γ honestly.** It is the measured cost of the shipped configuration, and it is
also why the model spends more attention FLOPs per token than the dense base (0.202 vs 0.118
GFLOPs at 1 K) while still being far slower in wall-clock. Sparsity only starts to bite once
`top_k Γ block_size` is small relative to the context (see the next point).
- **Turning the window shrinkage on is a flag, not a retrain** β but it was never validated in
that mode, because the weights were trained with the window forced open. For reference, with
`ssa_force_full_window=false` the same weights read **0.90Γ / 0.54Γ / 0.29Γ** as many keys at
1 K / 2 K / 4 K and decode at 20.7 / 18.5 / 14.8 tok/s. Even then it stays **2.5β3.2Γ slower**
than the dense base, and quality in that mode is **unevaluated** β treat the numbers as the
architecture's ceiling on this implementation, not as a free win.
- What follows from this is an implementation item, not an architecture one: the per-block Python
loop in the SSA layer issues many small kernels, so launch overhead dominates the arithmetic
saved (GPU utilization never exceeds ~28%). The sparse path has to be fused before any of this
can pay off on real hardware.
### 4.5 Effect of the P0 revision
Measured with the same data, recipe and seed, P0 vs the old single-vector shared branch:
| | old shared branch | P0 (per-block) |
|---|---|---|
| Held-out loss | 1.5376 | 1.5376 |
| Shared gate after SFT | 0.01001 | 0.01001 |
Under this regime (short dialogues, full causal window in the local path) the shared branch
carries almost no load, so the revision is quality-neutral and the gate still does not grow.
P0's intended benefit is long-range: it should only show up once the local window is allowed
to shrink / contexts are far longer than the 192-token window. That experiment has **not**
been run yet β see Limitations.
## 5. How to Run Inference
### 5.1 This is a custom architecture
`model_type: baihu_ssa` is **not** in the Transformers registry; a plain
`AutoModelForCausalLM.from_pretrained(...)` fails. Register the config/model classes first
(three lines below). Also note the model **does not** use `GenerationMixin` β call its own
`generate()` (greedy or temperature/top-k; no beam search).
The implementation lives in the project repository (not on the Hub). **This checkpoint
requires two revisions** of that code: the P0 revision of `modeling_baihu_ssa.py` (an older
copy loads without error but computes a different shared branch), and the dtype-aware loader
in `model_baihu_ssa.py` β before it, `from_pretrained(..., dtype=...)` was silently ignored
and the model always came back float32.
### 5.2 Minimal working example
```python
import os
import sys
import torch
from transformers import AutoConfig, AutoModelForCausalLM, AutoTokenizer
sys.path.insert(0, os.path.join("ssa_model", "src")) # path to the cloned repo's src/
from baihu_ssa.configuration_baihu_ssa import BaiHuSSAConfig
from baihu_ssa.model_baihu_ssa import BaiHuSSAForCausalLM
# ---- register the custom architecture (REQUIRED) ----
AutoConfig.register("baihu_ssa", BaiHuSSAConfig, exist_ok=True)
AutoModelForCausalLM.register(BaiHuSSAConfig, BaiHuSSAForCausalLM, exist_ok=True)
REPO = "ZichenAI/BaiHu-V1-Flash"
device = "cuda" if torch.cuda.is_available() else "cpu"
dtype = torch.bfloat16 if device == "cuda" else torch.float32
tok = AutoTokenizer.from_pretrained(REPO)
model = AutoModelForCausalLM.from_pretrained(REPO, dtype=dtype).to(device).eval()
messages = [
{"role": "user", "content": "What is the rule behind this sequence? 1, 4, 9, 16, 25"},
{"role": "assistant", "content": "The gaps between consecutive terms are 3, 5, 7, 9 "
"(+2 each time), so the n-th term is n squared."},
{"role": "user", "content": "Using that same rule, what comes after 36 and 49?"},
]
ids = tok.apply_chat_template(messages, add_generation_prompt=True, tokenize=True,
enable_thinking=False)
ids = ids["input_ids"] if not isinstance(ids, list) else ids # transformers 5.x returns a dict here
with torch.no_grad():
out = model.generate(torch.tensor([ids], device=device), max_new_tokens=64,
do_sample=False, eos_token_id=tok.convert_tokens_to_ids("<|im_end|>"))
text = tok.decode(out[0, len(ids):], skip_special_tokens=True)
if "<think>" in text: # non-thinking mode emits an empty think block
text = text.split("</think>")[-1]
print(text.strip())
```
Training used `add_generation_prompt=True, enable_thinking=False` (the generation prompt then
ends with `<|im_start|>assistant\n<think>\n\n</think>\n\n`). Keep that format for best results.
The Hub checkpoint stores **float32** weights (2.4 GB). Passing `dtype=torch.bfloat16`, as
above, loads them at **1.20 GB** and leaves the (float32) rotary tables untouched; omitting
`dtype` gives the 2.40 GB float32 model. Both score identically on the benchmarks in Β§4.3.
### 5.3 Troubleshooting
| Error | Cause | Fix |
|---|---|---|
| `does not recognize this architecture` | custom architecture not registered | `AutoConfig.register` + `AutoModelForCausalLM.register` as in Β§5.2 |
| output is repetitive garbage | wrong chat format (e.g. a raw prompt with no ChatML wrapper) | render with the tokenizer's chat template, `enable_thinking=False` |
| `CUDA error: no kernel image is available` | PyTorch without kernels for an old GPU (Maxwell, sm_52) | pin torch 2.7.1+cu126 (2.8 dropped sm_50/sm_60) |
## 6. Known Limitations
1. **0.6B scale.** Multi-step arithmetic is still unreliable; the SFT teaches the *behaviour*
(reuse the earlier rule/format, answer directly), not new reasoning ability.
2. **The shared branch is still barely used** (gate β 0.0101 after SFT, same as the old
revision). Whether P0 pays off can only be decided in a long-context / window-shrink regime.
3. **No inference speedup β in fact a slowdown.** As shipped, the model is 3.9β5.1Γ slower to
decode and ~50Γ slower to prefill than the dense base, and reads *more* attention keys,
because the released configuration forces the dense window open (see Β§4.4). Use it to study
the architecture, not to serve traffic.
4. **Trained on synthetic dialogues.** The 1,294 conversations are model-generated; style and
coverage are limited to the six transfer kinds and the topics they cover. Evaluation above
is on a held-out slice of the same distribution β not a general capability claim.
5. **Custom architecture, no llama.cpp/GGUF support** (SSA attention is not implemented there).
6. **Not an instruct model at large.** It is a 0.6B research model; expect terse answers and
occasional arithmetic slips.
## 7. License
Custom license (`LICENSE.custom.md` in this repository):
- **Personal / non-commercial use: free**, including running, modifying and publicly
distributing derivative models under the same license.
- **Commercial use requires a paid license** β contact **novaweb6868@outlook.com**.
- Metadata: `license: other`, tag `commercial-license-required`.
## 8. Citation
```bibtex
@misc{baihu_v1_flash,
title = {BaiHu-V1-Flash: an SSA (sparse-attention + SubQ) retrofit of Qwen3-0.6B-Base, fine-tuned on bilingual transfer dialogues},
author = {ZichenAI},
year = {2026},
url = {https://huggingface.co/ZichenAI/BaiHu-V1-Flash}
}
```
|