File size: 12,776 Bytes
62c0c19
 
732428e
 
 
 
 
 
44a68e3
732428e
 
 
 
 
 
 
 
 
62c0c19
732428e
 
 
 
 
 
 
44a68e3
732428e
 
 
44a68e3
 
 
732428e
 
 
 
 
 
 
 
 
 
 
44a68e3
 
f2c497b
 
44a68e3
 
 
 
 
 
f2c497b
 
44a68e3
f2c497b
732428e
44a68e3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
732428e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44a68e3
732428e
 
 
 
 
 
 
44a68e3
f2c497b
 
 
732428e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f2c497b
 
 
 
732428e
 
 
 
 
 
 
 
 
44a68e3
 
 
732428e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f2c497b
 
 
 
 
732428e
 
 
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
---
license: apache-2.0
pipeline_tag: text-generation
language:
  - ja
  - en
  - zh
base_model:
  - RWKV/RWKV7-G1l-2.9B-20261031
  - RWKV/RWKV7-G1l-7.2B-20261031
  - RWKV/RWKV7-G1l-13.3B-20261031
tags:
  - rwkv
  - rwkv7
  - speculative-decoding
  - multi-token-prediction
  - mtp
  - draft-model
---

# RWKV7-MTP-Exp: MTP draft sidecars for RWKV-7 G1l

**Experimental** multi-token-prediction (MTP) drafters for speculative decoding with RWKV-7 G1l models.

