Hakureirm commited on
Commit
16136bb
·
verified ·
1 Parent(s): 70479b0

Retire the optional kernels, carry the tokenizer, drop dead config keys

Browse files
added_tokens.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ {
2
+ "<|rwkv_tokenizer_end_of_text|>": 0
3
+ }
config.json CHANGED
@@ -9,13 +9,10 @@
9
  },
10
  "bos_token_id": 0,
11
  "decay_low_rank_dim": 64,
12
- "deep_embed_size": 768,
13
  "dtype": "bfloat16",
14
  "eos_token_id": 0,
15
  "gate_low_rank_dim": 128,
16
  "head_dim": 64,
17
- "hidden_act": "sqrelu",
18
- "hidden_ratio": 4.0,
19
  "hidden_size": 768,
20
  "intermediate_size": 3072,
21
  "max_position_embeddings": 4096,
@@ -25,7 +22,6 @@
25
  "num_heads": 12,
26
  "num_hidden_layers": 12,
27
  "pad_token_id": 0,
28
- "sparse_channel_mix": false,
29
  "tie_word_embeddings": false,
30
  "transformers_version": "5.15.0.dev0",
31
  "use_cache": true,
 
9
  },
10
  "bos_token_id": 0,
11
  "decay_low_rank_dim": 64,
 
12
  "dtype": "bfloat16",
13
  "eos_token_id": 0,
14
  "gate_low_rank_dim": 128,
15
  "head_dim": 64,
 
 
16
  "hidden_size": 768,
17
  "intermediate_size": 3072,
18
  "max_position_embeddings": 4096,
 
22
  "num_heads": 12,
23
  "num_hidden_layers": 12,
24
  "pad_token_id": 0,
 
25
  "tie_word_embeddings": false,
26
  "transformers_version": "5.15.0.dev0",
27
  "use_cache": true,
configuration_rwkv7.py CHANGED
@@ -13,107 +13,88 @@
13
  # limitations under the License.
14
  """RWKV-7 (Goose) model configuration."""
15
 
 
 
16
  from transformers.configuration_utils import PreTrainedConfig
 
17
 
18
 
19
- class Rwkv7Config(PreTrainedConfig):
20
- r"""
21
  Configuration for [`Rwkv7Model`], an all-recurrent (attention-free) RWKV-7 "Goose"
22
  model. Instantiating with the defaults yields the ~0.1B RWKV-7 configuration.
23
 
24
  Parameter names follow the upstream RWKV reference implementation
25
  (`BlinkDL/RWKV-LM`) rather than a renamed variant, so converting a native
26
  `.pth` checkpoint is close to a rename-free copy.
27
-
28
- Args:
29
- vocab_size (`int`, *optional*, defaults to 65536):
30
- Vocabulary size (RWKV "world" tokenizer).
31
- hidden_size (`int`, *optional*, defaults to 768):
32
- Model width `C`.
33
- num_hidden_layers (`int`, *optional*, defaults to 12):
34
- Number of blocks.
35
- head_dim (`int`, *optional*, defaults to 64):
36
- Width of one WKV head. `hidden_size` must be divisible by it.
37
- num_heads (`int`, *optional*, defaults to 12):
38
- Number of WKV heads; must equal `hidden_size // head_dim`.
39
- decay_low_rank_dim (`int`, *optional*, defaults to 64):
40
- Rank of the decay (`w`) LoRA.
41
- a_low_rank_dim (`int`, *optional*, defaults to 64):
42
- Rank of the in-context-learning-rate (`a`) LoRA.
43
- v_low_rank_dim (`int`, *optional*, defaults to 32):
44
- Rank of the value-residual (`v`) LoRA. Unused on layer 0, which
45
- *produces* `v_first` instead of mixing towards it.
46
- gate_low_rank_dim (`int`, *optional*, defaults to 128):
47
- Rank of the output-gate (`g`) LoRA.
48
- intermediate_size (`int`, *optional*):
49
- Channel-mix inner width (`hidden_ratio * hidden_size` by convention).
50
- hidden_ratio (`float`, *optional*, defaults to 4.0):
51
- Used to derive `intermediate_size` when it is not given explicitly.
52
- hidden_act (`str`, *optional*, defaults to `"sqrelu"`):
53
- Channel-mix activation. RWKV-7 uses squared ReLU.
54
- norm_eps (`float`, *optional*, defaults to 1e-05):
55
- Epsilon of every LayerNorm/GroupNorm in the model.
56
- norm_bias (`bool`, *optional*, defaults to `True`):
57
- Whether the norms carry a bias.
58
- max_position_embeddings (`int`, *optional*, defaults to 8192):
59
- Training context length. RWKV is recurrent and not bounded by it at
60
- inference; it only sizes generation defaults.
61
- tie_word_embeddings (`bool`, *optional*, defaults to `False`):
62
- Whether to tie the input embedding and the LM head.
63
- use_cache (`bool`, *optional*, defaults to `True`):
64
- Whether to return the recurrent state.
65
- use_deep_embed (`bool`, *optional*, defaults to `False`):
66
- Enable the RWKV-8 "DeepEmbed" hook: a per-layer, per-token vector that
67
- channelwise-modulates the channel-mix. The table is deliberately NOT a
68
- model weight it is meant to live in RAM/SSD and be prefetched per
69
- token, which is the whole point of the design (VRAM savings) so it is
70
- passed to the forward as `deep_embeds` instead. No RWKV-7 checkpoint
71
- carries one; this is an extension point, off by default.
72
- deep_embed_size (`int`, *optional*):
73
- Width of one layer's DeepEmbed vector. `hidden_size` reproduces the
74
- reference "1x" variant (modulating the channel-mix output);
75
- `intermediate_size` reproduces "4x" (modulating its input). Defaults to
76
- `hidden_size` when `use_deep_embed` is set.
77
- wkv_state_dtype (`str`, *optional*, defaults to `"float32"`):
78
- Precision the recurrent WKV state is carried and accumulated in,
79
- independently of the activation dtype. The recurrence is unrolled over
80
- the whole sequence, so a narrow state drifts; `"float32"` with fp16
81
- activations is the combination the reference implementation uses.
82
- `"float16"`/`"bfloat16"` trade that for a smaller state.
83
- wkv_implementation (`str`, *optional*, defaults to `"eager"`):
84
- Which WKV recurrence to use, by name, from
85
- `models.rwkv7.modeling_rwkv7.RWKV7_WKV_FUNCTIONS`. `"eager"` is the
86
- portable PyTorch path the sequential step when decoding, the
87
- chunk-parallel form otherwise, and per-segment when a packed batch is
88
- passed. Register an entry in that mapping to plug in a fused or varlen
89
- kernel without forking the model.
90
- sparse_channel_mix (`bool`, *optional*, defaults to `False`):
91
- Skip the channel-mix value-projection rows whose input channel is zero.
92
- The activation is a squared ReLU, so its zeros are exact and skipping
93
- them is exact too; on a 7.2B checkpoint only about a tenth of the
94
- channels are nonzero -- 10.07% measured over 16 decode steps, 6.85%
95
- low and 11.99% high -- and that projection is a third of the
96
- model's bytes. Costs a
97
- transposed copy of the value weight (about +30% weights), built lazily,
98
- and only pays once launches are captured — see the model doc.
99
-
100
- Exact but not bit-reproducible: the surviving channels are summed across
101
- several partitions that combine through an atomic add, so the order the
102
- partitions arrive in — and therefore the last unit in the last place of
103
- each output — varies between runs of identical input. Same rounding
104
- class as a split-K GEMM. Greedy decoding can turn that into a different
105
- token at a near-tie, so leave this off when you need a run to reproduce
106
- itself exactly.
107
- bos_token_id (`int`, *optional*, defaults to 0):
108
- Beginning-of-sequence id. The RWKV world tokenizer has no dedicated BOS
109
- token and the reference implementation prepends nothing, so this exists to
110
- satisfy `GenerationMixin` rather than to be emitted.
111
- eos_token_id (`int`, *optional*, defaults to 0):
112
- End-of-sequence id, id 0 in the RWKV world vocabulary.
113
- pad_token_id (`int`, *optional*, defaults to 0):
114
- Padding id, the same id 0. Set deliberately rather than left `None`:
115
- `generate` needs one to pad a batch, and without it a batched call either
116
- raised or fell back to the eos id with a warning on every step.
117
 
118
  ```python
119
  >>> from transformers import Rwkv7Config, Rwkv7Model
@@ -126,81 +107,46 @@ class Rwkv7Config(PreTrainedConfig):
126
  model_type = "rwkv7"
127
  keys_to_ignore_at_inference = ["past_key_values"]
128
 
129
- def __init__(
130
- self,
131
- vocab_size=65536,
132
- hidden_size=768,
133
- num_hidden_layers=12,
134
- head_dim=64,
135
- num_heads=12,
136
- decay_low_rank_dim=64,
137
- a_low_rank_dim=64,
138
- v_low_rank_dim=32,
139
- gate_low_rank_dim=128,
140
- intermediate_size=None,
141
- hidden_ratio=4.0,
142
- hidden_act="sqrelu",
143
- norm_eps=1e-5,
144
- norm_bias=True,
145
- max_position_embeddings=8192,
146
- tie_word_embeddings=False,
147
- use_cache=True,
148
- use_deep_embed=False,
149
- deep_embed_size=None,
150
- wkv_state_dtype="float32",
151
- wkv_implementation="eager",
152
- sparse_channel_mix=False,
153
- bos_token_id=0,
154
- eos_token_id=0,
155
- pad_token_id=0,
156
- **kwargs,
157
- ):
158
- self.vocab_size = vocab_size
159
- self.hidden_size = hidden_size
160
- self.num_hidden_layers = num_hidden_layers
161
- self.head_dim = head_dim
162
- self.num_heads = num_heads
163
- self.decay_low_rank_dim = decay_low_rank_dim
164
- self.a_low_rank_dim = a_low_rank_dim
165
- self.v_low_rank_dim = v_low_rank_dim
166
- self.gate_low_rank_dim = gate_low_rank_dim
167
- self.hidden_ratio = hidden_ratio
168
- # `None` rather than a number, so `hidden_ratio` is what actually decides. It
169
- # used to default to 3072, which is exactly `768 * 4.0` -- correct for the
170
- # default `hidden_size` and silently wrong for every other one. A config built
171
- # as `Rwkv7Config(hidden_size=4096, num_heads=64)` came back with a channel-mix
172
- # four times narrower than the architecture it names, and `hidden_ratio` was
173
- # dead code that nothing could reach without passing `intermediate_size=None`
174
- # explicitly. Serialisation is unaffected: the resolved value is written to
175
- # `config.json` either way.
176
- self.intermediate_size = (
177
- intermediate_size if intermediate_size is not None else int(hidden_size * hidden_ratio)
178
- )
179
- self.hidden_act = hidden_act
180
- self.norm_eps = norm_eps
181
- self.norm_bias = norm_bias
182
- self.max_position_embeddings = max_position_embeddings
183
- self.use_cache = use_cache
184
- self.use_deep_embed = use_deep_embed
185
- self.deep_embed_size = deep_embed_size if deep_embed_size is not None else hidden_size
186
- if wkv_state_dtype not in ("float32", "float16", "bfloat16"):
187
- raise ValueError(f"wkv_state_dtype must be float32/float16/bfloat16, got {wkv_state_dtype}")
188
- self.wkv_state_dtype = wkv_state_dtype
189
- self.wkv_implementation = wkv_implementation
190
- self.sparse_channel_mix = sparse_channel_mix
191
-
192
- if hidden_size % head_dim != 0:
193
- raise ValueError(f"hidden_size {hidden_size} must be divisible by head_dim {head_dim}")
194
- if num_heads != hidden_size // head_dim:
195
- raise ValueError(f"num_heads must be hidden_size // head_dim = {hidden_size // head_dim}, got {num_heads}")
196
 
197
- super().__init__(
198
- bos_token_id=bos_token_id,
199
- eos_token_id=eos_token_id,
200
- pad_token_id=pad_token_id,
201
- tie_word_embeddings=tie_word_embeddings,
202
- **kwargs,
203
- )
 
 
 
 
 
 
204
 
205
 
206
  __all__ = ["Rwkv7Config"]
 
13
  # limitations under the License.
14
  """RWKV-7 (Goose) model configuration."""
15
 
16
+ from huggingface_hub.dataclasses import strict
17
+
18
  from transformers.configuration_utils import PreTrainedConfig
19
+ from transformers.utils import auto_docstring
20
 
21
 
22
+ @auto_docstring(
23
+ custom_intro="""
24
  Configuration for [`Rwkv7Model`], an all-recurrent (attention-free) RWKV-7 "Goose"
