miyuki2026 commited on
Commit
2d22b18
·
1 Parent(s): cce54bc
examples/tutorials/dpo/ultrafeedback-dpo/step_2_train_dpo_model_ddp_qlora.py CHANGED
@@ -6,7 +6,7 @@ https://huggingface.co/docs/trl/v0.16.1/en/sft_trainer
6
  多卡 V00 32G 全参微调
7
  python3 -m torch.distributed.run --nproc_per_node=2 step_2_train_dpo_model_ddp_qlora.py
8
 
9
- torchrun --nproc_per_node=2 step_2_train_dpo_model_ddp_qlora.py
10
 
11
  DPO本来就是风格微调,用LoRA 训练更合理,更科学。
12
 
@@ -14,7 +14,8 @@ DPO本来就是风格微调,用LoRA 训练更合理,更科学。
14
  ----------
15
 
16
  nohup torchrun --nproc_per_node=2 step_2_train_dpo_model_ddp_qlora.py \
17
- --learning_rate 5e-5
 
18
  --dpo_beta 0.05 \
19
  --lora_rank 32 \
20
  &
@@ -80,6 +81,7 @@ def get_args():
80
  type=str
81
  ),
82
 
 
83
  parser.add_argument("--learning_rate", default=2e-5, type=float),
84
  parser.add_argument("--dpo_beta", default=0.5, type=float),
85
  parser.add_argument("--lora_rank", default=32, type=int),
@@ -221,10 +223,13 @@ def main():
221
  num_proc=args.num_workers,
222
  remove_columns=train_dataset.column_names,
223
  )
 
 
 
224
 
225
  dpo_config = DPOConfig(
226
  output_dir=args.output_model_dir,
227
- num_train_epochs=1,
228
  per_device_train_batch_size=1 if debug_mode else 2,
229
  gradient_accumulation_steps=1 if debug_mode else 8,
230
  save_strategy="steps",
 
6
  多卡 V00 32G 全参微调
7
  python3 -m torch.distributed.run --nproc_per_node=2 step_2_train_dpo_model_ddp_qlora.py
8
 
9
+ torchrun --nproc_per_node=4 step_2_train_dpo_model_ddp_qlora.py
10
 
11
  DPO本来就是风格微调,用LoRA 训练更合理,更科学。
12
 
 
14
  ----------
15
 
16
  nohup torchrun --nproc_per_node=2 step_2_train_dpo_model_ddp_qlora.py \
17
+ --num_train_epochs 5 \
18
+ --learning_rate 5e-5 \
19
  --dpo_beta 0.05 \
20
  --lora_rank 32 \
21
  &
 
81
  type=str
82
  ),
83
 
84
+ parser.add_argument("--num_train_epochs", default=3, type=int),
85
  parser.add_argument("--learning_rate", default=2e-5, type=float),
86
  parser.add_argument("--dpo_beta", default=0.5, type=float),
87
  parser.add_argument("--lora_rank", default=32, type=int),
 
223
  num_proc=args.num_workers,
224
  remove_columns=train_dataset.column_names,
225
  )
226
+ train_dataset = train_dataset.filter(
227
+ function=lambda x: len(x["input_ids"]) < 2048
228
+ )
229
 
