miyuki2026 commited on
Commit
93c8813
·
1 Parent(s): 740e347
examples/tutorials/dpo/ultrachat-sft/step_2_train_sft_model_deepspeed.py CHANGED
@@ -3,7 +3,7 @@
3
  """
4
  https://huggingface.co/docs/trl/v0.16.1/en/sft_trainer
5
 
6
- 多卡 V00 32G 全参微调
7
 
8
  #学术加速
9
  source /etc/network_turbo
@@ -71,11 +71,9 @@ else:
71
 
72
  from datasets import load_dataset
73
  import torch
74
- import transformers
75
  from transformers import AutoModelForCausalLM, AutoTokenizer, DataCollatorForLanguageModeling
76
  from trl import SFTTrainer, SFTConfig, DataCollatorForCompletionOnlyLM
77
 
78
- # transformers.logging.set_verbosity_error()
79
  warnings.filterwarnings("ignore", category=UserWarning, module="transformers")
80
 
81
 
 
3
  """
4
  https://huggingface.co/docs/trl/v0.16.1/en/sft_trainer
5
 
6
+ 4卡 V00 32G 全参微调
7
 
8
  #学术加速
9
  source /etc/network_turbo
 
71
 
72
  from datasets import load_dataset
73
  import torch
 
74
  from transformers import AutoModelForCausalLM, AutoTokenizer, DataCollatorForLanguageModeling
75
  from trl import SFTTrainer, SFTConfig, DataCollatorForCompletionOnlyLM
76
 
 
77
  warnings.filterwarnings("ignore", category=UserWarning, module="transformers")
78
 
79
 
examples/tutorials/dpo/ultrafeedback-dpo/step_2_train_dpo_model_single_gpu.py CHANGED
@@ -185,7 +185,7 @@ def main():
185
 