25
  model. Instantiating with the defaults yields the ~0.1B RWKV-7 configuration.
26
 
27
  Parameter names follow the upstream RWKV reference implementation
28
  (`BlinkDL/RWKV-LM`) rather than a renamed variant, so converting a native
29
  `.pth` checkpoint is close to a rename-free copy.
30
+ """,
31
+ checkpoint="Hakureirm/rwkv7-168m-pile-hf",
32
+ )
33
+ @strict
34
+ class Rwkv7Config(PreTrainedConfig):
35
+ r"""
36
+ vocab_size (`int`, *optional*, defaults to 65536):
37
+ Vocabulary size (RWKV "world" tokenizer).
38
+ hidden_size (`int`, *optional*, defaults to 768):
39
+ Model width `C`.
40
+ num_hidden_layers (`int`, *optional*, defaults to 12):
41
+ Number of blocks.
42
+ head_dim (`int`, *optional*, defaults to 64):
43
+ Width of one WKV head. `hidden_size` must be divisible by it.
44
+ num_heads (`int`, *optional*, defaults to 12):
45
+ Number of WKV heads; must equal `hidden_size // head_dim`.
46
+ decay_low_rank_dim (`int`, *optional*, defaults to 64):
47
+ Rank of the decay (`w`) LoRA.
48
+ a_low_rank_dim (`int`, *optional*, defaults to 64):
49
+ Rank of the in-context-learning-rate (`a`) LoRA.
50
+ v_low_rank_dim (`int`, *optional*, defaults to 32):
51
+ Rank of the value-residual (`v`) LoRA. Unused on layer 0, which
52
+ *produces* `v_first` instead of mixing towards it.
53
+ gate_low_rank_dim (`int`, *optional*, defaults to 128):
54
+ Rank of the output-gate (`g`) LoRA.
55
+ intermediate_size (`int`, *optional*):
56
+ Channel-mix inner width. Defaults to `4 * hidden_size`.
57
+ norm_eps (`float`, *optional*, defaults to 1e-05):
58
+ Epsilon of every LayerNorm/GroupNorm in the model.
59
+ norm_bias (`bool`, *optional*, defaults to `True`):
60
+ Whether the norms carry a bias.
61
+ max_position_embeddings (`int`, *optional*, defaults to 8192):
62
+ Training context length. RWKV is recurrent and not bounded by it at
63
+ inference; it only sizes generation defaults.
64
+ tie_word_embeddings (`bool`, *optional*, defaults to `False`):
65
+ Whether to tie the input embedding and the LM head.
66
+ use_cache (`bool`, *optional*, defaults to `True`):
67
+ Whether to return the recurrent state.
68
+ use_deep_embed (`bool`, *optional*, defaults to `False`):
69
+ Enable the RWKV-8 "DeepEmbed" hook: a per-layer, per-token vector that
70
+ channelwise-modulates the channel-mix. The table is deliberately NOT a
71
+ model weight. It is meant to live in RAM/SSD and be prefetched per
72
+ token, which is the whole point of the design (VRAM savings), so it is
73
+ passed to the forward as `deep_embeds` instead. No RWKV-7 checkpoint
74
+ carries one; this is an extension point, off by default.
75
+ wkv_state_dtype (`str`, *optional*, defaults to `"float32"`):
76
+ Precision the recurrent WKV state is carried and accumulated in,
77
+ independently of the activation dtype. The recurrence is unrolled over
78
+ the whole sequence, so a narrow state drifts; `"float32"` with fp16
79
+ activations is the combination the reference implementation uses.
80
+ `"float16"`/`"bfloat16"` trade that for a smaller state.
81
+ wkv_implementation (`str`, *optional*, defaults to `"eager"`):
82
+ Which WKV recurrence to use, by name, from
83
+ `models.rwkv7.modeling_rwkv7.RWKV7_WKV_FUNCTIONS`. `"eager"` is the
84
+ portable PyTorch path: the sequential step when decoding, the
85
+ chunk-parallel form otherwise, and per-segment when a packed batch is
86
+ passed. Register an entry in that mapping to plug in a fused or varlen
87
+ kernel without forking the model.
88
+ bos_token_id (`int`, *optional*, defaults to 0):
89
+ Beginning-of-sequence id. The RWKV world tokenizer has no dedicated BOS
90
+ token and the reference implementation prepends nothing, so this exists to
91
+ satisfy `GenerationMixin` rather than to be emitted.
92
+ eos_token_id (`int`, *optional*, defaults to 0):
93
+ End-of-sequence id, id 0 in the RWKV world vocabulary.
94
+ pad_token_id (`int`, *optional*, defaults to 0):
95
+ Padding id, the same id 0. Set deliberately rather than left `None`:
96
+ `generate` needs one to pad a batch, and without it a batched call either
97
+ raised or fell back to the eos id with a warning on every step.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
 