230
  dpo_config = DPOConfig(
231
  output_dir=args.output_model_dir,
232
+ num_train_epochs=args.num_train_epochs,
233
  per_device_train_batch_size=1 if debug_mode else 2,
234
  gradient_accumulation_steps=1 if debug_mode else 8,
235
  save_strategy="steps",
examples/tutorials/dpo/ultrafeedback-dpo/step_2_train_dpo_model_unsloth_ddp_qlora.py ADDED
@@ -0,0 +1,301 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ https://huggingface.co/docs/trl/v0.16.1/en/sft_trainer
5
+
6
+ 多卡 V00 32G 全参微调
7
+ python3 -m torch.distributed.run --nproc_per_node=4 step_2_train_dpo_model_unsloth_ddp_qlora.py
8
+
9
+ torchrun --nproc_per_node=4 step_2_train_dpo_model_unsloth_ddp_qlora.py
10
+
11
+ DPO本来就是风格微调,用LoRA 训练更合理,更科学。
12
+
13
+ ----------
14
+
15
+ nohup torchrun --nproc_per_node=4 step_2_train_dpo_model_unsloth_ddp_qlora.py \
16
+ --num_train_epochs 5 \
17
+ --learning_rate 5e-5 \
18
+ --dpo_beta 0.05 \
19
+ --lora_rank 32 \
20
+ &
21
+
22
+ kill -9 `ps -aef | grep 'step_2_train_dpo_model_unsloth_ddp_qlora.py' | grep -v grep | awk '{print $2}'`
23
+
24
+
25
+ """
26
+ import argparse
27
+ import os
28
+ from pathlib import Path
29
+ import platform
30
+
31
+ # os.environ["HF_ENDPOINT"] = "https://hf-mirror.com"
32
+ os.environ["UNSLOTH_USE_MODELSCOPE"] = "1"
33
+
34
+ debug_mode = True if platform.system() in ("Windows", "Darwin") else False
35
+ print(f"debug_mode: {debug_mode}")
36
+
37
+ if platform.system() in ("Windows", "Darwin"):
38
+ from project_settings import project_path, temp_directory
39
+ else:
40
+ project_path = os.path.abspath("../../../")
41
+ project_path = Path(project_path)
42
+ temp_directory = Path("/root/autodl-tmp/OpenMiniMind/temp")
43
+
44
+ from datasets import load_dataset
45
+
46
+ from trl import DPOConfig, DPOTrainer
47
+ from unsloth import FastLanguageModel, is_bfloat16_supported
48
+
49
+
50
+ def get_args():
51
+ parser = argparse.ArgumentParser()
52
+ parser.add_argument("--local_rank", type=int, default=-1) # torchrun会自动传递这个参数
53
+
54
+ parser.add_argument(
55
+ "--model_name",
56
+ default=(project_path / "pretrained_models/jingyaogong/MiniMind2").as_posix() if debug_mode else "qgyd2021/Qwen2.5-0.5B-ultrachat-sft-deepspeed",
57
+ type=str
58
+ ),
59
+ parser.add_argument(
60
+ "--dataset_path",
61
+ default="HuggingFaceH4/ultrafeedback_binarized",
62
+ # default="miyuki2026/tutorials" if debug_mode else "HuggingFaceH4/ultrachat_200k",
63
+ type=str
64
+ ),
65
+ parser.add_argument(
66
+ "--dataset_cache_dir",
67
+ default=(temp_directory / "hub_datasets").as_posix(),
68
+ type=str
69
+ ),
70
+ parser.add_argument(
71
+ "--model_cache_dir",
72
+ default=(temp_directory / "hub_models").as_posix(),
73
+ type=str
74
+ ),
75
+ parser.add_argument(
76
+ "--output_model_dir",
77
+ default=(temp_directory / "trained_models/qwen2_5-0_5B-ultrafeedback-dpo-ddp-qlora").as_posix(),
78
+ type=str
79
+ ),
80
+
81
+ parser.add_argument("--max_seq_length",
82
+ default=1024 if debug_mode else 2048,
83
+ type=int
84
+ )
85
+
86
+ parser.add_argument("--num_train_epochs", default=3, type=int),
87
+ parser.add_argument("--learning_rate", default=2e-5, type=float),
88
+ parser.add_argument("--dpo_beta", default=0.5, type=float),
89
+ parser.add_argument("--lora_rank", default=32, type=int),
90
+
91
+ parser.add_argument(
92
+ "--num_workers",
93
+ default=None if debug_mode else os.cpu_count() // 2,
94
+ type=int
95
+ ),
96
+ args = parser.parse_args()
97
+ return args
98
+
99
+
100
+ def format_func(examples, tokenizer):
101
+ chosen = examples["chosen"]
102
+ rejected = examples["rejected"]
103
+
104
+ chosen_prompt = chosen[:-1]
105
+ chosen_response = chosen[-1]
106
+
107
+ rejected_prompt = rejected[:-1]
108
+ rejected_response = rejected[-1]
109
+
110
+ chosen_prompt_text = tokenizer.apply_chat_template(
111
+ conversation=chosen_prompt,
112
+ tokenize=False,
113
+ add_generation_prompt=True, # DPO 需要添加生成提示,让模型知道要从这里开始生成
114
+ )
115
+ rejected_prompt_text = tokenizer.apply_chat_template(
116
+ conversation=rejected_prompt,
117
+ tokenize=False,
118
+ add_generation_prompt=True, # DPO 需要添加生成提示,让模型知道要从这里开始生成
119
+ )
120
+ if chosen_prompt_text != rejected_prompt_text:
121
+ raise AssertionError()
122
+
123
+ chosen_response_role = chosen_response["role"]
124
+ chosen_response_text = chosen_response["content"]
125
+ if chosen_response_role != "assistant":
126
+ raise AssertionError()
127
+
128
+ rejected_response_role = rejected_response["role"]
129
+ rejected_response_text = rejected_response["content"]
130
+ if rejected_response_role != "assistant":
131
+ raise AssertionError()
132
+
133
+ result = {
134
+ "prompt": chosen_prompt_text,
135
+ "chosen": chosen_response_text,
136
+ "rejected": rejected_response_text,
137
+ }
138
+ return result
139
+
140
+
141
+ def main():
142
+ args = get_args()
143
+
144
+ if args.local_rank == -1:
145
+ local_rank = os.environ.get("LOCAL_RANK", 0)
146
+ local_rank = int(local_rank)
147
+ else:
148
+ local_rank = args.local_rank
149
+ is_main_process = True if local_rank in (0, -1) else False
150
+
151
+ # 设置缓存目录
152
+ os.environ["MODELSCOPE_CACHE"] = args.model_cache_dir
153
+ os.environ["HF_DATASETS_CACHE"] = args.dataset_cache_dir
154
+
155
+ model, tokenizer = FastLanguageModel.from_pretrained(
156
+ model_name=args.model_name,
157
+ max_seq_length=args.max_seq_length,
158
+ dtype=None,
159
+ load_in_4bit=True,
160
+ cache_dir=args.model_cache_dir,
161
+ )
162
+ model = FastLanguageModel.get_peft_model(
163
+ model,
164
+ r=args.lora_rank,
165
+ lora_alpha=args.lora_rank * 2,
166
+ target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
167
+ lora_dropout=0, # Unsloth 推荐使用 0
168
+ bias="none", # Unsloth 推荐使用 "none"
169
+ use_gradient_checkpointing="unsloth", # 使用 unsloth 的梯度检查点实现
170
+ random_state=42,
171
+ max_seq_length=args.max_seq_length, # 重要:设置最大序列长度
172
+ )
173
+
174
+ # 创建参考模型(使用相同的权重,但不训练)
175
+ # 在 unsloth 中,我们可以直接使用原始模型作为参考模型
176
+ # 但需要确保它也是量化版本
177
+ ref_model, _ = FastLanguageModel.from_pretrained(
178
+ model_name=args.model_name,
179
+ max_seq_length=args.max_seq_length,
180
+ dtype=None,
181
+ load_in_4bit=True,
182
+ cache_dir=args.model_cache_dir,
183
+ )
184
+ ref_model.eval()
185
+
186
+ if is_main_process:
187
+ model.print_trainable_parameters()
188
+ print(f"model device: {model.device}")
189
+ print(f"ref_model device: {ref_model.device}")
190
+
191
+ if tokenizer.pad_token is None:
192
+ tokenizer.pad_token = tokenizer.eos_token
193
+ tokenizer.pad_token_id = tokenizer.eos_token_id
194
+ tokenizer.padding_side = "left" # DPO需要left padding
195
+
196
+ print(model)
197
+ print(ref_model)
198
+ print(tokenizer)
199
+
200
+ dataset_dict = load_dataset(
201
+ path=args.dataset_path,
202
+ cache_dir=args.dataset_cache_dir,
203
+ )
204
+ train_dataset = dataset_dict["train_prefs"]
205
+ valid_dataset = dataset_dict["test_prefs"]
206
+
207
+ train_dataset = train_dataset.map(
208
+ lambda x: format_func(x, tokenizer),
209
+ batched=False,
210
+ num_proc=args.num_workers,
211
+ remove_columns=train_dataset.column_names,
212
+ )
213
+ valid_dataset = valid_dataset.map(
214
+ lambda x: format_func(x, tokenizer),
215
+ batched=False,
216
+ num_proc=args.num_workers,
217
+ remove_columns=valid_dataset.column_names,
218
+ )
219
+
220
+ def filter_long_samples(example):
221
+ # 简单估计长度,实际训练时会由tokenizer处理
222
+ prompt_len = len(example["prompt"])
223
+ chosen_len = len(example["chosen"])
224
+ rejected_len = len(example["rejected"])
225
+ if (prompt_len + chosen_len) > args.max_seq_length:
226
+ return False
227
+ if (rejected_len + chosen_len) > args.max_seq_length:
228
+ return False
229
+ return True
230
+ train_dataset = train_dataset.filter(filter_long_samples)
231
+ valid_dataset = valid_dataset.filter(filter_long_samples)
232
+
233
+ # 配置 DPO 训练参数
234
+ dpo_config = DPOConfig(
235
+ output_dir=args.output_model_dir,
236
+ num_train_epochs=args.num_train_epochs,
237
+ per_device_train_batch_size=1 if debug_mode else 2,
238
+ per_device_eval_batch_size=1 if debug_mode else 2,
239
+ gradient_accumulation_steps=1 if debug_mode else 8,
240
+ gradient_checkpointing=True,
241
+ gradient_checkpointing_kwargs={"use_reentrant": False},
242
+ save_strategy="steps",
243
+ save_steps=100,
244
+ save_total_limit=2,
245
+ logging_steps=10,
246
+ eval_strategy="steps", # 添加评估策略
247
+ eval_steps=100, # 每100步评估一次
248
+ learning_rate=args.learning_rate,
249
+ warmup_steps=100,
250
+ lr_scheduler_type="cosine",
251
+ fp16=not is_bfloat16_supported(), # 如果不支持bfloat16则使用fp16
252
+ bf16=is_bfloat16_supported(), # 如果支持bfloat16则使用bf16
253
+ optim="adamw_8bit", # 使用8bit优化器节省显存
254
+ report_to="none",
255
+ max_length=args.max_seq_length, # prompt + chosen 的最大长度
256
+ max_prompt_length=args.max_seq_length // 2, # prompt 的最大长度
257
+
258
+ # DPO 特定参数
259
+ beta=args.dpo_beta, # DPO 的温度参数
260
+ remove_unused_columns=False,
261
+ dataloader_pin_memory=False,
262
+
263
+ # DDP 相关参数
264
+ ddp_find_unused_parameters=False, # 重要:告诉DDP忽略未使用的参数
265
+ local_rank=local_rank, # 传递当前进程的local_rank
266
+
267
+ # 其他优化
268
+ dataloader_num_workers=args.num_workers or 0,
269
+ )
270
+
271
+ trainer = DPOTrainer(
272
+ model=model,
273
+ ref_model=ref_model,
274
+ args=dpo_config,
275
+ train_dataset=train_dataset,
276
+ eval_dataset=valid_dataset,
277
+ processing_class=tokenizer,
278
+ # DPOTrainer 会自动处理数据,不需要 data_collator
279
+ )
280
+
281
+ # 开始训练
282
+ print("开始 DPO 训练...")
283
+ trainer.train()
284
+
285
+ # 保存模型
286
+ if is_main_process: # 只在主进程保存
287
+ print(f"保存模型到: {args.output_model_dir}")
288
+ # 保存 LoRA 权重
289
+ model.save_pretrained(args.output_model_dir)
290
+ tokenizer.save_pretrained(args.output_model_dir)
291
+ # 如果需要合并并保存完整模型(会占用更多空间)
292
+ merged_model = model.merge_and_unload()
293
+ merged_model.save_pretrained(args.output_model_dir + "_merged")
294
+ print(f"模型保存完成!")
295
+
296
+ print("DPO 训练完成!")
297
+ return
298
+
299
+
300
+ if __name__ == "__main__":
301
+ main()
examples/tutorials/grpo/step_1_download_model_ms.py CHANGED
@@ -3,9 +3,9 @@
3
  """
4
  或使用命令行
5
 
6
- python3 step_1_prepare_data.py \
7
- --repo_id qgyd2021/gpt2-for-sequence-classification-sst2-reward \
8
- --local_dir /root/autodl-tmp/OpenMiniMind/trained_models/gpt2-for-sequence-classification-sst2-reward
9
 
10
  """
11
  import argparse
 
3
  """
4
  或使用命令行
5
 
6
+ python3 step_1_download_model_ms.py \
7
+ --repo_id Qwen/Qwen2.5-3B-Instruct \
8
+ --local_dir /root/autodl-tmp/OpenMiniMind/pretrained_models/Qwen/Qwen2.5-3B-Instruct
9
 
10
  """
11
  import argparse
examples/tutorials/grpo/step_2_train_grpo_model.py CHANGED
@@ -23,11 +23,8 @@ else:
23
  temp_directory = Path("/root/autodl-tmp/OpenMiniMind/temp")
24
 
25
  from datasets import load_dataset
26
- import torch
27
  from transformers import (
28
- AutoTokenizer, AutoModelForCausalLM, AutoModelForSequenceClassification,
29
- GPT2LMHeadModel, GPT2ForSequenceClassification,
30
- DataCollatorWithPadding
31
  )
32
  from trl import GRPOConfig, GRPOTrainer
33
 
@@ -37,8 +34,8 @@ def get_args():
37
  parser.add_argument(
38
  "--model_name",
39
  # default="Qwen/Qwen2.5-3B-Instruct",
40
- # default=(project_path / "pretrained_models/Qwen/Qwen2.5-3B-Instruct").as_posix(),
41
- default=(project_path / "pretrained_models/jingyaogong/MiniMind2").as_posix(),
42
  type=str
43
  )
44
  parser.add_argument("--dataset_path", default="Jiayi-Pan/Countdown-Tasks-3to4", type=str)
@@ -50,8 +47,6 @@ def get_args():
50
  # 训练参数
51
  parser.add_argument("--valid_dataset_size", default=2000, type=int)
52
 
53
- # 生成参数
54
-
55
  parser.add_argument(
56
  "--output_model_dir",
57
  default=(project_path / "trained_models/qwen2_5-3B-Instruct-Countdown-GRPO").as_posix(),
@@ -277,11 +272,19 @@ def main():
277
  bf16=False,
278
  max_grad_norm=1.0,
279
  report_to="none", # 可根据需要改为"wandb"等
 
280
  # GRPO特有参数
281
  num_generations=4, # 每个提示生成的响应数量
282
- temperature=0.7,
283
  max_completion_length=512, # 生成的最大长度
 
 
 
 
 
 
284
  reward_weights=[0.1, 1.0],
 
 
285
  )
286
 
287
  grpo_trainer = GRPOTrainer(
 
23
  temp_directory = Path("/root/autodl-tmp/OpenMiniMind/temp")
24
 
25
  from datasets import load_dataset
 
26
  from transformers import (
27
+ AutoTokenizer, AutoModelForCausalLM,
 
 
28
  )
29
  from trl import GRPOConfig, GRPOTrainer
30
 
 
34
  parser.add_argument(
35
  "--model_name",
36
  # default="Qwen/Qwen2.5-3B-Instruct",
37
+ default=(project_path / "pretrained_models/Qwen/Qwen2.5-3B-Instruct").as_posix(),
38
+ # default=(project_path / "pretrained_models/jingyaogong/MiniMind2").as_posix(),
39
  type=str
40
  )
41
  parser.add_argument("--dataset_path", default="Jiayi-Pan/Countdown-Tasks-3to4", type=str)
 
47
  # 训练参数
48
  parser.add_argument("--valid_dataset_size", default=2000, type=int)
49
 
 
 
50
  parser.add_argument(
51
  "--output_model_dir",
52
  default=(project_path / "trained_models/qwen2_5-3B-Instruct-Countdown-GRPO").as_posix(),
 
272
  bf16=False,
273
  max_grad_norm=1.0,
274
  report_to="none", # 可根据需要改为"wandb"等
275
+
276
  # GRPO特有参数
277
  num_generations=4, # 每个提示生成的响应数量
 
278
  max_completion_length=512, # 生成的最大长度
279
+ steps_per_generation=12,
280
+ temperature=0.7,
281
+
282
+ beta=0.001,
283
+ num_iterations=1,
284
+ epsilon=0.2,
285
  reward_weights=[0.1, 1.0],
286
+ loss_type="dapo",
287
+
288
  )
289
 
290
  grpo_trainer = GRPOTrainer(