| --- |
| 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). |