186
  trainer = DPOTrainer(
187
  model=model,
188
- ref_model=ref_model, # 提供参考模型
189
  args=dpo_config,
190
  train_dataset=train_dataset,
191
  # DPOTrainer 会自动处理数据,不需要 data_collator
 
185
 
186
  trainer = DPOTrainer(
187
  model=model,
188
+ ref_model=ref_model,
189
  args=dpo_config,
190
  train_dataset=train_dataset,
191
  # DPOTrainer 会自动处理数据,不需要 data_collator
examples/tutorials/rlhf/gpt2_sst2_ppo/step_2_train_model_ddp.py CHANGED
@@ -49,6 +49,8 @@ nohup torchrun --nproc_per_node=2 step_2_train_model_ddp.py \
49
 
50
 
51
  ------------
 
 
52
 
53
  """
54
  import argparse
@@ -72,9 +74,7 @@ from transformers import (
72
  GPT2LMHeadModel, GPT2ForSequenceClassification,
73
  DataCollatorWithPadding
74
  )
75
- from trl import PPOConfig
76
-
77
- from toolbox.trl.trainer.ppo_trainer import PPOTrainer
78
 
79
 
80
  def get_args():
 
49
 
50
 
51
  ------------
52
+ 感觉 GPT2,sst2 数据集根本不足以生成好的长句。或许生成短句并得高分才是正确的收敛结果。
53
+
54
 
55
  """
56
  import argparse
 
74
  GPT2LMHeadModel, GPT2ForSequenceClassification,
75
  DataCollatorWithPadding
76
  )
77
+ from trl import PPOConfig, PPOTrainer
 
 
78
 
79
 
80
  def get_args():
examples/tutorials/rlhf/gpt2_sst2_ppo/step_2_train_model_on_cpu.py CHANGED
@@ -39,8 +39,7 @@ else:
39
  project_path = Path(os.path.abspath("../../../../"))
40
  temp_directory = Path("/root/autodl-tmp/OpenMiniMind/temp")
41
 
42
- from trl import PPOConfig
43
- from toolbox.trl.trainer.ppo_trainer import PPOTrainer
44
 
45
 
46
  def get_args():
 
39
  project_path = Path(os.path.abspath("../../../../"))
40
  temp_directory = Path("/root/autodl-tmp/OpenMiniMind/temp")
41
 
42
+ from trl import PPOConfig, PPOTrainer
 
43
 
44
 
45
  def get_args():
toolbox/trl/__init__.py DELETED
@@ -1,5 +0,0 @@
1
- #!/usr/bin/python3
2
- # -*- coding: utf-8 -*-
3
-
4
- if __name__ == "__main__":
5
- pass
 
 
 
 
 
 
toolbox/trl/trainer/__init__.py DELETED
@@ -1,5 +0,0 @@
1
- #!/usr/bin/python3
2
- # -*- coding: utf-8 -*-
3
-
4
- if __name__ == "__main__":
5
- pass
 
 
 
 
 
 
toolbox/trl/trainer/ppo_trainer.py DELETED
@@ -1,809 +0,0 @@
1
- #!/usr/bin/python3
2
- # -*- coding: utf-8 -*-
3
- # Copyright 2025 The HuggingFace Team. All rights reserved.
4
- #
5
- # Licensed under the Apache License, Version 2.0 (the "License");
6
- # you may not use this file except in compliance with the License.
7
- # You may obtain a copy of the License at
8
- #
9
- # http://www.apache.org/licenses/LICENSE-2.0
10
- #
11
- # Unless required by applicable law or agreed to in writing, software
12
- # distributed under the License is distributed on an "AS IS" BASIS,
13
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
- # See the License for the specific language governing permissions and
15
- # limitations under the License.
16
-
17
- import gc
18
- import math
19
- import os
20
- import textwrap
21
- import time
22
- from collections import defaultdict
23
- from contextlib import contextmanager, nullcontext
24
- from typing import Optional, Union
25
-
26
- import numpy as np
27
- import pandas as pd
28
- import torch
29
- import torch.nn as nn
30
- from accelerate import Accelerator
31
- from accelerate.utils import broadcast, gather_object
32
- from datasets import Dataset
33
- from torch.utils.data import DataLoader
34
- from transformers import (
35
- BaseImageProcessor,
36
- DataCollatorWithPadding,
37
- FeatureExtractionMixin,
38
- GenerationConfig,
39
- PreTrainedTokenizerBase,
40
- ProcessorMixin,
41
- Trainer,
42
- TrainerCallback,
43
- TrainerControl,
44
- is_wandb_available,
45
- )
46
- from transformers.integrations import get_reporting_integration_callbacks
47
- from transformers.trainer import DEFAULT_CALLBACKS, DEFAULT_PROGRESS_CALLBACK
48
- from transformers.trainer_callback import CallbackHandler, ExportableState, PrinterCallback
49
- from transformers.utils import is_peft_available
50
-
51
- from trl.core import masked_mean, masked_whiten
52
- from trl.models import create_reference_model
53
- from trl.models.utils import unwrap_model_for_generation
54
- from trl.trainer.ppo_config import PPOConfig
55
- from trl.trainer.utils import (
56
- OnlineTrainerState,
57
- batch_generation,
58
- disable_dropout_in_model,
59
- exact_div,
60
- first_true_indices,
61
- forward,
62
- generate_model_card,
63
- get_comet_experiment_url,
64
- get_reward,
65
- log_table_to_comet_experiment,
66
- peft_module_casting_to_bf16,
67
- prepare_deepspeed,
68
- print_rich_table,
69
- selective_log_softmax,
70
- truncate_response,
71
- )
72
-
73
-
74
- if is_peft_available():
75
- from peft import PeftConfig, PeftModel, get_peft_model
76
-
77
- if is_wandb_available():
78
- import wandb
79
-
80
-
81
- INVALID_LOGPROB = 1.0
82
-
83
-
84
- # taken from https://github.com/OpenLMLab/MOSS-RLHF/blob/40b91eb2f2b71b16919addede0341d2bef70825d/ppo/ppo_trainer.py#L29
85
- # we did this we can do a single `model = accelerator.prepare(model)`
86
- class PolicyAndValueWrapper(nn.Module):
87
- def __init__(self, policy, value_model) -> None:
88
- super().__init__()
89
- self.policy = policy
90
- self.value_model = value_model
91
- self.critic_backbone = getattr(value_model, value_model.base_model_prefix)
92
-
93
- def forward(self, **kwargs):
94
- output = self.critic_backbone(**kwargs)
95
- logits = self.value_model.score(output.hidden_states[-1])
96
- return self.policy(**kwargs), logits
97
-
98
-
99
- class PPOTrainer(Trainer):
100
- _tag_names = ["trl", "ppo"]
101
-
102
- def __init__(
103
- self,
104
- args: PPOConfig,
105
- processing_class: Optional[
106
- Union[PreTrainedTokenizerBase, BaseImageProcessor, FeatureExtractionMixin, ProcessorMixin]
107
- ],
108
- model: nn.Module,
109
- ref_model: Optional[nn.Module],
110
- reward_model: nn.Module,
111
- train_dataset: Dataset,
112
- value_model: Optional[nn.Module] = None,
113
- data_collator: Optional[DataCollatorWithPadding] = None,
114
- eval_dataset: Optional[Union[Dataset, dict[str, Dataset]]] = None,
115
- # less commonly used
116
- optimizers: tuple[torch.optim.Optimizer, torch.optim.lr_scheduler.LambdaLR] = (None, None),
117
- callbacks: Optional[list[TrainerCallback]] = None,
118
- peft_config: Optional["PeftConfig"] = None,
119
- ) -> None:
120
- if ref_model is model:
121
- raise ValueError(
122
- "`model` and `ref_model` cannot be the same object. If you want `ref_model` to be the "
123
- "same as `model`, you must make a copy of it, or `None` if you use peft."
124
- )
125
-
126
- self.args = args
127
- self.processing_class = processing_class
128
- self.policy_model = model
129
-
130
- # Define the collator if not provided
131
- if data_collator is None:
132
- data_collator = DataCollatorWithPadding(self.processing_class)
133
-
134
- # Handle stop token settings: update policy model's generation_config to use provided stop token
135
- if args.stop_token and args.stop_token_id:
136
- raise ValueError("You cannot set both `stop_token` and `stop_token_id`.")
137
- elif args.stop_token:
138
- if args.stop_token == "eos":
139
- self.policy_model.generation_config.eos_token_id = self.stop_token_id = processing_class.eos_token_id
140
- else:
141
- raise ValueError(
142
- f"Unknown `stop_token` {args.stop_token}. Allowed values are: `'eos'` and `None` (no stop token)."
143
- )
144
- else:
145
- self.policy_model.generation_config.eos_token_id = self.stop_token_id = args.stop_token_id # None or int
146
-
147
- # peft support
148
- if not is_peft_available() and peft_config is not None:
149
- raise ImportError(
150
- "PEFT is not installed and you passed a `peft_config` in the trainer's kwargs, please install it to use the PEFT models"
151
- )
152
- elif is_peft_available() and peft_config is not None:
153
- # if model is a peft model and we have a peft_confg, we merge and unload it first
154
- if isinstance(self.policy_model, PeftModel):
155
- self.policy_model = self.policy_model.merge_and_unload()
156
-
157
- # get peft model with the given config
158
- self.policy_model = get_peft_model(self.policy_model, peft_config)
159
- if args.bf16 and getattr(self.policy_model, "is_loaded_in_4bit", False):
160
- peft_module_casting_to_bf16(self.policy_model)
161
-
162
- self.is_peft_model = is_peft_available() and isinstance(self.policy_model, PeftModel)
163
- self.model_adapter_name = args.model_adapter_name
164
- self.ref_adapter_name = args.ref_adapter_name
165
-
166
- if ref_model:
167
- self.ref_model = ref_model
168
- elif self.is_peft_model:
169
- self.ref_model = None
170
- else:
171
- self.ref_model = create_reference_model(self.policy_model)
172
-
173
- self.reward_model = reward_model
174
- self.train_dataset = train_dataset
175
- self.train_dataset_len = len(train_dataset)
176
- self.value_model = value_model
177
- self.data_collator = data_collator
178
- self.eval_dataset = eval_dataset
179
- self.optimizer, self.lr_scheduler = optimizers
180
- self.optimizer_cls_and_kwargs = None # needed for transformers >= 4.47
181
-
182
- #########
183
- # calculate various batch sizes
184
- #########
185
- if args.total_episodes is None: # allow the users to define episodes in terms of epochs.
186
- args.total_episodes = int(args.num_train_epochs * self.train_dataset_len)
187
- accelerator = Accelerator(gradient_accumulation_steps=args.gradient_accumulation_steps)
188
- self.accelerator = accelerator
189
- args.world_size = accelerator.num_processes
190
- args.local_batch_size = (
191
- args.per_device_train_batch_size * args.gradient_accumulation_steps * args.num_mini_batches
192
- )
193
- args.micro_batch_size = int(args.per_device_train_batch_size * args.world_size)
194
- args.batch_size = int(args.local_batch_size * args.world_size)
195
- args.mini_batch_size = exact_div(
196
- args.batch_size, args.num_mini_batches, "`batch_size` must be a multiple of `num_mini_batches`"
197
- )
198
- args.local_mini_batch_size = exact_div(
199
- args.local_batch_size, args.num_mini_batches, "`local_batch_size` must be a multiple of `num_mini_batches`"
200
- )
201
- if args.whiten_rewards:
202
- assert args.local_mini_batch_size >= 8, (
203
- f"Per-rank minibatch size {args.local_mini_batch_size} is insufficient for whitening"
204
- )
205
- # `per_rank_rollout_batch_size` is our `args.local_batch_size`
206
- # `per_rank_minibatch_size` is our `args.local_mini_batch_size`
207
- args.num_total_batches = math.ceil(
208
- args.total_episodes / args.batch_size
209
- ) # we may train for more than `total_episodes`
210
- time_tensor = torch.tensor(int(time.time()), device=accelerator.device)
211
- time_int = broadcast(time_tensor, 0).item() # avoid different timestamps across processes
212
- args.run_name = f"{args.exp_name}__{args.seed}__{time_int}"
213
- self.local_seed = args.seed + accelerator.process_index * 100003 # Prime
214
- if args.num_sample_generations > 0:
215
- self.sample_generations_freq = max(1, args.num_total_batches // args.num_sample_generations)
216
- self.local_dataloader_batch_size = args.local_batch_size
217
-
218
- #########
219
- # setup model, optimizer, and others
220
- #########
221
- for module in [self.policy_model, self.ref_model, self.value_model, self.reward_model]:
222
- if module is not None:
223
- disable_dropout_in_model(module)
224
- self.model = PolicyAndValueWrapper(self.policy_model, self.value_model)
225
- self.model.config = self.policy_model.config # needed for pushing to hub
226
- self.create_optimizer_and_scheduler(
227
- num_training_steps=args.num_total_batches
228
- ) # note that we are calling `self.lr_scheduler.step()` manually only at the batch level
229
-
230
- #########
231
- ### trainer specifics
232
- #########
233
- default_callbacks = DEFAULT_CALLBACKS + get_reporting_integration_callbacks(self.args.report_to)
234
- self.callbacks = default_callbacks if callbacks is None else default_callbacks + callbacks
235
- self.callback_handler = CallbackHandler(
236
- self.callbacks, self.model, self.processing_class, self.optimizer, self.lr_scheduler
237
- )
238
- self.add_callback(PrinterCallback if self.args.disable_tqdm else DEFAULT_PROGRESS_CALLBACK)
239
- self.control = TrainerControl()
240
- self.state = OnlineTrainerState(
241
- is_local_process_zero=self.is_local_process_zero(),
242
- is_world_process_zero=self.is_world_process_zero(),
243
- stateful_callbacks=[
244
- cb for cb in self.callback_handler.callbacks + [self.control] if isinstance(cb, ExportableState)
245
- ],
246
- )
247
- self.current_flos = 0
248
- self.hp_search_backend = None
249
- self.is_deepspeed_enabled = getattr(self.accelerator.state, "deepspeed_plugin", None) is not None
250
- self.is_fsdp_enabled = getattr(self.accelerator.state, "fsdp_plugin", None) is not None
251
- # Create distant repo and output directory if needed
252
- self.hub_model_id = None
253
- if self.args.push_to_hub:
254
- self.init_hf_repo()
255
- if self.args.should_save:
256
- os.makedirs(self.args.output_dir, exist_ok=True)
257
-
258
- # Add tags for models that have been loaded with the correct transformers version
259
- if hasattr(self.model, "add_model_tags"):
260
- self.model.add_model_tags(self._tag_names)
261
-
262
- #########
263
- ### setup dataloader
264
- #########
265
- self.dataloader = DataLoader(
266
- self.train_dataset,
267
- batch_size=self.local_dataloader_batch_size,
268
- shuffle=True,
269
- collate_fn=self.data_collator,
270
- drop_last=True, # needed; otherwise the last batch will be of ragged shape
271
- )
272
- # sync random states for DataLoader(shuffle=True) before `accelerator.prepare`
273
- # see https://gist.github.com/vwxyzjn/2581bff1e48e185e0b85b6dfe1def79c
274
- torch.manual_seed(args.seed)
275
- self.model, self.optimizer, self.dataloader = accelerator.prepare(self.model, self.optimizer, self.dataloader)
276
- torch.manual_seed(self.local_seed) # reset the local seed again
277
-
278
- self.eval_dataloader = DataLoader(
279
- self.eval_dataset,
280
- batch_size=args.per_device_eval_batch_size,
281
- collate_fn=self.data_collator,
282
- drop_last=True,
283
- ) # no need to shuffle eval dataset
284
- self.eval_dataloader = accelerator.prepare(self.eval_dataloader)
285
-
286
- if self.is_deepspeed_enabled:
287
- self.reward_model = prepare_deepspeed(
288
- self.reward_model, args.per_device_train_batch_size, args.fp16, args.bf16
289
- )
290
-
291
- if self.ref_model is None:
292
- if not self.is_peft_model:
293
- raise ValueError("No reference model and model is not a Peft model.")
294
- else:
295
- self.ref_model = prepare_deepspeed(
296
- self.ref_model, args.per_device_train_batch_size, args.fp16, args.bf16
297
- )
298
- else:
299
- if self.ref_model is None:
300
- if not self.is_peft_model:
301
- raise ValueError("No reference model and model is not a Peft model.")
302
- else:
303
- self.ref_model = self.ref_model.to(self.accelerator.device)
304
- self.reward_model = self.reward_model.to(self.accelerator.device)
305
-
306
- def get_train_dataloader(self) -> DataLoader:
307
- return self.dataloader
308
-
309
- def get_eval_dataloader(self) -> DataLoader:
310
- return self.eval_dataloader
311
-
312
- @contextmanager
313
- def null_ref_context(self):
314
- """Context manager for handling null reference model (that is, peft adapter manipulation)."""
315
- with (
316
- self.accelerator.unwrap_model(self.model.policy).disable_adapter()
317
- if self.is_peft_model and not self.ref_adapter_name
318
- else nullcontext()
319
- ):
320
- if self.ref_adapter_name:
321
- self.model.policy.set_adapter(self.ref_adapter_name)
322
- yield
323
- if self.ref_adapter_name:
324
- self.model.policy.set_adapter(self.model_adapter_name or "default")
325
-
326
- def save_model(self, output_dir: Optional[str] = None, _internal_call: bool = False):
327
- backup_model = self.model
328
- if isinstance(self.model, torch.nn.parallel.distributed.DistributedDataParallel):
329
- self.model = self.model.module.policy # save only the policy
330
- else:
331
- self.model = self.model.policy # save only the policy
332
-
333
- if self.is_deepspeed_enabled:
334
- backup_deepspeed = self.deepspeed
335
- self.deepspeed = self.model
336
-
337
- super().save_model(output_dir, _internal_call)
338
-
339
- self.model = backup_model
340
-
341
- if self.is_deepspeed_enabled:
342
- self.deepspeed = backup_deepspeed
343
-
344
- def train(self):
345
- args = self.args
346
- accelerator = self.accelerator
347
- optimizer = self.optimizer
348
- model = self.model
349
- ref_policy = self.ref_model
350
- reward_model = self.reward_model
351
- processing_class = self.processing_class
352
- dataloader = self.dataloader
353
- device = accelerator.device
354
-
355
- def repeat_generator():
356
- while True:
357
- yield from dataloader
358
-
359
- iter_dataloader = iter(repeat_generator())
360
- generation_config = GenerationConfig(
361
- min_new_tokens=self.args.response_length // 2,
362
- max_new_tokens=args.response_length,
363
- temperature=(args.temperature + 1e-7),
364
- top_k=0.0,
365
- top_p=1.0,
366
- do_sample=True,
367
- # pad_token_id=processing_class.pad_token_id, eos_token_id=processing_class.eos_token_id,
368
- # repetition_penalty=3.0,
369
- )
370
-
371
- accelerator.print("===training policy===")
372
- start_time = time.time()
373
- stats_shape = (args.num_ppo_epochs, args.num_mini_batches, args.gradient_accumulation_steps)
374
- approxkl_stats = torch.zeros(stats_shape, device=device)
375
- pg_clipfrac_stats = torch.zeros(stats_shape, device=device)
376
- pg_loss_stats = torch.zeros(stats_shape, device=device)
377
- vf_loss_stats = torch.zeros(stats_shape, device=device)
378
- vf_clipfrac_stats = torch.zeros(stats_shape, device=device)
379
- entropy_stats = torch.zeros(stats_shape, device=device)
380
- ratio_stats = torch.zeros(stats_shape, device=device)
381
- model.train()
382
-
383
- # trainer state initialization
384
- self.state.global_step = 0
385
- self.state.episode = 0
386
- self.state.max_steps = args.num_total_batches * args.num_mini_batches
387
- self.state.num_train_epochs = args.total_episodes / self.train_dataset_len
388
- # Compute absolute values for logging, eval, and save if given as ratio
389
- if args.logging_steps is not None:
390
- if args.logging_steps < 1:
391
- self.state.logging_steps = math.ceil(self.state.max_steps * args.logging_steps)
392
- else:
393
- self.state.logging_steps = args.logging_steps
394
- if args.eval_steps is not None:
395
- if args.eval_steps < 1:
396
- self.state.eval_steps = math.ceil(self.state.max_steps * args.eval_steps)
397
- else:
398
- self.state.eval_steps = args.eval_steps
399
- if args.save_steps is not None:
400
- if args.save_steps < 1:
401
- self.state.save_steps = math.ceil(self.state.max_steps * args.save_steps)
402
- else:
403
- self.state.save_steps = args.save_steps
404
- self.control = self.callback_handler.on_train_begin(args, self.state, self.control)
405
-
406
- # backward compatibility
407
- if self.is_deepspeed_enabled:
408
- self.deepspeed = self.model
409
- self.model_wrapped = self.model
410
-
411
- for update in range(1, args.num_total_batches + 1):
412
- self.state.episode += 1 * args.batch_size
413
- data = next(iter_dataloader)
414
- with torch.no_grad():
415
- queries = data["input_ids"].to(device)
416
- context_length = queries.shape[1]
417
- responses = []
418
- postprocessed_responses = []
419
- logprobs = []
420
- ref_logprobs = []
421
- scores = []
422
- sequence_lengths = []
423
- values = []
424
- with unwrap_model_for_generation(
425
- self.model, self.accelerator, gather_deepspeed3_params=self.args.ds3_gather_for_generation
426
- ) as unwrapped_model:
427
- query_responses, logitss = batch_generation(
428
- unwrapped_model.policy,
429
- queries,
430
- args.local_rollout_forward_batch_size,
431
- processing_class.pad_token_id,
432
- generation_config,
433
- )
434
-
435
- for i in range(0, queries.shape[0], args.local_rollout_forward_batch_size):
436
- query = queries[i : i + args.local_rollout_forward_batch_size]
437
- query_response = query_responses[i : i + args.local_rollout_forward_batch_size]
438
- response = query_response[:, context_length:]
439
- logits = logitss[i : i + args.local_rollout_forward_batch_size]
440
- logprob = selective_log_softmax(logits, response)
441
- del logits
442
- torch.cuda.empty_cache()
443
-
444
- if ref_policy is None:
445
- with self.null_ref_context():
446
- ref_output = forward(model.policy, query_response, processing_class.pad_token_id)
447
- else:
448
- ref_output = forward(ref_policy, query_response, processing_class.pad_token_id)
449
- ref_logits = ref_output.logits[:, context_length - 1 : -1]
450
- ref_logits /= args.temperature + 1e-7
451
- ref_logprob = selective_log_softmax(ref_logits, response)
452
- del ref_output, ref_logits
453
- torch.cuda.empty_cache()
454
-
455
- # Response Processing 1. truncate response after the first occurrence of `stop_token_id`
456
- postprocessed_response = response
457
- if self.stop_token_id is not None: # handle the edge case when stop_token_id exists but is 0
458
- postprocessed_response = truncate_response(
459
- self.stop_token_id, processing_class.pad_token_id, response
460
- )
461
-
462
- # Response Processing 2. run reward model on the truncated responses
463
- postprocessed_query_response = torch.cat((query, postprocessed_response), 1)
464
- sequence_length = first_true_indices(postprocessed_response == processing_class.pad_token_id) - 1
465
- unwrapped_value_model = accelerator.unwrap_model(model).value_model
466
- full_value, _, _ = get_reward(
467
- unwrapped_value_model, query_response, processing_class.pad_token_id, context_length
468
- )
469
- value = full_value[:, context_length - 1 : -1].squeeze(-1)
470
- _, score, _ = get_reward(
471
- reward_model, postprocessed_query_response, processing_class.pad_token_id, context_length
472
- )
473
-
474
- responses.append(response)
475
- postprocessed_responses.append(postprocessed_response)
476
- logprobs.append(logprob)
477
- ref_logprobs.append(ref_logprob)
478
- sequence_lengths.append(sequence_length)
479
- scores.append(score)
480
- values.append(value)
481
- responses = torch.cat(responses, 0)
482
- postprocessed_responses = torch.cat(postprocessed_responses, 0)
483
- logprobs = torch.cat(logprobs, 0)
484
- ref_logprobs = torch.cat(ref_logprobs, 0)
485
- sequence_lengths = torch.cat(sequence_lengths, 0)
486
- scores = torch.cat(scores, 0)
487
- values = torch.cat(values, 0)
488
- del (logprob, ref_logprob, full_value, value, score, unwrapped_model)
489
- torch.cuda.empty_cache()
490
- gc.collect()
491
-
492
- # Response Processing 3. Filter completion. Ensure that the sample contains stop_token_id
493
- # Completions not passing that filter will receive a lower score.
494
- contain_eos_token = torch.any(postprocessed_responses == self.processing_class.eos_token_id, dim=-1)
495
- if self.args.missing_eos_penalty is not None:
496
- scores[~contain_eos_token] -= self.args.missing_eos_penalty
497
- # accelerator.print(f"{scores=}, {(contain_eos_token.sum() / len(contain_eos_token))=}")
498
-
499
- # be very careful with `padding_mask_p1`; see https://excalidraw.com/#json=LWnzG4w2k5DjF_EOL_xPt,e2w3a-hFJ_gX5vOfeyXGTw
500
- response_idxs = torch.arange(responses.shape[1], device=responses.device).repeat(responses.shape[0], 1)
501
- padding_mask = response_idxs > sequence_lengths.unsqueeze(1)
502
- logprobs = torch.masked_fill(logprobs, padding_mask, INVALID_LOGPROB)
503
- ref_logprobs = torch.masked_fill(ref_logprobs, padding_mask, INVALID_LOGPROB)
504
- sequence_lengths_p1 = sequence_lengths + 1
505
- padding_mask_p1 = response_idxs > (sequence_lengths_p1.unsqueeze(1))
506
- values = torch.masked_fill(values, padding_mask_p1, 0)
507
-
508
- # 4. compute rewards
509
- kl = logprobs - ref_logprobs
510
- non_score_reward = -args.kl_coef * kl
511
- rewards = non_score_reward.clone()
512
- actual_start = torch.arange(rewards.size(0), device=rewards.device)
513
- actual_end = torch.where(sequence_lengths_p1 < rewards.size(1), sequence_lengths_p1, sequence_lengths)
514
- rewards[[actual_start, actual_end]] += scores
515
-
516
- # 5. whiten rewards
517
- if args.whiten_rewards:
518
- rewards = masked_whiten(rewards, mask=~padding_mask_p1, shift_mean=False)
519
- rewards = torch.masked_fill(rewards, padding_mask_p1, 0)
520
-
521
- # 6. compute advantages and returns
522
- lastgaelam = 0
523
- advantages_reversed = []
524
- gen_length = responses.shape[1]
525
- for t in reversed(range(gen_length)):
526
- nextvalues = values[:, t + 1] if t < gen_length - 1 else 0.0
527
- delta = rewards[:, t] + args.gamma * nextvalues - values[:, t]
528
- lastgaelam = delta + args.gamma * args.lam * lastgaelam
529
- advantages_reversed.append(lastgaelam)
530
- advantages = torch.stack(advantages_reversed[::-1], axis=1)
531
- returns = advantages + values
532
- advantages = masked_whiten(advantages, ~padding_mask)
533
- advantages = torch.masked_fill(advantages, padding_mask, 0)
534
- torch.cuda.empty_cache()
535
-
536
- # Do multiple epochs of PPO training, with a fresh random shuffle in each epoch
537
- for ppo_epoch_idx in range(args.num_ppo_epochs):
538
- b_inds = np.random.permutation(args.local_batch_size)
539
- minibatch_idx = 0
540
- for mini_batch_start in range(0, args.local_batch_size, args.local_mini_batch_size):
541
- mini_batch_end = mini_batch_start + args.local_mini_batch_size
542
- mini_batch_inds = b_inds[mini_batch_start:mini_batch_end]
543
- gradient_accumulation_idx = 0
544
- for micro_batch_start in range(0, args.local_mini_batch_size, args.per_device_train_batch_size):
545
- with accelerator.accumulate(model):
546
- micro_batch_end = micro_batch_start + args.per_device_train_batch_size
547
- micro_batch_inds = mini_batch_inds[micro_batch_start:micro_batch_end]
548
- mb_advantage = advantages[micro_batch_inds]
549
- mb_responses = responses[micro_batch_inds]
550
- mb_query_responses = query_responses[micro_batch_inds]
551
- mb_logprobs = logprobs[micro_batch_inds]
552
- mb_return = returns[micro_batch_inds]
553
- mb_values = values[micro_batch_inds]
554
-
555
- output, vpred_temp = forward(model, mb_query_responses, processing_class.pad_token_id)
556
- logits = output.logits[:, context_length - 1 : -1]
557
- logits /= args.temperature + 1e-7
558
- new_logprobs = selective_log_softmax(logits, mb_responses)
559
- new_logprobs = torch.masked_fill(
560
- new_logprobs, padding_mask[micro_batch_inds], INVALID_LOGPROB
561
- )
562
- vpred = vpred_temp[:, context_length - 1 : -1].squeeze(-1)
563
- vpred = torch.masked_fill(vpred, padding_mask_p1[micro_batch_inds], 0)
564
- vpredclipped = torch.clamp(
565
- vpred,
566
- mb_values - args.cliprange_value,
567
- mb_values + args.cliprange_value,
568
- )
569
- vf_losses1 = torch.square(vpred - mb_return)
570
- vf_losses2 = torch.square(vpredclipped - mb_return)
571
- vf_loss_max = torch.max(vf_losses1, vf_losses2)
572
- vf_loss = 0.5 * masked_mean(vf_loss_max, ~padding_mask_p1[micro_batch_inds])
573
- vf_clipfrac = masked_mean(
574
- (vf_losses2 > vf_losses1).float(), ~padding_mask_p1[micro_batch_inds]
575
- )
576
- logprobs_diff = new_logprobs - mb_logprobs
577
- ratio = torch.exp(logprobs_diff)
578
- pg_losses = -mb_advantage * ratio
579
- pg_losses2 = -mb_advantage * torch.clamp(ratio, 1.0 - args.cliprange, 1.0 + args.cliprange)
580
- pg_loss_max = torch.max(pg_losses, pg_losses2)
581
- pg_loss = masked_mean(pg_loss_max, ~padding_mask[micro_batch_inds])
582
- loss = pg_loss + args.vf_coef * vf_loss
583
- accelerator.backward(loss)
584
- optimizer.step()
585
- optimizer.zero_grad()
586
- with torch.no_grad():
587
- pg_clipfrac = masked_mean(
588
- (pg_losses2 > pg_losses).float(), ~padding_mask[micro_batch_inds]
589
- )
590
- prob_dist = torch.nn.functional.softmax(logits, dim=-1)
591
- entropy = torch.logsumexp(logits, dim=-1) - torch.sum(prob_dist * logits, dim=-1)
592
- approxkl = 0.5 * (logprobs_diff**2).mean()
593
- approxkl_stats[ppo_epoch_idx, minibatch_idx, gradient_accumulation_idx] = approxkl
594
- pg_clipfrac_stats[ppo_epoch_idx, minibatch_idx, gradient_accumulation_idx] = (
595
- pg_clipfrac
596
- )
597
- pg_loss_stats[ppo_epoch_idx, minibatch_idx, gradient_accumulation_idx] = pg_loss
598
- vf_loss_stats[ppo_epoch_idx, minibatch_idx, gradient_accumulation_idx] = vf_loss
599
- vf_clipfrac_stats[ppo_epoch_idx, minibatch_idx, gradient_accumulation_idx] = (
600
- vf_clipfrac
601
- )
602
- entropy_stats[ppo_epoch_idx, minibatch_idx, gradient_accumulation_idx] = entropy.mean()
603
- ratio_stats[ppo_epoch_idx, minibatch_idx, gradient_accumulation_idx] = ratio.mean()
604
- gradient_accumulation_idx += 1
605
- minibatch_idx += 1
606
- # del everything and empty cache
607
- # fmt: off
608
- del (
609
- output, vpred_temp, logits, new_logprobs, vpred, vpredclipped,
610
- vf_losses1, vf_losses2, vf_loss, vf_clipfrac, logprobs_diff, ratio, pg_losses, pg_losses2, pg_loss_max,
611
- pg_loss, loss, pg_clipfrac, prob_dist, entropy, approxkl, mb_return,
612
- mb_advantage, mb_values, mb_responses, mb_query_responses, mb_logprobs,
613
- )
614
- # fmt: on
615
- torch.cuda.empty_cache()
616
- with torch.no_grad():
617
- mean_kl = kl.sum(1).mean()
618
- mean_entropy = (-logprobs).sum(1).mean()
619
- mean_non_score_reward = non_score_reward.sum(1).mean()
620
- rlhf_reward = mean_non_score_reward + scores.mean()
621
- eps = int(self.state.episode / (time.time() - start_time))
622
- metrics = {}
623
- metrics["eps"] = eps
624
- metrics["objective/kl"] = self.accelerator.gather_for_metrics(mean_kl).mean().item()
625
- metrics["objective/entropy"] = self.accelerator.gather_for_metrics(mean_entropy).mean().item()
626
- metrics["objective/non_score_reward"] = (
627
- self.accelerator.gather_for_metrics(mean_non_score_reward).mean().item()
628
- )
629
- metrics["objective/rlhf_reward"] = self.accelerator.gather_for_metrics(rlhf_reward).mean().item()
630
- metrics["objective/scores"] = self.accelerator.gather_for_metrics(scores.mean()).mean().item()
631
- metrics["policy/approxkl_avg"] = self.accelerator.gather_for_metrics(approxkl_stats).mean().item()
632
- metrics["policy/clipfrac_avg"] = self.accelerator.gather_for_metrics(pg_clipfrac_stats).mean().item()
633
- metrics["loss/policy_avg"] = self.accelerator.gather_for_metrics(pg_loss_stats).mean().item()
634
- metrics["loss/value_avg"] = self.accelerator.gather_for_metrics(vf_loss_stats).mean().item()
635
- metrics["val/clipfrac_avg"] = self.accelerator.gather_for_metrics(vf_clipfrac_stats).mean().item()
636
- metrics["policy/entropy_avg"] = self.accelerator.gather_for_metrics(entropy_stats).mean().item()
637
- metrics["val/ratio"] = self.accelerator.gather_for_metrics(ratio_stats).mean().item()
638
- metrics["val/ratio_var"] = self.accelerator.gather_for_metrics(ratio_stats).var().item()
639
- metrics["val/num_eos_tokens"] = (responses == processing_class.eos_token_id).sum().item()
640
- metrics["lr"] = self.lr_scheduler.get_last_lr()[0]
641
- metrics["episode"] = self.state.episode
642
- self.state.epoch = self.state.episode / self.train_dataset_len # used by self.log
643
- self.state.global_step += 1
644
- self.log(metrics)
645
-
646
- self.lr_scheduler.step()
647
- self.control = self.callback_handler.on_step_end(args, self.state, self.control)
648
- if self.control.should_save:
649
- self._save_checkpoint(model, trial=None)
650
- self.control = self.callback_handler.on_save(self.args, self.state, self.control)
651
- del kl, mean_kl, mean_entropy, mean_non_score_reward, scores, metrics, non_score_reward
652
- torch.cuda.empty_cache()
653
- gc.collect()
654
-
655
- if args.num_sample_generations > 0 and (update - 1) % self.sample_generations_freq == 0:
656
- self.generate_completions(sampling=True)
657
- torch.cuda.empty_cache()
658
- del (
659
- query_responses,
660
- responses,
661
- postprocessed_responses,
662
- logprobs,
663
- ref_logprobs,
664
- values,
665
- sequence_lengths,
666
- contain_eos_token,
667
- sequence_lengths_p1,
668
- response_idxs,
669
- padding_mask,
670
- padding_mask_p1,
671
- rewards,
672
- actual_start,
673
- actual_end,
674
- advantages,
675
- returns,
676
- )
677
- torch.cuda.empty_cache()
678
-
679
- # HF trainer specifics
680
- self.control = self.callback_handler.on_train_end(args, self.state, self.control)
681
- if self.control.should_save:
682
- self._save_checkpoint(model, trial=None, metrics=None)
683
- self.control = self.callback_handler.on_save(self.args, self.state, self.control)
684
-
685
- def generate_completions(self, sampling: bool = False):
686
- args = self.args
687
- processing_class = self.processing_class
688
- generation_config = GenerationConfig(
689
- min_new_tokens=self.args.response_length // 2,
690
- max_new_tokens=self.args.response_length,
691
- temperature=(0.01 + 1e-7),
692
- top_k=0.0,
693
- top_p=1.0,
694
- do_sample=True,
695
- # pad_token_id=processing_class.pad_token_id, eos_token_id=processing_class.eos_token_id,
696
- # repetition_penalty=3.0,
697
- )
698
-
699
- table = defaultdict(list)
700
- with unwrap_model_for_generation(
701
- self.model, self.accelerator, gather_deepspeed3_params=self.args.ds3_gather_for_generation
702
- ) as unwrapped_model:
703
- for batch in self.eval_dataloader:
704
- query = batch["input_ids"]
705
- with torch.no_grad():
706
- context_length = query.shape[1]
707
- query_response, _ = batch_generation(
708
- unwrapped_model.policy,
709
- query,
710
- query.shape[0],
711
- processing_class.pad_token_id,
712
- generation_config,
713
- )
714
- response = query_response[:, context_length:]
715
- postprocessed_response = response
716
- if self.stop_token_id is not None: # handle the edge case when stop_token_id exists but is 0
717
- postprocessed_response = truncate_response(
718
- self.stop_token_id, processing_class.pad_token_id, response
719
- )
720
- table["query"].extend(
721
- gather_object(processing_class.batch_decode(query, skip_special_tokens=True))
722
- )
723
- table["model response"].extend(
724
- gather_object(processing_class.batch_decode(postprocessed_response))
725
- )
726
-
727
- postprocessed_query_response = torch.cat((query, postprocessed_response), 1)
728
- _, score, _ = get_reward(
729
- self.reward_model, postprocessed_query_response, processing_class.pad_token_id, context_length
730
- )
731
- table["score"].extend(self.accelerator.gather_for_metrics(score).float().cpu().numpy())
732
-
733
- if sampling:
734
- break
735
- df = pd.DataFrame(table)
736
-
737
- if self.accelerator.is_main_process:
738
- print_rich_table(df.iloc[0 : 0 + 5])
739
- if "wandb" in args.report_to:
740
- import wandb
741
-
742
- if wandb.run is not None:
743
- wandb.log({"completions": wandb.Table(dataframe=df)})
744
-
745
- if "comet_ml" in args.report_to:
746
- log_table_to_comet_experiment(
747
- name="completions.csv",
748
- table=df,
749
- )
750
-
751
- def create_model_card(
752
- self,
753
- model_name: Optional[str] = None,
754
- dataset_name: Optional[str] = None,
755
- tags: Union[str, list[str], None] = None,
756
- ):
757
- """
758
- Creates a draft of a model card using the information available to the `Trainer`.
759
-
760
- Args:
761
- model_name (`str` or `None`, *optional*, defaults to `None`):
762
- Name of the model.
763
- dataset_name (`str` or `None`, *optional*, defaults to `None`):
764
- Name of the dataset used for training.
765
- tags (`str`, `list[str]` or `None`, *optional*, defaults to `None`):
766
- Tags to be associated with the model card.
767
- """
768
- if not self.is_world_process_zero():
769
- return
770
-
771
- if hasattr(self.model.config, "_name_or_path") and not os.path.isdir(self.model.config._name_or_path):
772
- base_model = self.model.config._name_or_path
773
- else:
774
- base_model = None
775
-
776
- tags = tags or []
777
- if isinstance(tags, str):
778
- tags = [tags]
779
-
780
- if hasattr(self.model.config, "unsloth_version"):
781
- tags.append("unsloth")
782
-
783
- citation = textwrap.dedent("""\
784
- @article{mziegler2019fine-tuning,
785
- title = {{Fine-Tuning Language Models from Human Preferences}},
786
- author = {Daniel M. Ziegler and Nisan Stiennon and Jeffrey Wu and Tom B. Brown and Alec Radford and Dario Amodei and Paul F. Christiano and Geoffrey Irving},
787
- year = 2019,
788
- eprint = {arXiv:1909.08593}
789
- }""")
790
-
791
- model_card = generate_model_card(
792
- base_model=base_model,
793
- model_name=model_name,
794
- hub_model_id=self.hub_model_id,
795
- dataset_name=dataset_name,
796
- tags=tags,
797
- wandb_url=wandb.run.get_url() if is_wandb_available() and wandb.run is not None else None,
798
- comet_url=get_comet_experiment_url(),
799
- trainer_name="PPO",
800
- trainer_citation=citation,
801
- paper_title="Fine-Tuning Language Models from Human Preferences",
802
- paper_id="1909.08593",
803
- )
804
-
805
- model_card.save(os.path.join(self.args.output_dir, "README.md"))
806
-
807
-
808
- if __name__ == "__main__":
809
- pass