99
  ```python
100
  >>> from transformers import Rwkv7Config, Rwkv7Model
 
107
  model_type = "rwkv7"
108
  keys_to_ignore_at_inference = ["past_key_values"]
109
 
110
+ vocab_size: int = 65536
111
+ hidden_size: int = 768
112
+ num_hidden_layers: int = 12
113
+ head_dim: int = 64
114
+ num_heads: int = 12
115
+ decay_low_rank_dim: int = 64
116
+ a_low_rank_dim: int = 64
117
+ v_low_rank_dim: int = 32
118
+ gate_low_rank_dim: int = 128
119
+ # `None` rather than a number: a literal default is correct for the default
120
+ # `hidden_size` and silently wrong for every other one, so a config built as
121
+ # `Rwkv7Config(hidden_size=4096, num_heads=64)` would come back with a channel-mix
122
+ # four times narrower than the architecture it names. `__post_init__` resolves it,
123
+ # and the resolved value is written to `config.json` either way.
124
+ intermediate_size: int | None = None
125
+ norm_eps: float = 1e-5
126
+ norm_bias: bool = True
127
+ max_position_embeddings: int = 8192
128
+ tie_word_embeddings: bool = False
129
+ use_cache: bool = True
130
+ use_deep_embed: bool = False
131
+ wkv_state_dtype: str = "float32"
132
+ wkv_implementation: str = "eager"
133
+ bos_token_id: int | None = 0
134
+ eos_token_id: int | None = 0
135
+ pad_token_id: int | None = 0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
 
137
+ def __post_init__(self, **kwargs):
138
+ if self.intermediate_size is None:
139
+ self.intermediate_size = 4 * self.hidden_size
140
+ if self.wkv_state_dtype not in ("float32", "float16", "bfloat16"):
141
+ raise ValueError(f"wkv_state_dtype must be float32/float16/bfloat16, got {self.wkv_state_dtype}")
142
+ if self.hidden_size % self.head_dim != 0:
143
+ raise ValueError(f"hidden_size {self.hidden_size} must be divisible by head_dim {self.head_dim}")
144
+ if self.num_heads != self.hidden_size // self.head_dim:
145
+ raise ValueError(
146
+ f"num_heads must be hidden_size // head_dim = {self.hidden_size // self.head_dim}, "
147
+ f"got {self.num_heads}"
148
+ )
149
+ super().__post_init__(**kwargs)
150
 
151
 
152
  __all__ = ["Rwkv7Config"]
fused_wkv.py DELETED
@@ -1,138 +0,0 @@
1
- # Copyright 2026 The RWKV team and The HuggingFace Inc. team. All rights reserved.
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
- """Single-token WKV in one Triton kernel: the state is read once and written once.
15
-
16
- The portable recurrence in `modeling_rwkv7.rwkv7_recurrent` touches the state four
17
- times for one decoded token -- once to form `sa = (-kk) @ S`, once to update `S`,
18
- once to read it back for `r @ S`, once to store it. That is the natural way to write
19
- it and it is free at batch 1, where the state is a megabyte a layer and invisible
20
- next to the weights being streamed.
21
-
22
- It stops being free with batch. At batch 256 and 7.2B's shape the state is
23
- `[256, 64, 64, 64]` -- 268 MB a layer, 8.6 GB across 32 layers -- and profiling put
24
- 77% of a decode step inside the time-mix while its own projections were 5.5% of it.
25
- The cost was the state, not the weights, and four passes over it rather than two.
26
-
27
- This does the whole update with the `[head_dim, head_dim]` tile resident. Measured on
28
- one RTX 5090 at 7.2B: 5.7x on the recurrence at batch 256, and the whole model's
29
- batched decode goes from 55% of the albatross reference to 85%.
30
-
31
- Called as a plain function rather than wrapped in a `custom_op`. Dynamo traces a
32
- `@triton.jit` launch natively, and the wrapper is opaque to it -- with the state
33
- declared as mutated, inductor stops issuing CUDA graphs for the region. That is
34
- invisible at batch 256, where the kernel wins anyway, and costs everything at batch
35
- 1: 138.9 tok/s wrapped becomes 57.9, which is eager speed. `sparse_channel_mix` is a
36
- `custom_op` because its three kernels and its compaction buffers are genuinely
37
- opaque; one traced kernel is better off without the wrapper.
38
- """
39
-
40
- import torch
41
- from transformers.utils.import_utils import is_triton_available
42
-
43
- # Guarded rather than bare, although the module itself is only ever imported
44
- # under the same `is_triton_available()` condition: import scanners that parse
45
- # this file in isolation (transformers' remote-code `check_imports` is one)
46
- # exempt imports inside an availability-guarded block, and demand triton on
47
- # every machine otherwise.
48
- if is_triton_available():
49
- import triton
50
- import triton.language as tl
51
-
52
-
53
- @triton.jit
54
- def _wkv_one_kernel(
55
- state_ptr,
56
- r_ptr,
57
- w_ptr,
58
- k_ptr,
59
- v_ptr,
60
- kk_ptr,
61
- a_ptr,
62
- out_ptr,
63
- stride_sb,
64
- stride_sh,
65
- stride_si,
66
- stride_sj,
67
- stride_vb,
68
- stride_vh,
69
- N: tl.constexpr,
70
- ):
71
- """One program per (batch, head).
72
-
73
- `N` is `head_dim` and is a compile-time constant, so the `[N, N]` tile lives in
74
- registers for the whole body: 64x64 fp32 is 16 KB, which is what makes reading
75
- the state once sufficient.
76
- """
77
- batch = tl.program_id(0)
78
- head = tl.program_id(1)
79
-
80
- i = tl.arange(0, N)
81
- j = tl.arange(0, N)
82
- vec = batch * stride_vb + head * stride_vh
83
- r = tl.load(r_ptr + vec + i).to(tl.float32)
84
- w = tl.load(w_ptr + vec + i).to(tl.float32)
85
- k = tl.load(k_ptr + vec + i).to(tl.float32)
86
- v = tl.load(v_ptr + vec + j).to(tl.float32)
87
- kk = tl.load(kk_ptr + vec + i).to(tl.float32)
88
- a = tl.load(a_ptr + vec + i).to(tl.float32)
89
-
90
- # Every axis of the state is strided, the last one included. Hardcoding unit
91
- # stride there reads the tile transposed when the caller hands over anything
92
- # that is not contiguous, and the result is wrong by 80-98% with nothing
93
- # raised: the shape is right, so neither the kernel nor Triton notices.
94
- off = batch * stride_sb + head * stride_sh + i[:, None] * stride_si + j[None, :] * stride_sj
95
- state = tl.load(state_ptr + off).to(tl.float32)
96
-
97
- # Same three lines as the reference, in the same order and in fp32: `sa` uses the
98
- # PRE-update state and the output uses the POST-update one.
99
- sa = tl.sum((-kk)[:, None] * state, axis=0)
100
- state = tl.exp(w)[:, None] * state + (kk * a)[:, None] * sa[None, :] + k[:, None] * v[None, :]
101
- out = tl.sum(r[:, None] * state, axis=0)
102
-
103
- tl.store(state_ptr + off, state.to(state_ptr.dtype.element_ty))
104
- tl.store(out_ptr + vec + j, out.to(out_ptr.dtype.element_ty))
105
-
106
-
107
- def fused_wkv_one(
108
- r: torch.Tensor,
109
- w_log: torch.Tensor,
110
- k: torch.Tensor,
111
- v: torch.Tensor,
112
- kk: torch.Tensor,
113
- a: torch.Tensor,
114
- state: torch.Tensor,
115
- ) -> torch.Tensor:
116
- batch, _, heads, head_dim = r.shape
117
- # The grid comes from `r`, so a state that disagrees with it is read at offsets
118
- # belonging to another row and returns a plausible answer for the wrong sequence.
119
- # Checked rather than assumed: the kernel cannot tell, and neither can the caller.
120
- if tuple(state.shape) != (batch, heads, head_dim, head_dim):
121
- raise ValueError(
122
- f"state has shape {tuple(state.shape)}, but the vectors imply {(batch, heads, head_dim, head_dim)}"
123
- )
124
- out = torch.empty(batch, heads, head_dim, device=r.device, dtype=r.dtype)
125
- flat = [t.reshape(batch, heads, head_dim).contiguous() for t in (r, w_log, k, v, kk, a)]
126
- _wkv_one_kernel[(batch, heads)](
127
- state,
128
- *flat,
129
- out,
130
- state.stride(0),
131
- state.stride(1),
132
- state.stride(2),
133
- state.stride(3),
134
- out.stride(0),
135
- out.stride(1),
136
- N=head_dim,
137
- )
138
- return out.view(batch, 1, heads, head_dim)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hf_rwkv_tokenizer.py ADDED
@@ -0,0 +1,279 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding=utf-8
2
+ # Copyright 2024 The HuggingFace Inc. team.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ """Tokenization classes for RWKV."""
16
+
17
+ import os
18
+ import re
19
+ from typing import TYPE_CHECKING, List, Optional, Tuple
20
+
21
+ from transformers.tokenization_utils import AddedToken, PreTrainedTokenizer
22
+ from transformers.utils import logging
23
+
24
+
25
+ if TYPE_CHECKING:
26
+ pass
27
+
28
+ logger = logging.get_logger(__name__)
29
+
30
+
31
+ VOCAB_FILES_NAMES = {
32
+ "vocab_file": "rwkv_vocab_v20230424.txt",
33
+ }
34
+
35
+ class TRIE:
36
+ __slots__ = tuple("ch,to,values,front".split(","))
37
+ to: list
38
+ values: set
39
+
40
+ def __init__(self, front=None, ch=None):
41
+ self.ch = ch
42
+ self.to = [None for ch in range(256)]
43
+ self.values = set()
44
+ self.front = front
45
+
46
+ def __repr__(self):
47
+ fr = self
48
+ ret = []
49
+ while fr != None:
50
+ if fr.ch != None:
51
+ ret.append(fr.ch)
52
+ fr = fr.front
53
+ return "<TRIE %s %s>" % (ret[::-1], self.values)
54
+
55
+ def add(self, key: bytes, idx: int = 0, val=None):
56
+ if idx == len(key):
57
+ if val is None:
58
+ val = key
59
+ self.values.add(val)
60
+ return self
61
+ ch = key[idx]
62
+ if self.to[ch] is None:
63
+ self.to[ch] = TRIE(front=self, ch=ch)
64
+ return self.to[ch].add(key, idx=idx + 1, val=val)
65
+
66
+ def find_longest(self, key: bytes, idx: int = 0):
67
+ u: TRIE = self
68
+ ch: int = key[idx]
69
+
70
+ while u.to[ch] is not None:
71
+ u = u.to[ch]
72
+ idx += 1
73
+ if u.values:
74
+ ret = idx, u, u.values
75
+ if idx == len(key):
76
+ break
77
+ ch = key[idx]
78
+ return ret
79
+
80
+
81
+ class RWKV_TOKENIZER:
82
+ def __init__(self, file_name):
83
+ self.idx2token = {}
84
+ sorted = [] # must be already sorted
85
+ with open(file_name, "r", encoding="utf-8") as f:
86
+ lines = f.readlines()
87
+ for l in lines:
88
+ idx = int(l[: l.index(" ")])
89
+ x = eval(l[l.index(" ") : l.rindex(" ")])
90
+ x = x.encode("utf-8") if isinstance(x, str) else x
91
+ assert isinstance(x, bytes)
92
+
93
+ assert len(x) == int(l[l.rindex(" ") :])
94
+ sorted += [x]
95
+ self.idx2token[idx] = x
96
+
97
+ self.token2idx = {}
98
+ for k, v in self.idx2token.items():
99
+ self.token2idx[v] = int(k)
100
+
101
+ self.root = TRIE()
102
+ for t, i in self.token2idx.items():
103
+ _ = self.root.add(t, val=(t, i))
104
+
105
+ def encodeBytes(self, src: bytes):
106
+ idx: int = 0
107
+ tokens = []
108
+ while idx < len(src):
109
+ _idx: int = idx
110
+ idx, _, values = self.root.find_longest(src, idx)
111
+ assert idx != _idx
112
+ _, token = next(iter(values))
113
+ tokens.append(token)
114
+ return tokens
115
+
116
+ def decodeBytes(self, tokens):
117
+ return b"".join(map(lambda i: self.idx2token[i], tokens))
118
+
119
+ def encode(self, src):
120
+ if isinstance(src, str):
121
+ return [self.encodeBytes(src.encode("utf-8"))]
122
+ elif isinstance(src, list):
123
+ return [self.encodeBytes(s.encode("utf-8")) for s in src]
124
+
125
+ def decode(self, tokens):
126
+ return [self.decodeBytes(batch).decode("utf-8") for batch in tokens]
127
+ # try:
128
+ # return self.decodeBytes(tokens).decode('utf-8')
129
+ # except:
130
+ # return '\ufffd' # bad utf-8
131
+
132
+ def printTokens(self, tokens):
133
+ for i in tokens:
134
+ s = self.idx2token[i]
135
+ try:
136
+ s = s.decode("utf-8")
137
+ except:
138
+ pass
139
+ print(f"{repr(s)}{i}", end=" ")
140
+ print()
141
+
142
+
143
+ class RwkvTokenizer(PreTrainedTokenizer):
144
+ vocab_files_names = VOCAB_FILES_NAMES
145
+ model_input_names = ["input_ids", "attention_mask"]
146
+
147
+ def __init__(
148
+ self, vocab_file, bos_token="<|rwkv_tokenizer_end_of_text|>", eos_token="<|rwkv_tokenizer_end_of_text|>", unk_token="<|rwkv_tokenizer_end_of_text|>", **kwargs
149
+ ):
150
+ if not os.path.isfile(vocab_file):
151
+ raise ValueError(
152
+ f"Can't find a vocabulary file at path '{vocab_file}'."
153
+ )
154
+
155
+ with open(vocab_file, "r", encoding="utf-8") as reader:
156
+ tokens = reader.readlines()
157
+
158
+ if "add_bos_token" in kwargs:
159
+ self.add_bos_token = kwargs["add_bos_token"]
160
+ else:
161
+ self.add_bos_token = False
162
+ self.trie_tokenizer = RWKV_TOKENIZER(vocab_file)
163
+ vocab = self.trie_tokenizer.token2idx
164
+ self.encoder = vocab
165
+ self.decoder = {v: k for k, v in vocab.items()}
166
+ self._added_tokens_decoder = {0: AddedToken(str(bos_token))}
167
+ super().__init__(
168
+ bos_token=bos_token, eos_token=eos_token, unk_token=unk_token, **kwargs
169
+ )
170
+
171
+ @property
172
+ def vocab_size(self):
173
+ return len(self.encoder)
174
+
175
+ def get_vocab(self):
176
+ vocab = self.encoder
177
+ vocab.update(self.added_tokens_encoder)
178
+ vocab = dict(sorted(vocab.items(), key=lambda item: item[1]))
179
+ return vocab
180
+
181
+ def _tokenize(self, text, split_special_tokens=False):
182
+ # return self.wordpiece_tokenizer.tokenize(text.encode("utf-8"))
183
+ return self.trie_tokenizer.encode(text)[0]
184
+
185
+ def _convert_token_to_id(self, token):
186
+ return token
187
+
188
+ def _convert_id_to_token(self, index):
189
+ """Converts an index (integer) in a token (byte) using the vocab."""
190
+ token = self.decoder.get(index, self.unk_token)
191
+ if isinstance(token, (bytes)):
192
+ token = token.decode("utf-8", errors="replace")
193
+ return token
194
+
195
+ def convert_tokens_to_string(self, tokens):
196
+ """Converts a sequence of tokens (bytes) in a single string. Additional tokens are encoded to bytes"""
197
+ out_string = b"".join(
198
+ [k.encode(errors="replace") if isinstance(k, str) else k for k in tokens]
199
+ ).decode("utf-8")
200
+ return out_string
201
+
202
+ def save_vocabulary(
203
+ self, save_directory: str, filename_prefix: Optional[str] = None
204
+ ) -> Tuple[str]:
205
+ index = 0
206
+ if os.path.isdir(save_directory):
207
+ vocab_file = os.path.join(
208
+ save_directory,
209
+ (filename_prefix + "-" if filename_prefix else "") + "vocab.txt",
210
+ )
211
+ else:
212
+ vocab_file = (
213
+ filename_prefix + "-" if filename_prefix else ""
214
+ ) + save_directory
215
+ with open(vocab_file, "w", encoding="utf-8") as writer:
216
+ for token, token_index in sorted(
217
+ self.encoder.items(), key=lambda kv: kv[1]
218
+ ):
219
+ if index != token_index:
220
+ logger.warning(
221
+ f"Saving vocabulary to {vocab_file}: vocabulary indices are not consecutive."
222
+ " Please check that the vocabulary is not corrupted!"
223
+ )
224
+ index = token_index
225
+ writer.write(str(token) + "\n")
226
+ index += 1
227
+ return (vocab_file,)
228
+
229
+ def build_inputs_with_special_tokens(self, token_ids_0, token_ids_1=None):
230
+ if self.add_bos_token:
231
+ bos_token_ids = [self.bos_token_id]
232
+ else:
233
+ bos_token_ids = []
234
+
235
+ output = bos_token_ids + token_ids_0
236
+
237
+ if token_ids_1 is None:
238
+ return output
239
+
240
+ return output + bos_token_ids + token_ids_1
241
+
242
+ def get_special_tokens_mask(
243
+ self,
244
+ token_ids_0: List[int],
245
+ token_ids_1: Optional[List[int]] = None,
246
+ already_has_special_tokens: bool = False,
247
+ ) -> List[int]:
248
+ """
249
+ Retrieves sequence ids from a token list that has no special tokens added. This method is called when adding
250
+ special tokens using the tokenizer `prepare_for_model` or `encode_plus` methods.
251
+
252
+ Args:
253
+ token_ids_0 (`List[int]`):
254
+ List of IDs.
255
+ token_ids_1 (`List[int]`, *optional*):
256
+ Optional second list of IDs for sequence pairs.
257
+ already_has_special_tokens (`bool`, *optional*, defaults to `False`):
258
+ Whether or not the token list is already formatted with special tokens for the model.
259
+
260
+ Returns:
261
+ `List[int]`: A list of integers in the range [0, 1]: 1 for a special token, 0 for a sequence token.
262
+ """
263
+ if already_has_special_tokens:
264
+ return super().get_special_tokens_mask(
265
+ token_ids_0=token_ids_0,
266
+ token_ids_1=token_ids_1,
267
+ already_has_special_tokens=True,
268
+ )
269
+
270
+ if not self.add_bos_token:
271
+ return super().get_special_tokens_mask(
272
+ token_ids_0=token_ids_0,
273
+ token_ids_1=token_ids_1,
274
+ already_has_special_tokens=False,
275
+ )
276
+
277
+ if token_ids_1 is None:
278
+ return [1] + ([0] * len(token_ids_0))
279
+ return [1] + ([0] * len(token_ids_0)) + [1] + ([0] * len(token_ids_1))
modeling_rwkv7.py CHANGED
@@ -30,22 +30,9 @@ from transformers.initialization import zeros_
30
  from transformers.modeling_layers import GradientCheckpointingLayer
31
  from transformers.modeling_utils import PreTrainedModel
32
  from transformers.utils import ModelOutput, auto_docstring, can_return_tuple, logging
33
- from transformers.utils.import_utils import is_triton_available
34
  from .configuration_rwkv7 import Rwkv7Config
35
 
36
 
37
- # Imported at module scope, NOT lazily from the forward. A function containing an
38
- # `import` is on dynamo's skip list whether or not the import actually runs, so
39
- # calling one per layer breaks the graph at every layer -- which cost 138.9 tok/s at
40
- # batch 1 down to 47.4, eager speed, while the kernel itself was fine. `sparse_channel_mix`
41
- # avoids this by importing from `allocate_state`, outside anything traced; there is no
42
- # equivalent hook here, so it happens once at import.
43
- if is_triton_available():
44
- from .fused_wkv import fused_wkv_one
45
- else: # pragma: no cover - exercised only where triton is absent
46
- fused_wkv_one = None
47
-
48
-
49
  logger = logging.get_logger(__name__)
50
 
51
 
@@ -72,7 +59,7 @@ class Rwkv7TokenShift(nn.Module):
72
  if cu_seq_lens is not None:
73
  # In a packed row the token before a segment's first one belongs to the
74
  # PREVIOUS sequence. Resetting the recurrent state per segment does not
75
- # cover this the shift reaches back through it so the first token of
76
  # each segment gets the zero shift a sequence start is supposed to see.
77
  positions = torch.arange(x.shape[1], device=x.device)
78
  starts = (positions[:, None] == cu_seq_lens[None, :-1]).any(dim=1)
@@ -97,7 +84,7 @@ class Rwkv7TokenShift(nn.Module):
97
 
98
  # e^-0.5. The decay LoRA emits w_log = -INV_SQRT_E * sigmoid(...), so w_log lies in
99
  # (-e^-0.5, 0) and the per-step decay exp(w_log) lies in (exp(-e^-0.5), 1), i.e.
100
- # (0.5452, 1) see RWKV-7 reference. Note the floor is exp(-e^-0.5), not e^-0.5: this
101
  # comment said the latter, and `rwkv7_chunked` built its chunk_size bound on it.
102
  _INV_SQRT_E = 0.6065306597126334
103
 
@@ -126,7 +113,7 @@ def rwkv7_recurrent(
126
  out = r @ S # uses the POST-update state
127
 
128
  The state axes are (key, value); `S[i, j]` accumulates key channel `i` against
129
- value channel `j`. Accumulation is fp32 regardless of the activation dtype
130
  the recurrence is unrolled over the whole sequence, so a lower-precision state
131
  drifts. This is the portable path; a fused kernel may replace it as long as it
132
  reproduces these values.
@@ -180,14 +167,14 @@ def rwkv7_chunked(
180
 
181
  `chunk_size` is bounded by that division by `c`, but by OVERFLOW rather than by
182
  precision, and the difference is worth a factor of four. The per-step decay is at
183
- least `exp(-e^-0.5)` = 0.5452, so at the worst case every channel pinned at that
184
- floor `1/c` grows like `e^(e^-0.5 * chunk_size)`. That reaches 7.2e16 at 64 and
185
  fp32 tops out near 3.4e38, so there are twenty-odd decades of headroom left at the
186
  default, and the ceiling is `ln(finfo.max) / e^-0.5` = 146 in fp32.
187
 
188
  That derivation is worth stating carefully because the first version of it was
189
  wrong in a way the numbers hid. It said the decay floor was `e^-0.5` rather than
190
- `exp(-e^-0.5)`, hence growth like `e^(0.5 * chunk_size)` which puts the ceiling at
191
  177, and measured at the decay floor this function returns all-NaN from 147 up. The
192
  quoted 7.2e16 came from the correct law all along (`e^(0.5*64)` is 7.9e13), so the
193
  arithmetic had been done right and written up wrong, and following the prose rather
@@ -292,7 +279,7 @@ def rwkv7_eager(
292
  ) -> tuple[torch.Tensor, torch.Tensor]:
293
  """Portable WKV: the sequential step for a single token, chunk-parallel otherwise.
294
 
295
- With `cu_seq_lens` the row is a *packed* batch several independent sequences
296
  laid end to end, the layout a varlen kernel consumes. Each one has to start
297
  from a fresh state, so they are run in turn and their outputs concatenated;
298
  the state returned is the last segment's, which is the one a continuation
@@ -326,55 +313,6 @@ def rwkv7_eager(
326
  RWKV7_WKV_FUNCTIONS = {"eager": rwkv7_eager}
327
 
328
 
329
- def rwkv7_fused(
330
- r: torch.Tensor,
331
- w_log: torch.Tensor,
332
- k: torch.Tensor,
333
- v: torch.Tensor,
334
- kk: torch.Tensor,
335
- a: torch.Tensor,
336
- state: torch.Tensor,
337
- cu_seq_lens: torch.Tensor | None = None,
338
- **kwargs,
339
- ) -> tuple[torch.Tensor, torch.Tensor]:
340
- """`"eager"`, with the single-token step replaced by a fused Triton kernel.
341
-
342
- Only the decode step changes; prefill and packed batches fall through to the
343
- portable path, which is also where this lands when Triton is missing or the tensors
344
- are on CPU. A narrower state is NOT a fallback case -- the kernel stores back
345
- through the state's own dtype, which is the point of letting `wkv_state_dtype`
346
- select it, and this docstring claimed the opposite for several commits after that
347
- fallback was deliberately removed. The state is handed back rather than mutated in
348
- place; the portable path does not mutate either, and saying it did was the same
349
- stale prose.
350
-
351
- Worth it only with batch: the kernel's whole point is that the state is read once
352
- instead of three times, and at batch 1 the state is not what the step is spending
353
- its time on. Measured at 7.2B on one RTX 5090, against albatross faster3a: batched
354
- decode 55% -> 85% at 256x1, 91% -> 100% at 32x1, and 1x1 unchanged.
355
- """
356
- single_token = r.shape[1] == 1 and cu_seq_lens is None
357
- # The kernel loads to fp32 and stores back in whatever the state's dtype is, so a
358
- # narrower state is not a different code path -- and it is the only lever left at
359
- # batch. The kernel already runs at ~86% of this card's bandwidth moving a 268 MB
360
- # fp32 state per layer, so halving the state is worth more than any tuning of it.
361
- # `head_dim` a power of two is part of the kernel's applicability domain, not a
362
- # detail: `tl.arange` requires it, and without this term a width like 192 reached
363
- # Triton and came back as a CompilationError raised from inside the kernel rather
364
- # than as a fallback. The portable path has no such constraint, so falling through
365
- # is both correct and what the docstring already promises for the other cases.
366
- width = r.shape[-1]
367
- if not (single_token and r.is_cuda and fused_wkv_one is not None and width & (width - 1) == 0):
368
- return rwkv7_eager(r, w_log, k, v, kk, a, state, cu_seq_lens=cu_seq_lens, **kwargs)
369
- return fused_wkv_one(r, w_log, k, v, kk, a, state), state
370
-
371
-
372
- # Added after the definition rather than in the literal above: the modular converter
373
- # is free to move the dict, and it emits it next to `rwkv7_eager` -- ahead of this
374
- # function, which made the generated file raise NameError on import.
375
- RWKV7_WKV_FUNCTIONS["fused"] = rwkv7_fused
376
-
377
-
378
  class Rwkv7Attention(nn.Module):
379
  """RWKV-7 time-mixing block (the recurrent replacement for self-attention)."""
380
 
@@ -467,9 +405,9 @@ class Rwkv7Attention(nn.Module):
467
  #
468
  # The decay is held at w = exp(0) = 1, so the transition is the identity.
469
  #
470
- # `k` and `v` are zeroed EXPLICITLY. An earlier version relied on the
471
- # blanked hidden state making them zero, since the projections are
472
- # bias-free -- but that is only true for a pad with nothing before it.
473
  # A pad that FOLLOWS a real token still receives that token's hidden
474
  # state through the token shift, so `delta = shifted - 0` is non-zero,
475
  # and so are `k` and `v`. The update term `k v^T` then entered the state
@@ -503,7 +441,7 @@ class Rwkv7Attention(nn.Module):
503
  def _heads(t):
504
  return t.view(batch, seq_len, H, N)
505
 
506
- # Named rather than indexed: a key that is not registered used to surface as a
507
  # bare `KeyError: 'chunked'` from inside the forward, several frames from the
508
  # config field that caused it, and a caller looping over shapes would record it
509
  # as "this shape did not run" instead of "this model was never built". The
@@ -562,51 +500,6 @@ class Rwkv7Attention(nn.Module):
562
  return w_log, a, g, v_gate
563
 
564
 
565
- _SPARSE_OP_READY = False
566
-
567
-
568
- def _ensure_sparse_op() -> bool:
569
- """Import the Triton kernels once, outside anything that gets traced.
570
-
571
- A lazy `import` inside the forward is itself on dynamo's skip list, so doing
572
- this on the hot path breaks the graph at every layer even when the kernel is a
573
- properly registered custom op.
574
- """
575
- global _SPARSE_OP_READY
576
- if not _SPARSE_OP_READY and is_triton_available():
577
- from . import sparse_channel_mix # noqa: F401 (registers the op)
578
-
579
- _SPARSE_OP_READY = True
580
- return _SPARSE_OP_READY
581
-
582
-
583
- def sparse_channel_mix_value(
584
- activation: torch.Tensor,
585
- weight_t: torch.Tensor,
586
- accumulator: torch.Tensor,
587
- index: torch.Tensor,
588
- value: torch.Tensor,
589
- counter: torch.Tensor,
590
- ) -> torch.Tensor:
591
- """`out = activation @ weight_t`, reading only the rows the input selects.
592
-
593
- `activation` is `relu(key(x))**2`, so its zeros are EXACT and a zero channel
594
- contributes exactly nothing — skipping its weight row is not an approximation.
595
- On a 7.2B checkpoint ~90% of channels are zero at any step (10.07% density,
596
- measured over 16 decode steps) while this projection is a third of the model's
597
- bytes, which is why it is worth a kernel at all. `index`/`value`/`counter` are
598
- the scratch the kernel compacts the surviving channels into.
599
-
600
- Only pays when kernel launches are captured (`torch.compile` with CUDA graphs).
601
- Eagerly it LOSES to one dense cuBLAS call, because the step is then bound by
602
- launch overhead rather than by the weight stream — measured both ways.
603
- Falls back to a dense matmul when Triton is unavailable.
604
- """
605
- if _SPARSE_OP_READY:
606
- return torch.ops.rwkv7.sparse_channel_mix_value(activation, weight_t, accumulator, index, value, counter)
607
- return torch.nn.functional.linear(activation, weight_t.t())
608
-
609
-
610
  class Rwkv7FeedForward(nn.Module):
611
  """RWKV-7 channel-mixing block: squared-ReLU over a single token shift."""
612
 
@@ -619,12 +512,6 @@ class Rwkv7FeedForward(nn.Module):
619
  self.x_k = nn.Parameter(torch.zeros(1, 1, C))
620
  self.key = nn.Linear(C, config.intermediate_size, bias=False)
621
  self.value = nn.Linear(config.intermediate_size, C, bias=False)
622
- self._value_t = None # [inter, hidden] copy for the sparse path, built lazily
623
- self._value_fingerprint = None
624
- self._accumulator = None
625
- self._compact_index = None
626
- self._compact_value = None
627
- self._compact_counter = None
628
 
629
  def forward(
630
  self,
@@ -643,9 +530,8 @@ class Rwkv7FeedForward(nn.Module):
643
  # because the design keeps the table in RAM/SSD and prefetches per token.
644
  # Its width says which side it attaches to -- `intermediate_size` scales the
645
  # projection's INPUT (the reference "4x" variant), `hidden_size` its OUTPUT
646
- # ("1x"). Resolved once here so both the dense and sparse projections below
647
- # see the same decision; splitting it per branch is how the sparse path
648
- # silently dropped the 4x variant once already.
649
  scale_output = None
650
  if deep_embed is not None:
651
  if deep_embed.shape[-1] == inner.shape[-1]:
@@ -659,93 +545,8 @@ class Rwkv7FeedForward(nn.Module):
659
  return out, new_shift_state
660
 
661
  def _project(self, inner: torch.Tensor) -> torch.Tensor:
662
- """`value(inner)`, sparsely when that is both enabled and worthwhile.
663
-
664
- The sparse kernel decodes one token at a time on CUDA; anything else (a
665
- batch, a prefill, CPU) takes the dense projection, which is also what makes
666
- the two paths comparable in tests.
667
- """
668
- single_token = inner.shape[0] * inner.shape[1] == 1
669
- if self.config.sparse_channel_mix and single_token and inner.is_cuda:
670
- return self._sparse_value(inner.reshape(-1)).view(1, 1, -1)
671
  return self.value(inner)
672
 
673
- def _sparse_value(self, activation: torch.Tensor) -> torch.Tensor:
674
- """Value projection over only the nonzero channels of `relu(key(x))**2`.
675
-
676
- The transposed weight is built on first use rather than stored: the sparse
677
- read needs one contiguous row per input channel, which the `nn.Linear`
678
- layout does not give, and materialising it eagerly would cost every user
679
- the memory whether or not they enable this.
680
- """
681
- if not torch.compiler.is_compiling():
682
- self.build_sparse_cache()
683
- return sparse_channel_mix_value(
684
- activation,
685
- self._value_t,
686
- self._accumulator,
687
- self._compact_index,
688
- self._compact_value,
689
- self._compact_counter,
690
- )
691
-
692
- def build_sparse_cache(self) -> None:
693
- """Materialise the transposed weight and the accumulator the sparse path needs.
694
-
695
- Called lazily from the projection, and eagerly by
696
- [`Rwkv7Model.allocate_state`] — which is what makes it safe to compile. Built
697
- for the first time *inside* a compiled region these buffers cannot have their
698
- addresses pinned, inductor declines CUDA graphs for a region that mutates its
699
- inputs, and the decode runs 2.7x slower while reporting nothing but a single
700
- line of warning.
701
- """
702
- weight = self.value.weight
703
- _ensure_sparse_op()
704
- # Tie the cache to the weight's identity AND its in-place version, so a
705
- # `mul_`, a fresh load, or a replaced parameter all invalidate it. A cache
706
- # that silently survives a weight change is worse than no cache.
707
- #
708
- # One mutation this does NOT see, stated because it is the one people write:
709
- # `weight.data.copy_(...)`. Going through `.data` is what detaches from the
710
- # autograd version counter, so the version is unchanged, the storage is
711
- # unchanged, and the fingerprint matches a weight that no longer exists.
712
- # Measured on torch 2.13: `mul_` steps the version, `.data.copy_` does not,
713
- # `load_state_dict` does (it copies through the parameter). Quantisation and
714
- # adapter-merging code is where `.data.copy_` shows up. Call
715
- # [`invalidate_sparse_cache`] after doing that -- there is no cheap way to
716
- # notice from here, and the expensive way is a device sync per decoded token.
717
- fingerprint = (weight.data_ptr(), weight._version, weight.dtype)
718
- if self._value_t is None or self._value_fingerprint != fingerprint:
719
- inter, hidden = weight.shape[1], weight.shape[0]
720
- self._value_t = weight.t().contiguous()
721
- self._accumulator = torch.zeros(hidden, device=weight.device, dtype=torch.float32)
722
- # The projection compacts the surviving channels into these before
723
- # walking them. Allocated here with everything else rather than on
724
- # first use: a buffer that first appears inside a compiled region
725
- # cannot be pinned, and CUDA graphs are then declined for the whole
726
- # region -- which costs far more than the projection saves.
727
- self._compact_index = torch.zeros(inter, device=weight.device, dtype=torch.int32)
728
- self._compact_value = torch.zeros(inter, device=weight.device, dtype=torch.float32)
729
- self._compact_counter = torch.zeros(1, device=weight.device, dtype=torch.int32)
730
- self._value_fingerprint = fingerprint
731
-
732
- def invalidate_sparse_cache(self) -> None:
733
- """Drop the transposed weight, so the next call rebuilds it.
734
-
735
- The escape hatch for a weight change the fingerprint cannot see -- see
736
- [`build_sparse_cache`] for which those are. Rebuilding allocates, so do it
737
- before compiling, not between decode steps.
738
- """
739
- self._value_fingerprint = None
740
-
741
- def _load_from_state_dict(self, *args, **kwargs):
742
- # Loading replaces the projection under a cache that may already be warm.
743
- # The version counter happens to catch this on current torch, but that is a
744
- # property of how `load_state_dict` copies rather than a promise, and the
745
- # cost of being wrong is a silently stale projection.
746
- super()._load_from_state_dict(*args, **kwargs)
747
- self.invalidate_sparse_cache()
748
-
749
 
750
  class Rwkv7CacheLayer(LinearAttentionLayer):
751
  """One block's slice of the recurrent state: the WKV matrix and two token shifts.
@@ -1072,24 +873,18 @@ class Rwkv7Model(Rwkv7PreTrainedModel):
1072
  def allocate_state(self, batch: int, device=None, dtype=None) -> Rwkv7Cache:
1073
  """A zeroed cache, plus everything else the decode path would build lazily.
1074
 
1075
- Call this before compiling, and pass the result in as `state=`. Two kinds of
1076
- buffer have to exist by then, and for the same reason: `mark_static_address`
1077
- cannot run during tracing, so anything first allocated *inside* the compiled
1078
- region stays unpinned, and inductor declines CUDA graphs for a region that
1079
- mutates its inputs. Starting from `state=None` loses them to the state
1080
- buffers; leaving the sparse path cold loses them to one unpinned transposed
1081
- weight. Either way the decode runs several times slower and says so only in
1082
- a line of warning, so both are handled here rather than left to the caller
1083
- to remember.
1084
  """
1085
  state = self._empty_state(
1086
  batch,
1087
  device if device is not None else self.emb.weight.device,
1088
  dtype if dtype is not None else self.emb.weight.dtype,
1089
  )
1090
- if self.config.sparse_channel_mix:
1091
- for block in self.blocks:
1092
- block.ffn.build_sparse_cache()
1093
  return state
1094
 
1095
  def _empty_state(self, batch: int, device, dtype) -> Rwkv7Cache:
@@ -1125,11 +920,11 @@ class Rwkv7Model(Rwkv7PreTrainedModel):
1125
  *history* when `cu_seq_lens` is given -- see there.
1126
  deep_embeds (`torch.FloatTensor`, *optional*):
1127
  RWKV-8 DeepEmbed vectors for this batch, shaped
1128
- `[num_layers, batch, seq_len, deep_embed_size]` (or broadcastable).
1129
  Only meaningful when `config.use_deep_embed` is set; the table itself is
1130
  external to the checkpoint by design.
1131
  cu_seq_lens (`torch.LongTensor`, *optional*):
1132
- Cumulative sequence lengths for a *packed* batch several sequences
1133
  concatenated into one row instead of padded to a rectangle, starting at
1134
  0 and ending at `seq_len`, and non-decreasing. Each segment then decodes
1135
  from a fresh recurrent state, as if it had been run on its own. This is
@@ -1178,9 +973,9 @@ class Rwkv7Model(Rwkv7PreTrainedModel):
1178
  # can hand over the whole conversation's mask. A prefix chunk must slice
1179
  # its own mask to match, or it silently reads the wrong positions.
1180
  #
1181
- # Applied at seq_len == 1 as well. It used to be skipped there, on the
1182
- # theory that a single decoded token is never padding -- true of
1183
- # `generate`, but an assumption about the caller rather than a property
1184
  # of the model, and a fully-masked 1-token row was moving the state.
1185
  keep = attention_mask[:, -inputs_embeds.shape[1] :, None].to(inputs_embeds.dtype)
1186
 
@@ -1217,8 +1012,8 @@ class Rwkv7Model(Rwkv7PreTrainedModel):
1217
  if output_hidden_states:
1218
  all_hidden_states = all_hidden_states + (hidden_states,)
1219
  # Gated on the config flag as well as on the argument: passing
1220
- # `deep_embeds` to a model configured without them used to modulate the
1221
- # channel-mix anyway, which is a silently different model.
1222
  layer_deep_embed = (
1223
  deep_embeds[block.layer_id] if deep_embeds is not None and self.config.use_deep_embed else None
1224
  )
@@ -1258,9 +1053,9 @@ class Rwkv7ForCausalLM(Rwkv7PreTrainedModel, GenerationMixin):
1258
  )
1259
  model_inputs["state"] = state
1260
  # Everything else the caller passed goes through, minus what this model does
1261
- # not take. It used to be an allowlist of three names, which meant
1262
- # `generate(output_hidden_states=True)` returned a tuple of `None` -- the flag
1263
- # was dropped here, the forward never saw it, and generate collected the
1264
  # nothing it got back. Any user kwarg met the same fate, silently. The two
1265
  # excluded here are `labels`, which would make generate compute a loss it
1266
  # never reads, and the KV-cache bookkeeping that belongs to models with a KV
 
30
  from transformers.modeling_layers import GradientCheckpointingLayer
31
  from transformers.modeling_utils import PreTrainedModel
32
  from transformers.utils import ModelOutput, auto_docstring, can_return_tuple, logging
 
33
  from .configuration_rwkv7 import Rwkv7Config
34
 
35
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  logger = logging.get_logger(__name__)
37
 
38
 
 
59
  if cu_seq_lens is not None:
60
  # In a packed row the token before a segment's first one belongs to the
61
  # PREVIOUS sequence. Resetting the recurrent state per segment does not
62
+ # cover this (the shift reaches back through it), so the first token of
63
  # each segment gets the zero shift a sequence start is supposed to see.
64
  positions = torch.arange(x.shape[1], device=x.device)
65
  starts = (positions[:, None] == cu_seq_lens[None, :-1]).any(dim=1)
 
84
 
85
  # e^-0.5. The decay LoRA emits w_log = -INV_SQRT_E * sigmoid(...), so w_log lies in
86
  # (-e^-0.5, 0) and the per-step decay exp(w_log) lies in (exp(-e^-0.5), 1), i.e.
87
+ # (0.5452, 1). See RWKV-7 reference. Note the floor is exp(-e^-0.5), not e^-0.5: this
88
  # comment said the latter, and `rwkv7_chunked` built its chunk_size bound on it.
89
  _INV_SQRT_E = 0.6065306597126334
90
 
 
113
  out = r @ S # uses the POST-update state
114
 
115
  The state axes are (key, value); `S[i, j]` accumulates key channel `i` against
116
+ value channel `j`. Accumulation is fp32 regardless of the activation dtype:
117
  the recurrence is unrolled over the whole sequence, so a lower-precision state
118
  drifts. This is the portable path; a fused kernel may replace it as long as it
119
  reproduces these values.
 
167
 
168
  `chunk_size` is bounded by that division by `c`, but by OVERFLOW rather than by
169
  precision, and the difference is worth a factor of four. The per-step decay is at
170
+ least `exp(-e^-0.5)` = 0.5452, so at the worst case, every channel pinned at that
171
+ floor, `1/c` grows like `e^(e^-0.5 * chunk_size)`. That reaches 7.2e16 at 64 and
172
  fp32 tops out near 3.4e38, so there are twenty-odd decades of headroom left at the
173
  default, and the ceiling is `ln(finfo.max) / e^-0.5` = 146 in fp32.
174
 
175
  That derivation is worth stating carefully because the first version of it was
176
  wrong in a way the numbers hid. It said the decay floor was `e^-0.5` rather than
177
+ `exp(-e^-0.5)`, hence growth like `e^(0.5 * chunk_size)`, which puts the ceiling at
178
  177, and measured at the decay floor this function returns all-NaN from 147 up. The
179
  quoted 7.2e16 came from the correct law all along (`e^(0.5*64)` is 7.9e13), so the
180
  arithmetic had been done right and written up wrong, and following the prose rather
 
279
  ) -> tuple[torch.Tensor, torch.Tensor]:
280
  """Portable WKV: the sequential step for a single token, chunk-parallel otherwise.
281
 
282
+ With `cu_seq_lens` the row is a *packed* batch: several independent sequences
283
  laid end to end, the layout a varlen kernel consumes. Each one has to start
284
  from a fresh state, so they are run in turn and their outputs concatenated;
285
  the state returned is the last segment's, which is the one a continuation
 
313
  RWKV7_WKV_FUNCTIONS = {"eager": rwkv7_eager}
314
 
315
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
316
  class Rwkv7Attention(nn.Module):
317
  """RWKV-7 time-mixing block (the recurrent replacement for self-attention)."""
318
 
 
405
  #
406
  # The decay is held at w = exp(0) = 1, so the transition is the identity.
407
  #
408
+ # `k` and `v` are zeroed EXPLICITLY rather than relying on the blanked
409
+ # hidden state to make them zero: the projections are bias-free, so that
410
+ # only holds for a pad with nothing before it.
411
  # A pad that FOLLOWS a real token still receives that token's hidden
412
  # state through the token shift, so `delta = shifted - 0` is non-zero,
413
  # and so are `k` and `v`. The update term `k v^T` then entered the state
 
441
  def _heads(t):
442
  return t.view(batch, seq_len, H, N)
443
 
444
+ # Named rather than indexed: an unregistered key would otherwise surface as a
445
  # bare `KeyError: 'chunked'` from inside the forward, several frames from the
446
  # config field that caused it, and a caller looping over shapes would record it
447
  # as "this shape did not run" instead of "this model was never built". The
 
500
  return w_log, a, g, v_gate
501
 
502
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
503
  class Rwkv7FeedForward(nn.Module):
504
  """RWKV-7 channel-mixing block: squared-ReLU over a single token shift."""
505
 
 
512
  self.x_k = nn.Parameter(torch.zeros(1, 1, C))
513
  self.key = nn.Linear(C, config.intermediate_size, bias=False)
514
  self.value = nn.Linear(config.intermediate_size, C, bias=False)
 
 
 
 
 
 
515
 
516
  def forward(
517
  self,
 
530
  # because the design keeps the table in RAM/SSD and prefetches per token.
531
  # Its width says which side it attaches to -- `intermediate_size` scales the
532
  # projection's INPUT (the reference "4x" variant), `hidden_size` its OUTPUT
533
+ # ("1x"). Resolved once here rather than per branch, so a later branch cannot
534
+ # drop the 4x variant silently.
 
535
  scale_output = None
536
  if deep_embed is not None:
537
  if deep_embed.shape[-1] == inner.shape[-1]:
 
545
  return out, new_shift_state
546
 
547
  def _project(self, inner: torch.Tensor) -> torch.Tensor:
 
 
 
 
 
 
 
 
 
548
  return self.value(inner)
549
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
550
 
551
  class Rwkv7CacheLayer(LinearAttentionLayer):
552
  """One block's slice of the recurrent state: the WKV matrix and two token shifts.
 
873
  def allocate_state(self, batch: int, device=None, dtype=None) -> Rwkv7Cache:
874
  """A zeroed cache, plus everything else the decode path would build lazily.
875
 
876
+ Call this before compiling, and pass the result in as `state=`.
877
+ `mark_static_address` cannot run during tracing, so anything first allocated
878
+ *inside* the compiled region stays unpinned, and inductor declines CUDA graphs
879
+ for a region that mutates its inputs. Starting from `state=None` loses the
880
+ state buffers that way, and the decode then runs several times slower while
881
+ saying so only in a line of warning.
 
 
 
882
  """
883
  state = self._empty_state(
884
  batch,
885
  device if device is not None else self.emb.weight.device,
886
  dtype if dtype is not None else self.emb.weight.dtype,
887
  )
 
 
 
888
  return state
889
 
890
  def _empty_state(self, batch: int, device, dtype) -> Rwkv7Cache:
 
920
  *history* when `cu_seq_lens` is given -- see there.
921
  deep_embeds (`torch.FloatTensor`, *optional*):
922
  RWKV-8 DeepEmbed vectors for this batch, shaped
923
+ `[num_layers, batch, seq_len, hidden_size or intermediate_size]` (or broadcastable).
924
  Only meaningful when `config.use_deep_embed` is set; the table itself is
925
  external to the checkpoint by design.
926
  cu_seq_lens (`torch.LongTensor`, *optional*):
927
+ Cumulative sequence lengths for a *packed* batch: several sequences
928
  concatenated into one row instead of padded to a rectangle, starting at
929
  0 and ending at `seq_len`, and non-decreasing. Each segment then decodes
930
  from a fresh recurrent state, as if it had been run on its own. This is
 
973
  # can hand over the whole conversation's mask. A prefix chunk must slice
974
  # its own mask to match, or it silently reads the wrong positions.
975
  #
976
+ # Applied at seq_len == 1 as well. Skipping it there assumes a single
977
+ # decoded token is never padding -- true of `generate`, but an
978
+ # assumption about the caller rather than a property
979
  # of the model, and a fully-masked 1-token row was moving the state.
980
  keep = attention_mask[:, -inputs_embeds.shape[1] :, None].to(inputs_embeds.dtype)
981
 
 
1012
  if output_hidden_states:
1013
  all_hidden_states = all_hidden_states + (hidden_states,)
1014
  # Gated on the config flag as well as on the argument: passing
1015
+ # `deep_embeds` to a model configured without them would otherwise modulate
1016
+ # the channel-mix anyway, which is a silently different model.
1017
  layer_deep_embed = (
1018
  deep_embeds[block.layer_id] if deep_embeds is not None and self.config.use_deep_embed else None
1019
  )
 
1053
  )
1054
  model_inputs["state"] = state
1055
  # Everything else the caller passed goes through, minus what this model does
1056
+ # not take. An allowlist of specific names instead would make
1057
+ # `generate(output_hidden_states=True)` return a tuple of `None`: the flag
1058
+ # would be dropped here, the forward would never see it, and generate collects the
1059
  # nothing it got back. Any user kwarg met the same fate, silently. The two
1060
  # excluded here are `labels`, which would make generate compute a loss it
1061
  # never reads, and the KV-cache bookkeeping that belongs to models with a KV
rwkv_vocab_v20230424.txt ADDED
The diff for this file is too large to render. See raw diff
 
sparse_channel_mix.py DELETED
@@ -1,175 +0,0 @@
1
- # Copyright 2026 The RWKV team and The HuggingFace Inc. team. All rights reserved.
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
- """Triton sparse channel-mix value projection for RWKV-7 decode.
15
-
16
- Kept out of `modeling_rwkv7.py` so the model file stays importable without Triton;
17
- `sparse_channel_mix_value` there dispatches here and falls back to a dense matmul.
18
-
19
- Registered as a custom op rather than called directly: a raw Triton launch is on
20
- dynamo's skip list, so calling it from the model breaks the graph at every layer
21
- (21 breaks in a 7.2B forward, measured) and costs about 8% end to end. As an opaque
22
- op it stays inside the single captured graph.
23
-
24
- The weight is indexed `[inter, hidden]` so one selected input channel reads one
25
- contiguous row.
26
-
27
- STRUCTURE. The surviving indices are compacted into a shared list first, and the
28
- projection then walks that list with scalar loads and a runtime trip count. Five
29
- structures were measured in situ on a 7.2B decode -- end to end, batch 1, under
30
- `max-autotune` with CUDA graphs engaged:
31
-
32
- compacted indices, SPLIT=128 133.9 tok/s <- shipped
33
- tile-scan, (inter/16, hidden/1024) grid 127.8
34
- scalar re-read instead of a reduction slower than tile-scan
35
- input axis moved off the grid, 512 prog slower than tile-scan
36
- coarse output tiles, few programs far worse
37
-
38
- The compacted structure was tried once before and rejected, and why is worth
39
- keeping, because the reason was true and has stopped being true. Measured with one
40
- launch per stage and no CUDA graph, its extra launch and the barrier its
41
- data-dependent trip count forces cost ~1.3 ms/step of pipeline bubbles against the
42
- 0.43 ms of kernel time it saved. Once the decode compiled into a single graph with
43
- cudagraphs engaged, GPU-busy time equals wall time and there are no bubbles left to
44
- pay -- so the arithmetic that killed it no longer applied to anything.
45
-
46
- The first re-test still lost, 122.3 against 127.8, because it inherited SPLIT=8:
47
- 128 programs against the tile-scan's 4096. The split decides how much of the card
48
- the walk occupies and had never been swept. It is the whole difference:
49
-
50
- SPLIT 8 -> 122.3 SPLIT 64 -> 133.3
51
- SPLIT 16 -> 131.1 SPLIT 128 -> 133.9 <- optimum, bracketed both ways
52
- SPLIT 32 -> 133.3 SPLIT 256 -> 132.9
53
- SPLIT 512 -> 132.4
54
-
55
- The durable lesson is about the record rather than the kernel: a rejection is only
56
- as good as the conditions it was measured under, and one that does not name them
57
- gets re-read as a fact about the algorithm. 128 is measured at hidden=4096; other
58
- widths have not been swept.
59
-
60
- Measure any variant of this end to end, not as a standalone kernel. Three separate
61
- standalone harnesses each favoured a configuration that lost in the model -- by
62
- allowing L2 reuse, by letting independent iterations overlap, and by using random
63
- weights whose `relu(x)**2` is ~50% dense instead of the ~10% a real checkpoint
64
- gives at any decode step.
65
-
66
- Cross-tile partials land in an fp32 accumulator through atomics, and the finalize
67
- pass re-zeros both that accumulator and the counter as it casts, so no separate
68
- clear is ever launched.
69
- """
70
-
71
- import torch
72
- from transformers.utils.import_utils import is_triton_available
73
-
74
- # Guarded rather than bare, although the module itself is only ever imported
75
- # under the same `is_triton_available()` condition: import scanners that parse
76
- # this file in isolation (transformers' remote-code `check_imports` is one)
77
- # exempt imports inside an availability-guarded block, and demand triton on
78
- # every machine otherwise.
79
- if is_triton_available():
80
- import triton
81
- import triton.language as tl
82
-
83
-
84
- # How many programs the walk is split into. Measured optimum at hidden=4096; it sets
85
- # how much of the card the matvec occupies, so it is the first thing to sweep on a
86
- # model of a different width.
87
- _SPLIT = 128
88
- _BLOCK_H = 256
89
- _BLOCK_C = 256
90
-
91
-
92
- @triton.jit
93
- def _compact_kernel(act_ptr, idx_ptr, val_ptr, cnt_ptr, inter, BLOCK: tl.constexpr):
94
- """Append this tile's surviving (index, value) pairs to one shared list."""
95
- offs = tl.program_id(0) * BLOCK + tl.arange(0, BLOCK)
96
- act = tl.load(act_ptr + offs, mask=offs < inter, other=0.0).to(tl.float32)
97
- nonzero = act != 0.0
98
- count = tl.sum(nonzero.to(tl.int32))
99
- if count > 0:
100
- # reserve a contiguous range, then place each survivor at its rank within it
101
- base = tl.atomic_add(cnt_ptr, count)
102
- rank = tl.cumsum(nonzero.to(tl.int32)) - nonzero.to(tl.int32)
103
- tl.store(idx_ptr + base + rank, offs, mask=nonzero)
104
- tl.store(val_ptr + base + rank, act, mask=nonzero)
105
-
106
-
107
- @triton.jit
108
- def _sparse_value_kernel(
109
- idx_ptr,
110
- val_ptr,
111
- cnt_ptr,
112
- w_ptr,
113
- acc_ptr,
114
- hidden,
115
- SPLIT: tl.constexpr,
116
- BLOCK_H: tl.constexpr,
117
- ):
118
- """Walk a slice of the compacted list, accumulating into one output tile."""
119
- pid_h, pid_s = tl.program_id(0), tl.program_id(1)
120
- total = tl.load(cnt_ptr)
121
- per = (total + SPLIT - 1) // SPLIT
122
- start = pid_s * per
123
- stop = tl.minimum(start + per, total)
124
- offs_h = pid_h * BLOCK_H + tl.arange(0, BLOCK_H)
125
- mask_h = offs_h < hidden
126
- acc = tl.zeros([BLOCK_H], dtype=tl.float32)
127
- for i in range(start, stop):
128
- idx = tl.load(idx_ptr + i)
129
- a = tl.load(val_ptr + i)
130
- acc += a * tl.load(w_ptr + idx * hidden + offs_h, mask=mask_h, other=0.0).to(tl.float32)
131
- if stop > start:
132
- tl.atomic_add(acc_ptr + offs_h, acc, mask=mask_h)
133
-
134
-
135
- @triton.jit
136
- def _sparse_finalize_kernel(acc_ptr, out_ptr, cnt_ptr, hidden, BLOCK: tl.constexpr):
137
- offs = tl.program_id(0) * BLOCK + tl.arange(0, BLOCK)
138
- mask = offs < hidden
139
- value = tl.load(acc_ptr + offs, mask=mask, other=0.0)
140
- tl.store(out_ptr + offs, value.to(out_ptr.dtype.element_ty), mask=mask)
141
- tl.store(acc_ptr + offs, tl.zeros([BLOCK], dtype=tl.float32), mask=mask)
142
- if tl.program_id(0) == 0:
143
- tl.store(cnt_ptr, 0)
144
-
145
-
146
- @torch.library.custom_op("rwkv7::sparse_channel_mix_value", mutates_args={"accumulator", "index", "value", "counter"})
147
- def triton_sparse_value(
148
- activation: torch.Tensor,
149
- weight_t: torch.Tensor,
150
- accumulator: torch.Tensor,
151
- index: torch.Tensor,
152
- value: torch.Tensor,
153
- counter: torch.Tensor,
154
- ) -> torch.Tensor:
155
- inter, hidden = weight_t.shape
156
- out = torch.empty(hidden, device=activation.device, dtype=activation.dtype)
157
- _compact_kernel[(triton.cdiv(inter, _BLOCK_C),)](activation, index, value, counter, inter, BLOCK=_BLOCK_C)
158
- _sparse_value_kernel[(triton.cdiv(hidden, _BLOCK_H), _SPLIT)](
159
- index,
160
- value,
161
- counter,
162
- weight_t,
163
- accumulator,
164
- hidden,
165
- SPLIT=_SPLIT,
166
- BLOCK_H=_BLOCK_H,
167
- num_warps=4,
168
- )
169
- _sparse_finalize_kernel[(triton.cdiv(hidden, 256),)](accumulator, out, counter, hidden, BLOCK=256)
170
- return out
171
-
172
-
173
- @triton_sparse_value.register_fake
174
- def _(activation, weight_t, accumulator, index, value, counter):
175
- return activation.new_empty(weight_t.shape[1])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
special_tokens_map.json ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ {
2
+ "bos_token": "<|rwkv_tokenizer_end_of_text|>",
3
+ "eos_token": "\n\n",
4
+ "unk_token": "<|rwkv_tokenizer_end_of_text|>",
5
+ "pad_token": "<|rwkv_tokenizer_end_of_text|>"
6
+ }
tokenizer_config.json ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "add_prefix_space": false,
3
+ "added_tokens_decoder": {
4
+ "0": {
5
+ "content": "<|rwkv_tokenizer_end_of_text|>",
6
+ "lstrip": false,
7
+ "normalized": false,
8
+ "rstrip": false,
9
+ "single_word": false,
10
+ "special": true
11
+ }
12
+ },
13
+ "auto_map": {
14
+ "AutoTokenizer": [
15
+ "hf_rwkv_tokenizer.RwkvTokenizer",
16
+ null
17
+ ]
18
+ },
19
+ "bos_token": "<|rwkv_tokenizer_end_of_text|>",
20
+ "pad_token": "<|rwkv_tokenizer_end_of_text|>",
21
+ "clean_up_tokenization_spaces": false,
22
+ "eos_token": "\n\n",
23
+ "model_max_length": 1000000000000000019884624838656,
24
+ "tokenizer_class": "RwkvTokenizer",
25
+ "unk_token": "<|rwkv_tokenizer_end_of_text|>",
26
+ "use_fast": false,
27
+ "chat_template": "{{ '<|rwkv_tokenizer_end_of_text|>' }}{% for message in messages %}{% if message['role'] == 'user' %}{{'User: ' + message['content'] + '\n\n'}}{% elif message['role'] == 'system' %}{{'System: ' + message['content'] + '\n\n'}}{% elif message['role'] == 'assistant' %}{{'Assistant: ' + message['content'] + '\n\n'}}{% endif %}{% endfor %}{% if add_generation_prompt %}{{ 'Assistant:' }}{% endif %}"
28
+ }