File size: 14,105 Bytes
dacd8a1 e8ca8bd f0e7465 e8ca8bd 0d72ea4 91fdc3c e8ca8bd 0d72ea4 91fdc3c e8ca8bd d37f5a3 e8ca8bd 0d72ea4 91fdc3c 0d72ea4 e8ca8bd 91fdc3c e8ca8bd 0d72ea4 91fdc3c e8ca8bd 91fdc3c 0d72ea4 e8ca8bd 91fdc3c e8ca8bd 0d72ea4 91fdc3c e8ca8bd 91fdc3c e8ca8bd 0d72ea4 91fdc3c e8ca8bd 0d72ea4 e8ca8bd 91fdc3c | 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 | ---
license: apache-2.0
base_model:
- BlinkDL/rwkv7-g1
- allenai/Olmo-3-1025-7B
---
# RWKV-7 "g1d" 0.1B — HuggingFace port (OLMo tokenizer)
# If it keeps falling back, check the cache folder first!!!!!!!!!!!!!!!!!!!!Basically, download the 'cuda' folder and place it inside the 'cache' folder.
# If it keeps falling back, check the cache folder first!!!!!!!!!!!!!!!!!!!!Basically, download the 'cuda' folder and place it inside the 'cache' folder.
# If it keeps falling back, check the cache folder first!!!!!!!!!!!!!!!!!!!!Basically, download the 'cuda' folder and place it inside the 'cache' folder.
# If it keeps falling back, check the cache folder first!!!!!!!!!!!!!!!!!!!!Basically, download the 'cuda' folder and place it inside the 'cache' folder.
# If it keeps falling back, check the cache folder first!!!!!!!!!!!!!!!!!!!!Basically, download the 'cuda' folder and place it inside the 'cache' folder.
# If it keeps falling back, check the cache folder first!!!!!!!!!!!!!!!!!!!!Basically, download the 'cuda' folder and place it inside the 'cache' folder.
# If it keeps falling back, check the cache folder first!!!!!!!!!!!!!!!!!!!!Basically, download the 'cuda' folder and place it inside the 'cache' folder.
# If it keeps falling back, check the cache folder first!!!!!!!!!!!!!!!!!!!!Basically, download the 'cuda' folder and place it inside the 'cache' folder.
A `trust_remote_code=True` HuggingFace wrapper around the BlinkDL
**RWKV-7 "Goose" g1d 0.1B** checkpoint
(`rwkv7-g1d-0.1b-20260129-ctx8192.pth`), re-headed for the **OLMo tokenizer**.
- The 12 transformer-style RWKV-7 blocks keep their **pretrained** weights.
- The **embedding** and **lm-head** are **re-initialized** (from RWKV's 65536-token
vocab to OLMo's 100278-token vocab), so they are *untrained* and need fine-tuning.
- Time-mixing runs on a **fused CUDA kernel** (forward **and** backward) when a GPU
+ CUDA toolchain are available, and transparently **falls back to pure PyTorch**
otherwise.
- `generate()` runs in **RNN mode with a recurrent state cache** — after the prompt
is absorbed into the state, each new token is a single-token forward instead of a
full-sequence recompute. Stateful inference runs on a second, **forward-only
"wkv7s" CUDA kernel** (state in/out), so prefill and decode are kernel-speed too.
> Because emb/head are freshly initialized, `generate()` produces gibberish until
> you fine-tune on OLMo-tokenized data. The model *body* is pretrained; only the
> vocabulary projection is new.
---
## Files
| File | Purpose |
|------|---------|
| `config.json` | Serialized `RWKV7Config` (dims + `auto_map` to the remote-code classes). |
| `configuration_rwkv7.py` | `RWKV7Config` — all architecture hyperparameters. |
| `modeling_rwkv7.py` | Model code: CUDA-kernel dispatch + PyTorch fallback, recurrent state cache for generation, `RWKV7Model`, `RWKV7ForCausalLM`. |
| `cuda/wkv7_cuda.cu`, `cuda/wkv7_op.cpp` | The fused bf16 "wind_backstepping" RWKV-7 kernel (forward + backward), copied from `RWKV-v7/train_temp/cuda/`. Used on the stateless (training) path. |
| `cuda/wkv7s.cu`, `cuda/wkv7s_op.cpp` | The stateful, forward-only "wkv7s" inference kernel (fp32 state in/out, arbitrary `T`, no chunk padding), adapted from RWKV-LM's inference kernel — patched here for **bf16** (upstream typedef was fp16) and **B > 1** (batch-aware state indexing). Used on the stateful (generation) path. |
| `model.safetensors` | Converted weights (bf16, ~244M params). |
| `tokenizer.json`, `tokenizer_config.json` | OLMo tokenizer (vocab 100278, GPT2-style BPE). |
| `generation_config.json` | Default generation settings (eos/pad ids). |
| `convert.py` | Reproduces `model.safetensors` from the original `.pth`. |
| `verify.py` | End-to-end smoke test (load / forward / parity / backward / generate). |
## Architecture (from `config.json`)
| field | value | meaning |
|-------|-------|---------|
| `num_hidden_layers` | 12 | RWKV-7 blocks |
| `hidden_size` | 768 | embedding dim `C` |
| `head_size` | 64 | → 12 heads (`H = C / head_size`) |
| `intermediate_size` | 3072 | channel-mix (FFN) hidden |
| `decay_lora` / `aaa_lora` / `mv_lora` / `gate_lora` | 64 / 64 / 32 / 128 | LoRA ranks for `w` / `a` / `v` / `g` |
| `vocab_size` | 100278 | OLMo tokenizer size (re-initialized emb/head) |
| `chunk_len` | 16 | CUDA kernel chunk length; sequence is padded to a multiple of this |
| `use_cuda_kernel` | true | prefer the fused kernel when possible |
---
## Usage
```python
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
PATH = "/workspace/rwkv7-g1d-olmo"
tok = AutoTokenizer.from_pretrained(PATH, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
PATH, trust_remote_code=True, dtype=torch.bfloat16
).cuda().eval()
ids = tok("The Eiffel tower is in the city of", return_tensors="pt").input_ids.cuda()
with torch.no_grad():
logits = model(ids).logits # (1, T, 100278)
# training / backward
model.train
#🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨labels[labels == tokenizer.pad_token_id] = -100🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
out = model(ids, labels=ids) # shifted cross-entropy loss
out.loss.backward() # gradients flow through the CUDA kernel
```
### Forcing the PyTorch fallback
Set the flag on the config (useful on CPU, non-bf16, or to debug the kernel):
```python
model.config.use_cuda_kernel = False # every RWKV-7 op now runs in pure PyTorch
```
The kernel is also skipped automatically when: no CUDA device, input dtype is not
bfloat16, or the kernel fails to compile — in all cases the fallback is used and a
one-line notice is printed.
### Stateful generation (RNN mode)
`generate()` uses the state cache automatically — nothing to configure:
```python
out_ids = model.generate(ids, max_new_tokens=64, do_sample=False)
```
You can also drive the state manually (streaming, chunked prefill, state
save/reuse, "infinite context" style loops):
```python
with torch.no_grad():
out = model(ids, use_cache=True) # prefill: prompt -> state
next_tok = out.logits[:, -1].argmax(-1, keepdim=True)
out = model(next_tok, state=out.state) # passing `state` implies use_cache
```
`outputs.state` is a list of per-layer tuples `(att_x_prev, wkv_state, ffn_x_prev)`:
| tensor | shape | dtype | role |
|--------|-------|-------|------|
| `att_x_prev` | `(B, C)` | input dtype | last post-`ln1` token, time-shift for the time-mix |
| `wkv_state` | `(B, H, N, N)` | fp32 | the WKV matrix state |
| `ffn_x_prev` | `(B, C)` | input dtype | last post-`ln2` token, time-shift for the channel-mix |
States are ordinary tensors — you can `clone()` them to snapshot/branch a
context, or persist them to disk and resume later.
> **Which implementation runs when?** Stateful calls under `torch.no_grad()`
> (i.e. normal generation) use the forward-only **wkv7s** kernel — one kernel
> launch per forward, any `T`, no chunk padding — so both long-prompt prefill
> and per-token decode are fast. wkv7s has **no backward**, so a stateful call
> with autograd enabled (e.g. TBPTT-style training that carries state) silently
> drops to the sequential PyTorch path instead. The kernel mutates its state
> buffer in place; the wrapper always clones the incoming state first, so a
> `state` you're holding on to is never corrupted (snapshot/branching stays safe).
Zero token-shift state + zero WKV state is exactly equivalent to the zero-padded
non-cached forward, so prefill→decode with the cache reproduces the full-recompute
logits.
---
## How the CUDA kernels + fallback work (`modeling_rwkv7.py`)
The time-mixing recurrence is dispatched by
`run_rwkv7(r, w, k, v, a, b, config, initial_state=None, output_final_state=False)`,
which returns `(y, final_state_or_None)`. Dispatch is three-way:
| call | implementation |
|------|----------------|
| stateless (training / plain forward), CUDA + bf16 | `wind_backstepping` (fused fwd + bwd) |
| stateful (cache), CUDA + bf16, **autograd off** | `wkv7s` (fwd-only, state in/out) |
| everything else (CPU, non-bf16, grad-enabled stateful, compile failure) | pure PyTorch |
1. **Training kernel** (`_rwkv7_cuda`, "wind_backstepping") — used when
`config.use_cuda_kernel` is set, the tensors are CUDA + bf16, the call is
**stateless** (`initial_state is None` and no final state requested), and
`_try_load_cuda_kernel()` succeeds. On first call it
JIT-compiles `cuda/wkv7_op.cpp` + `cuda/wkv7_cuda.cu` via
`torch.utils.cpp_extension.load` (flags `-D_C_=head_size`, `-D_CHUNK_LEN_=chunk_len`)
and registers the `wind_backstepping` op. Compilation happens once per process and
is cached; any failure is caught and flips the model to the fallback.
- `_WindBackstepping` is a `torch.autograd.Function`:
- **forward** → `wind_backstepping.forward` (produces `y` plus the saved state
`s` and `sa` needed for backprop).
- **backward** → `wind_backstepping.backward` (returns gradients for all six
inputs `w, q, k, v, z, b`), so training works end-to-end on the kernel.
- The sequence length is padded to a multiple of `chunk_len` (16) and sliced back.
2. **Inference kernel** (`_rwkv7_cuda_stateful`, "wkv7s") — used for **stateful**
calls (an incoming state and/or `output_final_state=True`) when the tensors are
CUDA + bf16 **and autograd is disabled** (`torch.no_grad()` — the kernel has no
backward). JIT-compiled from `cuda/wkv7s_op.cpp` + `cuda/wkv7s.cu`
(flag `-D_N_=head_size`), registered as the `wkv7s` op.
- The time loop lives inside the kernel: a single launch processes any `T`
(no `chunk_len` padding), fully parallel over `B·H` blocks × `N` threads.
- The fp32 state `(B, H, N, N)` is read at the start and written back at the
end (in-place); the Python wrapper clones the incoming state so the caller's
tensor is never mutated.
- Patched relative to the upstream RWKV-LM inference kernel: `bf16` typedef
was actually `at::Half` (fp16) upstream → switched to `at::BFloat16`, and
the state indexing was `B = 1`-only → made batch-aware.
3. **Fallback path** (`_rwkv7_pytorch`) — a plain sequential-over-time
implementation of the same recurrence
`state = state*exp(-exp(w)) + state·aᵀ·b + vᵀ·k`, `y = state·r`, in fp32.
It is fully differentiable through ordinary autograd (no custom backward needed),
accepts an `initial_state`, and can return the final state — it covers every
case the kernels can't (CPU, non-bf16, grad-enabled stateful calls).
All three paths take the **raw (pre-exp) decay `w`** and apply `exp(-exp(w))`
internally, and implement the identical per-head recurrence
(`s[i,j] = s[i,j]·w_j + v_i·k_j + (Σⱼa_j·s[i,j])·b_j`), so they are numerically
interchangeable. `verify.py` confirms kernel-vs-fallback
parity (identical bf16 logits and top-1 prediction).
### Model structure
`RWKV7ForCausalLM` → `.rwkv` (`RWKV7Model`) + `.head` (lm-head). `RWKV7Model` holds
`emb`, `blocks[0..11]` (`RWKV7Block` = `ln1` + `att` time-mix, `ln2` + `ffn`
channel-mix; block 0 also has `ln0`), and `ln_out`. State-dict keys mirror the
original RWKV layout under the `rwkv.` prefix (e.g. `rwkv.blocks.0.att.receptance.weight`).
Generation runs in **RNN mode**: the model carries a per-layer recurrent state
(see the state table above) through the `state=` kwarg / `outputs.state` field —
the same convention as `transformers`' Rwkv and Mamba models, so
`GenerationMixin` propagates it between steps automatically (`state` is in
`ALL_CACHE_NAMES`; a `_update_model_kwargs_for_generation` override covers older
versions). `prepare_inputs_for_generation` feeds only the last token once a state
exists. Passing `state=` implies `use_cache=True`; gradient checkpointing forces
it off during training. Stateful forwards run on the wkv7s kernel under
`no_grad`, PyTorch otherwise.
---
## Reproducing the conversion (`convert.py`)
```bash
# 1) download the original checkpoint (already done in /workspace)
wget https://huggingface.co/BlinkDL/rwkv7-g1/resolve/main/rwkv7-g1d-0.1b-20260129-ctx8192.pth \
-O /workspace/rwkv7-g1d-0.1b-20260129-ctx8192.pth
# 2) convert -> /workspace/rwkv7-g1d-olmo
python convert.py
```
What `convert.py` does:
1. Loads the OLMo tokenizer to read the target vocab size (100278).
2. Loads the `.pth` and infers all dims from the tensor shapes → builds `RWKV7Config`.
3. Remaps the original RWKV keys to the HF module layout (`rwkv.` prefix), **dropping**
`emb.weight` / `head.weight`, and loads them with `strict=False`
(asserts there are **no** unexpected or unmatched keys besides emb/head).
4. **Re-initializes** the embedding (`uniform(±1e-4)`) and head
(`orthogonal`, gain `0.5·√(vocab/hidden)`) for the new vocabulary — RWKV's own
init scheme.
5. Saves weights (`safetensors`), config, tokenizer, and copies the remote-code files.
## Verifying (`verify.py`)
```bash
python verify.py
```
Checks: load via `AutoModelForCausalLM(trust_remote_code=True)`, CUDA-kernel forward,
kernel-vs-fallback parity, cached-vs-uncached parity (prefill + stateful decode vs
full recompute), a backward pass (gradients on emb / attention / decay-LoRA), and a
short greedy `generate()` through the state cache.
> Note: 399/402 parameters receive gradients — the 3 without are
> `blocks.0.att.v0/v1/v2` (the value-residual params are unused in layer 0 by design).
---
## Requirements
- PyTorch with CUDA (bf16-capable GPU; tested on RTX 3060 / CUDA 13.0) for the kernel;
CPU/other works via the fallback.
- `transformers >= 5`, `safetensors`.
- A working CUDA toolchain (`nvcc`) for first-call kernel JIT compilation; if absent,
the model still runs on the PyTorch fallback.
## Credits
RWKV-7 architecture and the original checkpoint/kernels by **BlinkDL** —
<https://github.com/BlinkDL/RWKV-LM>. Training kernel copied from
`RWKV-v7/train_temp/cuda/`; the stateful `wkv7s` inference kernel adapted from
RWKV-LM's inference code (patched here for bf16 + batched state). |