aabbdev commited on
Commit
5904f9d
·
verified ·
1 Parent(s): cf99f40

Add TRL SFT compatibility

Browse files
Files changed (4) hide show
  1. README.md +38 -0
  2. chat_template.jinja +3 -1
  3. modeling_rwkv7.py +32 -3
  4. release-manifest.json +16 -10
README.md CHANGED
@@ -213,6 +213,44 @@ Reference stops are token ID `0` and `\n\nUser:`.
213
  Strip trailing spaces from user input. The official RWKV prompt guide is available
214
  in [`RWKV7-G1x-templates.txt`](https://github.com/BlinkDL/RWKV-LM/blob/main/RWKV-v7/RWKV7-G1x-templates.txt).
215
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
216
  ## Optimized local inference
217
 
218
  Install the versions listed in `inference/requirements.txt`, then run the bundled
 
213
  Strip trailing spaces from user input. The official RWKV prompt guide is available
214
  in [`RWKV7-G1x-templates.txt`](https://github.com/BlinkDL/RWKV-LM/blob/main/RWKV-v7/RWKV7-G1x-templates.txt).
215
 
216
+ ## Supervised fine-tuning
217
+
218
+ The bundled model supports TRL 1.10+ `SFTTrainer`, including its default
219
+ `chunked_nll`, gradient checkpointing, assistant-only loss, BFD packing, and PEFT
220
+ LoRA. Packing boundaries carried as reset `position_ids` are converted into RWKV
221
+ recurrent-state boundaries. Do not use the boundary-destroying `wrapped` packing
222
+ strategy.
223
+
224
+ ```python
225
+ from datasets import load_dataset
226
+ from peft import LoraConfig
227
+ from trl import SFTConfig, SFTTrainer
228
+
229
+ # Reuse `model` and `tokenizer` loaded in the Transformers quickstart above.
230
+ dataset = load_dataset("trl-lib/Capybara", split="train")
231
+ trainer = SFTTrainer(
232
+ model=model,
233
+ processing_class=tokenizer,
234
+ train_dataset=dataset,
235
+ args=SFTConfig(
236
+ output_dir="rwkv7-sft",
237
+ max_length=2048,
238
+ packing=True,
239
+ packing_strategy="bfd",
240
+ assistant_only_loss=True,
241
+ use_cache=False,
242
+ gradient_checkpointing=True,
243
+ ),
244
+ peft_config=LoraConfig(
245
+ task_type="CAUSAL_LM",
246
+ r=8,
247
+ lora_alpha=16,
248
+ target_modules=["receptance", "key", "value", "output"],
249
+ ),
250
+ )
251
+ trainer.train()
252
+ ```
253
+
254
  ## Optimized local inference
255
 
256
  Install the versions listed in `inference/requirements.txt`, then run the bundled
chat_template.jinja CHANGED
@@ -1,4 +1,4 @@
1
- {%- set add_generation_prompt = add_generation_prompt | default(true) -%}
2
  {%- set thinking = thinking | default(false) -%}
3
  {%- set bos_token = bos_token | default('', true) -%}
4
  {%- set tools = tools | default([], true) -%}
@@ -23,6 +23,7 @@
23
  {%- if message.role == 'user' -%}
24
  {{ 'User: ' ~ (message.content | trim) ~ '\n\n' }}
25
  {%- elif message.role == 'assistant' -%}
 
26
  {%- set content = message.content | default('', true) | trim -%}
27
  {{ 'Assistant:' }}
28
  {%- if message.tool_calls is defined and message.tool_calls | length > 0 -%}
@@ -43,6 +44,7 @@
43
  {{ ' ' ~ content }}
44
  {%- endif -%}
45
  {{ '\n\n' }}
 
46
  {%- elif message.role == 'tool' -%}
47
  {{ 'User: Function output:\n' ~ (message.content | trim) ~ '\n\n' }}
48
  {%- endif -%}
 
1
+ {%- set add_generation_prompt = add_generation_prompt | default(false) -%}
2
  {%- set thinking = thinking | default(false) -%}
3
  {%- set bos_token = bos_token | default('', true) -%}
4
  {%- set tools = tools | default([], true) -%}
 
23
  {%- if message.role == 'user' -%}
24
  {{ 'User: ' ~ (message.content | trim) ~ '\n\n' }}
25
  {%- elif message.role == 'assistant' -%}
26
+ {%- generation -%}
27
  {%- set content = message.content | default('', true) | trim -%}
28
  {{ 'Assistant:' }}
29
  {%- if message.tool_calls is defined and message.tool_calls | length > 0 -%}
 
44
  {{ ' ' ~ content }}
45
  {%- endif -%}
46
  {{ '\n\n' }}
47
+ {%- endgeneration -%}
48
  {%- elif message.role == 'tool' -%}
49
  {{ 'User: Function output:\n' ~ (message.content | trim) ~ '\n\n' }}
50
  {%- endif -%}
modeling_rwkv7.py CHANGED
@@ -407,9 +407,14 @@ class Rwkv7Attention(nn.Module):
407
  # The value-residual LoRA exists on every layer in a reference checkpoint,
408
  # but layer 0 PRODUCES v_first instead of mixing towards it, so its copy is
409
  # never read. It is registered anyway so that loading is lossless.
410
- self.v1 = nn.Parameter(torch.zeros(C, config.v_low_rank_dim))
411
- self.v2 = nn.Parameter(torch.zeros(config.v_low_rank_dim, C))
412
- self.v0 = nn.Parameter(torch.zeros(1, 1, C))
 
 
 
 
 
413
 
414
  self.k_k = nn.Parameter(torch.zeros(1, 1, C))
415
  self.k_a = nn.Parameter(torch.zeros(1, 1, C))
@@ -814,6 +819,7 @@ class Rwkv7Output(ModelOutput):
814
 
815
  last_hidden_state: torch.FloatTensor | None = None
816
  state: Rwkv7Cache | None = None
 
817
  hidden_states: tuple[torch.FloatTensor, ...] | None = None
818
  attentions: None = None
819
 
@@ -906,6 +912,19 @@ class Rwkv7PreTrainedModel(PreTrainedModel):
906
  init.uniform_(module.weight, a=-1e-4, b=1e-4)
907
 
908
 
 
 
 
 
 
 
 
 
 
 
 
 
 
909
  @auto_docstring
910
  class Rwkv7Model(Rwkv7PreTrainedModel):
911
  def __init__(self, config: Rwkv7Config):
@@ -948,6 +967,7 @@ class Rwkv7Model(Rwkv7PreTrainedModel):
948
  self,
949
  input_ids: torch.LongTensor | None = None,
950
  attention_mask: torch.LongTensor | None = None,
 
951
  inputs_embeds: torch.FloatTensor | None = None,
952
  state: Rwkv7Cache | None = None,
953
  cu_seq_lens: torch.LongTensor | None = None,
@@ -1005,6 +1025,11 @@ class Rwkv7Model(Rwkv7PreTrainedModel):
1005
  if inputs_embeds is None:
1006
  inputs_embeds = self.emb(input_ids)
1007
 
 
 
 
 
 
1008
  if use_cache and state is None:
1009
  state = self._empty_state(inputs_embeds.shape[0], inputs_embeds.device, inputs_embeds.dtype)
1010
 
@@ -1137,6 +1162,7 @@ class Rwkv7ForCausalLM(Rwkv7PreTrainedModel, GenerationMixin):
1137
  self,
1138
  input_ids: torch.LongTensor | None = None,
1139
  attention_mask: torch.LongTensor | None = None,
 
1140
  inputs_embeds: torch.FloatTensor | None = None,
1141
  state: Rwkv7Cache | None = None,
1142
  labels: torch.LongTensor | None = None,
@@ -1159,9 +1185,12 @@ class Rwkv7ForCausalLM(Rwkv7PreTrainedModel, GenerationMixin):
1159
  so `generate` declined to pass it and a caller who passed it was quietly
1160
  ignored.
1161
  """
 
 
1162
  outputs = self.rwkv7(
1163
  input_ids=input_ids,
1164
  attention_mask=attention_mask,
 
1165
  inputs_embeds=inputs_embeds,
1166
  state=state,
1167
  use_cache=use_cache,
 
407
  # The value-residual LoRA exists on every layer in a reference checkpoint,
408
  # but layer 0 PRODUCES v_first instead of mixing towards it, so its copy is
409
  # never read. It is registered anyway so that loading is lossless.
410
+ if layer_id == 0:
411
+ self.register_buffer("v1", torch.zeros(C, config.v_low_rank_dim))
412
+ self.register_buffer("v2", torch.zeros(config.v_low_rank_dim, C))
413
+ self.register_buffer("v0", torch.zeros(1, 1, C))
414
+ else:
415
+ self.v1 = nn.Parameter(torch.zeros(C, config.v_low_rank_dim))
416
+ self.v2 = nn.Parameter(torch.zeros(config.v_low_rank_dim, C))
417
+ self.v0 = nn.Parameter(torch.zeros(1, 1, C))
418
 
419
  self.k_k = nn.Parameter(torch.zeros(1, 1, C))
420
  self.k_a = nn.Parameter(torch.zeros(1, 1, C))
 
819
 
820
  last_hidden_state: torch.FloatTensor | None = None
821
  state: Rwkv7Cache | None = None
822
+ past_key_values: None = None
823
  hidden_states: tuple[torch.FloatTensor, ...] | None = None
824
  attentions: None = None
825
 
 
912
  init.uniform_(module.weight, a=-1e-4, b=1e-4)
913
 
914
 
915
+ def _packed_boundaries_from_position_ids(
916
+ position_ids: torch.LongTensor | None, seq_len: int
917
+ ) -> torch.LongTensor | None:
918
+ if position_ids is None or position_ids.ndim != 2 or position_ids.shape[0] != 1:
919
+ return None
920
+ starts = torch.nonzero(position_ids[0] == 0, as_tuple=False).flatten()
921
+ if starts.numel() <= 1:
922
+ return None
923
+ if starts[0].item() != 0:
924
+ raise ValueError("packed position_ids must start at zero")
925
+ return torch.cat((starts, starts.new_tensor([seq_len])))
926
+
927
+
928
  @auto_docstring
929
  class Rwkv7Model(Rwkv7PreTrainedModel):
930
  def __init__(self, config: Rwkv7Config):
 
967
  self,
968
  input_ids: torch.LongTensor | None = None,
969
  attention_mask: torch.LongTensor | None = None,
970
+ position_ids: torch.LongTensor | None = None,
971
  inputs_embeds: torch.FloatTensor | None = None,
972
  state: Rwkv7Cache | None = None,
973
  cu_seq_lens: torch.LongTensor | None = None,
 
1025
  if inputs_embeds is None:
1026
  inputs_embeds = self.emb(input_ids)
1027
 
1028
+ if cu_seq_lens is None and attention_mask is None:
1029
+ cu_seq_lens = _packed_boundaries_from_position_ids(
1030
+ position_ids, inputs_embeds.shape[1]
1031
+ )
1032
+
1033
  if use_cache and state is None:
1034
  state = self._empty_state(inputs_embeds.shape[0], inputs_embeds.device, inputs_embeds.dtype)
1035
 
 
1162
  self,
1163
  input_ids: torch.LongTensor | None = None,
1164
  attention_mask: torch.LongTensor | None = None,
1165
+ position_ids: torch.LongTensor | None = None,
1166
  inputs_embeds: torch.FloatTensor | None = None,
1167
  state: Rwkv7Cache | None = None,
1168
  labels: torch.LongTensor | None = None,
 
1185
  so `generate` declined to pass it and a caller who passed it was quietly
1186
  ignored.
1187
  """
1188
+ if labels is not None and use_cache is None:
1189
+ use_cache = False
1190
  outputs = self.rwkv7(
1191
  input_ids=input_ids,
1192
  attention_mask=attention_mask,
1193
+ position_ids=position_ids,
1194
  inputs_embeds=inputs_embeds,
1195
  state=state,
1196
  use_cache=use_cache,
release-manifest.json CHANGED
@@ -1,7 +1,7 @@
1
  {
2
  "builder": {
3
  "asset_set": "2026.08.06-r1",
4
- "assets_sha256": "5a31129dcca03f5ecd55157e1561af64aaf35046c77e253fc4d6214b95ab8ede",
5
  "version": "0.2.0"
6
  },
7
  "conversion": {
@@ -36,13 +36,13 @@
36
  },
37
  "README.md": {
38
  "role": "model_card",
39
- "sha256": "0f1a8bfe6a0d420333fdf5f7e787e751104348c81c531e3b846a6b0bf7b88d44",
40
- "size_bytes": 10331
41
  },
42
  "chat_template.jinja": {
43
  "role": "tokenizer",
44
- "sha256": "abcd52a871eb3f7f0de7b69f2e0ac974fd7cfb03d4acea7a4609cb441f5d9048",
45
- "size_bytes": 2336
46
  },
47
  "config.json": {
48
  "role": "model_config",
@@ -91,8 +91,8 @@
91
  },
92
  "modeling_rwkv7.py": {
93
  "role": "model_code",
94
- "sha256": "f3a1d9bde755f54887451e8755c6d48866ecc9146610c7d8fa73e0fa68c6734c",
95
- "size_bytes": 57446
96
  },
97
  "tokenizer.json": {
98
  "role": "tokenizer",
@@ -139,7 +139,13 @@
139
  "provenance": "locked-profile"
140
  },
141
  "model_code": {
142
- "format_version": 1,
 
 
 
 
 
 
143
  "source_repository": "https://github.com/huggingface/transformers.git",
144
  "source_revision": "4ad9ed0747ed6ba75c787e8f9040dcd64b166ee2",
145
  "sources": {
@@ -151,7 +157,7 @@
151
  },
152
  "modeling_rwkv7.py": {
153
  "asset_path": "model_code/modeling_rwkv7.py",
154
- "output_sha256": "f3a1d9bde755f54887451e8755c6d48866ecc9146610c7d8fa73e0fa68c6734c",
155
  "repository_path": "src/transformers/models/rwkv7/modeling_rwkv7.py",
156
  "source_sha256": "3e8e5af7c4eba0b5de1496aef44773d7ac1bb4d96756e6f55efaf29453d67952"
157
  }
@@ -320,7 +326,7 @@
320
  }
321
  }
322
  },
323
- "schema_version": 7,
324
  "source": {
325
  "filename": "rwkv7-g1i-1.5b-20260805-ctx16384.pth",
326
  "kind": "huggingface",
 
1
  {
2
  "builder": {
3
  "asset_set": "2026.08.06-r1",
4
+ "assets_sha256": "536d0d0afa96630b2bad11534ebe35434226f83b8999a1c60d76fe37802ff139",
5
  "version": "0.2.0"
6
  },
7
  "conversion": {
 
36
  },
37
  "README.md": {
38
  "role": "model_card",
39
+ "sha256": "fbcb25fd1bb80a23a961c9bc85ff57f606177341e7d24143a5cbcc75b69413da",
40
+ "size_bytes": 11454
41
  },
42
  "chat_template.jinja": {
43
  "role": "tokenizer",
44
+ "sha256": "e0f8e7809caa4ddf6f70f89a96b76974b5d554fd3122f2f0e2023c256c71994e",
45
+ "size_bytes": 2378
46
  },
47
  "config.json": {
48
  "role": "model_config",
 
91
  },
92
  "modeling_rwkv7.py": {
93
  "role": "model_code",
94
+ "sha256": "8b8a3459b40a33424b592abff360485577a8f19419be411e3dceb817a51ebe46",
95
+ "size_bytes": 58685
96
  },
97
  "tokenizer.json": {
98
  "role": "tokenizer",
 
139
  "provenance": "locked-profile"
140
  },
141
  "model_code": {
142
+ "format_version": 2,
143
+ "patches": [
144
+ "layer-zero-value-residual-buffers",
145
+ "trainer-past-key-values-placeholder",
146
+ "trl-position-ids-packing-boundaries",
147
+ "labels-disable-recurrent-cache"
148
+ ],
149
  "source_repository": "https://github.com/huggingface/transformers.git",
150
  "source_revision": "4ad9ed0747ed6ba75c787e8f9040dcd64b166ee2",
151
  "sources": {
 
157
  },
158
  "modeling_rwkv7.py": {
159
  "asset_path": "model_code/modeling_rwkv7.py",
160
+ "output_sha256": "8b8a3459b40a33424b592abff360485577a8f19419be411e3dceb817a51ebe46",
161
  "repository_path": "src/transformers/models/rwkv7/modeling_rwkv7.py",
162
  "source_sha256": "3e8e5af7c4eba0b5de1496aef44773d7ac1bb4d96756e6f55efaf29453d67952"
163
  }
 
326
  }
327
  }
328
  },
329
+ "schema_version": 8,
330
  "source": {
331
  "filename": "rwkv7-g1i-1.5b-20260805-ctx16384.pth",
332
  "kind": "huggingface",