- **What a sidecar is:** a small add-on network that reads the frozen base model's hidden states and proposes the next few tokens. The base model then verifies all of them in a single forward pass.
- **Quality:** the output distribution is exactly the base model's. The sidecar changes only speed.
- **Speed:** with [RWKV-Infer-Next](#usage-with-rwkv-infer-next), single-stream decoding becomes **1.8–2.3Γ— faster**. That includes the 13.3B model on a single 16 GB GPU.

| Folder | Base model | Base layers / width | Tapped layers | Training tokens | Size (bf16) |
|---|---|---|---|---|---|
| `RWKV7-G1l-2.9B-20261031/` | [RWKV/RWKV7-G1l-2.9B-20261031](https://huggingface.co/RWKV/RWKV7-G1l-2.9B-20261031) | 32 / 2560 | 9, 19 | 1.0B | 208 MB (103.6M params) |
| `RWKV7-G1l-7.2B-20261031/` | [RWKV/RWKV7-G1l-7.2B-20261031](https://huggingface.co/RWKV/RWKV7-G1l-7.2B-20261031) | 32 / 4096 | 9, 19 | 1.0B | 496 MB (247.6M params) |
| `RWKV7-G1l-13.3B-20261031/` | [RWKV/RWKV7-G1l-13.3B-20261031](https://huggingface.co/RWKV/RWKV7-G1l-13.3B-20261031) | 61 / 4096 | 17, 36 | 1.0B | 496 MB (247.6M params) |

> A sidecar works **only with the base model it was trained on**. Same width is not enough: a sidecar trained for the 7.2B
> model loads on the 13.3B model but gives no speed-up (1.03 tokens per forward). `mtp.json` records the base model,
> and RWKV-Infer-Next refuses a mismatch.

## Measured speed

Setup:
- Engine: RWKV-Infer-Next, int6 weights.
- Workload: single stream, 6 chat prompts (Japanese chat, proofreading, translation, English chat, code, Chinese), greedy and sampled.

Speeds are the mean over the 6 prompts, greedy / sampled.

| Base model | GPUs | GPU memory used | Without MTP | With this sidecar | Speed-up |
|---|---|---|---|---|---|
| G1l 2.9B | **one 16 GB GPU** | 5.1 GB | 240 / 230 tok/s | **436 / 443 tok/s** | **1.8–1.9Γ—** |
| G1l 2.9B | two 16 GB GPUs (tensor parallel) | β‰ˆ 4 GB each | 205 / 204 tok/s | **403 / 382 tok/s** | **1.9–2.0Γ—** |
| G1l 7.2B | **one 16 GB GPU** | 9.4 GB | 119 / 116 tok/s | **243 / 249 tok/s** | **2.0–2.1Γ—** |
| G1l 7.2B | two 16 GB GPUs (tensor parallel) | β‰ˆ 6 GB each | 141 / 140 tok/s | **269 / 275 tok/s** | **1.9–2.0Γ—** |
| G1l 13.3B | **one 16 GB GPU** (`--max-batch 16`) | 14.6 GB | 64 / 63 tok/s | **143 / 143 tok/s** | **2.2–2.3Γ—** |
| G1l 13.3B | two 16 GB GPUs (tensor parallel) | β‰ˆ 9.5 GB each | 78 / 76 tok/s | **162 / 162 tok/s** | **2.1Γ—** |

- **The 13.3B model with its sidecar fits one 16 GB GPU** in int6. Cap the concurrent sequences at 16 (`--max-batch 16`); the default of 32 reserves recurrent state and CUDA graphs that do not fit.
- **The 2.9B model is faster on one GPU** than split over two, with or without MTP.
- The two-GPU rows are PCIe GPUs without P2P.

- **By task** (greedy, one GPU): acceptance is highest on proofreading and code, and lowest on English chat and Chinese.

  | Base model | Proofreading | Code | Japanese chat | Translation | Chinese | English chat |
  |---|---|---|---|---|---|---|
  | 2.9B | 559 | 498 | 426 | 407 | 402 | 323 tok/s |
  | 7.2B | 268 | 239 | 256 | 253 | 231 | 211 tok/s |
  | 13.3B | 171 | 156 | 142 | 154 | 117 | 115 tok/s |

- **Training length:** speed-ups grow with training. The 1.0B-token sidecars replace the earlier ones. Measured in the same session on one GPU (greedy / sampled):

  | Base model | Earlier sidecar | This sidecar (1.0B tokens) | Gain |
  |---|---|---|---|
  | 7.2B | 300M tokens: 226 / 223 tok/s | 243 / 249 tok/s | +7–12% |
  | 13.3B | 270M tokens: 136 / 133 tok/s | 143 / 143 tok/s | +5–7% |

  Earlier checkpoints: 7.2B gave 1.4Γ— after 25M tokens; 13.3B gave 1.5Γ— after 30M and 1.7Γ— after 90M.

## Architecture

Each sidecar is **one transformer-style block**, applied K times per decoding step. The block is gated sliding-window attention followed by an RWKV-7 channel-mix. At every step it reads:
- the base model's last hidden state;
- two of the base model's intermediate residual streams (EAGLE-3-style fusion);
- the embedding of the token it is conditioned on.

```
                    base model (RWKV-7, frozen)
 token ─► emb ─► ln0 ─┬─► layer 0 … layer L-1 ─► ln_out ─► h_j ───────────► head ─► p(token j+1)
                      β”‚      β”‚ outputs of layers f1, f2
                      β”‚      β–Ό
                      β”‚    m1_j, m2_j
                      └─► e_t = ln0(emb(t))               (shared with the base model)

 drafter (this repository): one block, depth k = 1..K drafts the token k positions ahead
   depth 1 : u = fproj Β· [ LN(h_j) ; LN(m1_j) ; LN(m2_j) ]          mid-layer fusion
   depth k : u = LN_d(o_{k-1})                                      its own previous output (depth adapter)
   x  = LN( proj Β· [ u ; LN(e_{j+k}) ] + dep[k-1] )
   x  = x + GatedSWA( LN(x) )        16 query / 4 KV heads Γ— 128, q/k RMSNorm, partial RoPE, window 512
   x  = x + ChannelMix( LN(x) )      RWKV-7 channel-mix: token shift, reluΒ², 4Γ— hidden
   o_k = LN(x) ─► base model's head ─► draft token k
```

| Component | Definition |
|---|---|
| Token mixer | **Gated sliding-window attention**: grouped-query attention with 16 query heads and 4 KV heads of dim 128 (2048-wide), per-head RMSNorm on q and k, rotate-half RoPE on the first 32 dims (ΞΈ = 1e6), sigmoid output gate (`o = W_o (attn βŠ™ Οƒ(W_g x))`), window of 512 keys |
| Channel mixer | RWKV-7 channel-mix: `x + (x_prev βˆ’ x) βŠ™ x_k` token shift, `W_v Β· relu(W_k Β·)Β²`, hidden size 4C |
| Depth-1 input | the base model's final hidden state `h` (after `ln_out`), fused with the **output** residual streams of two base layers through `fproj` (3C β†’ C) |
| Deeper inputs | the drafter's own previous output through a separate LayerNorm, plus a learned per-depth vector (`dep`) |
| Embedding and head | the base model's `ln0(emb)` and output head, shared and frozen (not stored here) |
| Draft vocabulary | `draft_vocab`: token ids by training frequency. Drafting over the top 16384 rows covers about 97% of the token stream and makes the draft head 4Γ— cheaper |
| Normalisation | LayerNorm (eps 1e-5) everywhere except q/k RMSNorm (eps 1e-6); no biases |

**Why gated SWA attention rather than an RWKV block?** A preliminary screen trained drafters with the same recipe and compared depth-1 greedy acceptance:

| Drafter layer | chat | text |
|---|---|---|
| RWKV time-mix | 54.6% | 55.8% |
| attention | 64.1% | 74.9% |

The attention layer drafts much better, and the sliding window keeps its state bounded. The state is a KV ring of 512 + K entries per sequence, so the system keeps RWKV's constant-memory property. Rolling back a rejected draft costs nothing.

**Inference.** One speculative step:
1. Draft d1…dK by chaining the block.
2. The base model processes `[t, d1 … dK]` in one forward.
3. The sampler picks a token at every position from the **base model's** distribution.
4. The drafts are accepted up to the first mismatch.
5. The base model's recurrent state is rolled back to the accepted prefix.

Every emitted token is a sample from the base model given the accepted prefix, so the output distribution is the same as plain decoding, greedy or sampled.

## Training

| Item | Setting |
|---|---|
| Base model | frozen (bf16, or weight-only int8 where it did not fit) |
| Target | the base model's own next-token distribution (KL distillation), not the data tokens |
| Unrolling | 3 drafting depths per position, trained on the drafter's own outputs (training-time test); equal weights |
| Context | 512-token windows |
| Optimiser | AdamW, Ξ² = (0.9, 0.99), **no weight decay**, lr 1e-3 with 200 warm-up steps, gradient clip 1.0, fp32 master weights |
| Initialisation | the block starts as an identity (zero output projections); fusion starts as "final hidden only" |
| Data | a Japanese-weighted multilingual mix: Japanese / English / Chinese web and encyclopedic text, code, math, and chat and translation conversations. The 13.3B mix was β‰ˆ 49% Japanese (incl. translation), 30% English (incl. math), 12% Chinese, 9% code |

## Usage with RWKV-Infer-Next

[RWKV-Infer-Next](https://github.com/OpenMOSE/RWKV-Infer-Next) (an inference server for RWKV-7):
- tensor parallel, continuous batching on CUDA graphs, int6 / int8 / bf16 weights;
- OpenAI-compatible API, chat templates, thinking and tool calls, WebUI.

It downloads the base model and the matching sidecar from this repository automatically.

```bash
pip install -r requirements.txt
python -m rwkv_next --model RWKV/RWKV7-G1l-2.9B-20261031 --tp 1                    # one GPU (β‰ˆ 5 GB)
python -m rwkv_next --model RWKV/RWKV7-G1l-7.2B-20261031 --tp 1                    # one 16 GB GPU
python -m rwkv_next --model RWKV/RWKV7-G1l-13.3B-20261031 --tp 1 --max-batch 16    # one 16 GB GPU
python -m rwkv_next --model RWKV/RWKV7-G1l-13.3B-20261031 --tp 2                   # two GPUs
# WebUI:  http://localhost:8000/ui        API:  http://localhost:8000/v1/chat/completions
```

What happens at start:
1. The base model is downloaded once into the Hugging Face cache.
2. `RWKV_MTP_PATH=auto` (the default) looks for `mtp/` in the model folder. When there is none, it reads `index.json` from `RWKV_MTP_REPO` (default `OpenMOSE/RWKV7-MTP-Exp`) and downloads only the matching folder.
3. The status page shows the sidecar in use.

**Local models.** A local model folder named like the base model (e.g. `.../RWKV7-G1l-7.2B-20261031`) gets its sidecar from this repository the same way. Alternatively, place a folder from this repository in the model folder as `mtp/`:

```bash
huggingface-cli download OpenMOSE/RWKV7-MTP-Exp --include "RWKV7-G1l-7.2B-20261031/*" --local-dir /tmp/mtp
mv /tmp/mtp/RWKV7-G1l-7.2B-20261031 /path/to/RWKV7-G1l-7.2B-20261031/mtp
```

**Newer RWKV-7 7B / 13B releases** that have no sidecar of their own can borrow one with `--mtp-force` (`RWKV_MTP_FORCE=1`). It picks the sidecar of the same width with the closest layer count.
- The output is unchanged; only the speed depends on how close the new model is to the base model.
- A poor match can be slower than no MTP. Measured: the 7.2B sidecar forced onto the 13.3B model ran at 66 tok/s, against 78 without MTP.

**Settings:**
- `RWKV_MTP_PATH=off` disables MTP.
- `RWKV_MTP_REPO=` (empty) prevents any download.
- `RWKV_MTP_K` sets the drafts per step by batch size. The default `4:3,16:2,0:0` drafts 3 tokens for ≀ 4 concurrent streams, 2 for ≀ 16, and none beyond, where the GPU is already saturated.

## Files

```
index.json                              which folder serves which base model
RWKV7-G1l-2.9B-20261031/mtp.safetensors   drafter weights (bf16) + draft_vocab (int64)
RWKV7-G1l-2.9B-20261031/mtp.json          {"meta": {...}}
RWKV7-G1l-7.2B-20261031/...
RWKV7-G1l-13.3B-20261031/...
```

`mtp.json` meta:

| Key | Meaning |
|---|---|
| `format` / `version` | `rwkv-mtp` / `1` |
| `base_model`, `n_layer`, `n_embd` | the base model this sidecar belongs to |
| `fuse_layers` | the two tapped base layers (0-based; the layer's output stream) |
| `kind`, `depth`, `dd`, `ctx` | `attn`, training depth 3, depth adapter present, window 512 |
| `target`, `tokens` | `kl`, training tokens |

Tensor names follow the definition above (`lh`, `lf.{0,1}`, `fproj`, `lhd`, `dep`, `le`, `proj`, `blk.ln{0,1,2}`, `attn.{q,k,v,g,o}`,
`attn.{q,k}_norm`, `blk.ffn.{x_k,key,value}`, `lo`).

## Limitations

- **Experimental.** The architecture is fixed (format v1), but these checkpoints may be replaced by longer-trained ones.
- **Base model.** Each sidecar is tied to its base model revision (`RWKV7-G1l-*-20261031`).
- **Where the speed-up is large:** for few concurrent streams, where decoding is memory-bound. Large batches gain little and are left to plain decoding by default.
- **Where acceptance is lower:** English chat and Chinese.
- **Exact output for a given seed:** a K+1-token verify rounds differently from a 1-token step in bf16 and int6, so greedy outputs can differ from plain decoding at near-ties. The distribution is unchanged.

## Acknowledgements

This model was made possible through the computational resources and technical support provided by
**Recursal AI** and **Featherless AI**. We would like to express our deepest gratitude.

## License

Apache-2.0, the same as the base models.