ashhhhhh26 commited on
Commit
b9fd392
Β·
verified Β·
1 Parent(s): 282c596

Add training script: QLoRA SFT on Qwen2.5-Coder-7B with SOTA code datasets

Browse files
Files changed (1) hide show
  1. train.py +247 -0
train.py ADDED
@@ -0,0 +1,247 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Train a Claude Code-level coding model via QLoRA SFT on Qwen2.5-Coder-7B-Instruct.
4
+
5
+ Recipe (based on published SOTA results):
6
+ - Base: Qwen2.5-Coder-7B-Instruct (88.4% HumanEval baseline)
7
+ - Data: KodCode-V1-SFT-R1 (verified competitive programming with R1-style CoT)
8
+ + Code-Feedback (multi-turn code dialogue)
9
+ + Magicoder-OSS-Instruct (diverse code generation)
10
+ + Magicoder-Evol-Instruct (evolved code instructions)
11
+ - Method: QLoRA (4-bit NF4 + LoRA r=64, all-linear)
12
+ - Target: Push past 90%+ HumanEval, maximize LiveCodeBench with CoT reasoning
13
+
14
+ References:
15
+ - rStar-Coder (arxiv:2505.21297): Qwen2.5-Coder-7B β†’ 57.3% LiveCodeBench
16
+ - KodCode (arxiv:2503.02951): Verified coding dataset with R1-style reasoning
17
+ - Qwen2.5-Coder (arxiv:2409.12186): Base model technical report
18
+ """
19
+
20
+ import os
21
+ import torch
22
+ from datasets import load_dataset, concatenate_datasets
23
+ from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
24
+ from peft import LoraConfig, prepare_model_for_kbit_training
25
+ from trl import SFTConfig, SFTTrainer
26
+
27
+ # ─── Configuration ────────────────────────────────────────────────────────────
28
+ MODEL_ID = "Qwen/Qwen2.5-Coder-7B-Instruct"
29
+ OUTPUT_DIR = "./qwen25-coder-7b-mythos"
30
+ HUB_MODEL_ID = "ashhhhhh26/qwen25-coder-32b-mythos"
31
+
32
+ SYSTEM_PROMPT = """You are an elite software engineer with deep expertise across all programming languages, frameworks, and paradigms. You write clean, efficient, well-documented code. You think step-by-step through complex problems, consider edge cases, and provide production-quality solutions. When debugging, you methodically trace through the code to identify root causes. You explain your reasoning clearly and concisely."""
33
+
34
+ # ─── 1. Load datasets ────────────────────────────────────────────────────────
35
+ print("=" * 60)
36
+ print("Loading datasets...")
37
+ print("=" * 60)
38
+
39
+ # Dataset 1: KodCode-V1-SFT-R1 (verified competitive programming + R1-style reasoning)
40
+ ds_kodcode = load_dataset("KodCode/KodCode-V1-SFT-R1", split="train")
41
+ print(f"KodCode-V1-SFT-R1 (raw): {len(ds_kodcode)} samples")
42
+
43
+ # Filter for R1-verified correct solutions only
44
+ ds_kodcode = ds_kodcode.filter(lambda x: x["r1_correctness"] is True, num_proc=4)
45
+ print(f"KodCode-V1-SFT-R1 (r1_correctness=True): {len(ds_kodcode)} samples")
46
+
47
+ # Dataset 2: Code-Feedback (~66K) - already in messages format
48
+ ds_feedback = load_dataset("m-a-p/Code-Feedback", split="train")
49
+ print(f"Code-Feedback: {len(ds_feedback)} samples")
50
+
51
+ # Dataset 3: Magicoder-OSS-Instruct-75K
52
+ ds_magicoder_oss = load_dataset("ise-uiuc/Magicoder-OSS-Instruct-75K", split="train")
53
+ print(f"Magicoder-OSS-Instruct: {len(ds_magicoder_oss)} samples")
54
+
55
+ # Dataset 4: Magicoder-Evol-Instruct-110K
56
+ ds_magicoder_evol = load_dataset("ise-uiuc/Magicoder-Evol-Instruct-110K", split="train")
57
+ print(f"Magicoder-Evol-Instruct: {len(ds_magicoder_evol)} samples")
58
+
59
+
60
+ # ─── 2. Convert all datasets to ChatML messages format ───────────────────────
61
+ print("\\nConverting datasets to ChatML format...")
62
+
63
+ def convert_kodcode(example):
64
+ """Convert KodCode conversations (human/gpt) to standard ChatML messages."""
65
+ role_map = {"human": "user", "gpt": "assistant"}
66
+ messages = [{"role": "system", "content": SYSTEM_PROMPT}]
67
+ for msg in example["conversations"]:
68
+ role = role_map.get(msg["from"], msg["from"])
69
+ messages.append({"role": role, "content": msg["value"]})
70
+ return {"messages": messages}
71
+
72
+ def convert_feedback(example):
73
+ """Code-Feedback already has messages, just add system prompt."""
74
+ messages = example["messages"]
75
+ if messages and messages[0]["role"] != "system":
76
+ messages = [{"role": "system", "content": SYSTEM_PROMPT}] + messages
77
+ return {"messages": messages}
78
+
79
+ def convert_magicoder_oss(example):
80
+ """Convert problem/solution to messages format."""
81
+ return {
82
+ "messages": [
83
+ {"role": "system", "content": SYSTEM_PROMPT},
84
+ {"role": "user", "content": example["problem"]},
85
+ {"role": "assistant", "content": example["solution"]},
86
+ ]
87
+ }
88
+
89
+ def convert_magicoder_evol(example):
90
+ """Convert instruction/response to messages format."""
91
+ return {
92
+ "messages": [
93
+ {"role": "system", "content": SYSTEM_PROMPT},
94
+ {"role": "user", "content": example["instruction"]},
95
+ {"role": "assistant", "content": example["response"]},
96
+ ]
97
+ }
98
+
99
+ # Apply conversions
100
+ ds_kodcode = ds_kodcode.map(convert_kodcode, num_proc=4, remove_columns=ds_kodcode.column_names)
101
+ ds_feedback = ds_feedback.map(convert_feedback, num_proc=4, remove_columns=[c for c in ds_feedback.column_names if c != "messages"])
102
+ ds_magicoder_oss = ds_magicoder_oss.map(convert_magicoder_oss, num_proc=4, remove_columns=ds_magicoder_oss.column_names)
103
+ ds_magicoder_evol = ds_magicoder_evol.map(convert_magicoder_evol, num_proc=4, remove_columns=ds_magicoder_evol.column_names)
104
+
105
+ # Combine all datasets
106
+ combined_dataset = concatenate_datasets([ds_kodcode, ds_feedback, ds_magicoder_oss, ds_magicoder_evol])
107
+ combined_dataset = combined_dataset.shuffle(seed=42)
108
+ print(f"\\nTotal combined dataset: {len(combined_dataset)} samples")
109
+
110
+ # Quality filter
111
+ def filter_quality(example):
112
+ """Remove examples with very short responses."""
113
+ msgs = example["messages"]
114
+ assistant_msgs = [m for m in msgs if m["role"] == "assistant"]
115
+ if not assistant_msgs:
116
+ return False
117
+ total_assistant_len = sum(len(m["content"]) for m in assistant_msgs)
118
+ return total_assistant_len >= 50
119
+
120
+ combined_dataset = combined_dataset.filter(filter_quality, num_proc=4)
121
+ print(f"After quality filter: {len(combined_dataset)} samples")
122
+
123
+
124
+ # ─── 3. Load model with QLoRA (4-bit quantization) ───────────────────────────
125
+ print("\\n" + "=" * 60)
126
+ print(f"Loading {MODEL_ID} with 4-bit quantization...")
127
+ print("=" * 60)
128
+
129
+ bnb_config = BitsAndBytesConfig(
130
+ load_in_4bit=True,
131
+ bnb_4bit_quant_type="nf4",
132
+ bnb_4bit_use_double_quant=True,
133
+ bnb_4bit_compute_dtype=torch.bfloat16,
134
+ )
135
+
136
+ model = AutoModelForCausalLM.from_pretrained(
137
+ MODEL_ID,
138
+ quantization_config=bnb_config,
139
+ attn_implementation="flash_attention_2",
140
+ torch_dtype=torch.bfloat16,
141
+ device_map="auto",
142
+ )
143
+ model = prepare_model_for_kbit_training(model)
144
+
145
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
146
+ if tokenizer.pad_token is None:
147
+ tokenizer.pad_token = tokenizer.eos_token
148
+ tokenizer.padding_side = "right"
149
+
150
+ print(f"Model loaded. Parameters: {model.num_parameters():,}")
151
+
152
+
153
+ # ─── 4. LoRA config ──────────────────────────────────────────────────────────
154
+ peft_config = LoraConfig(
155
+ r=64,
156
+ lora_alpha=128,
157
+ lora_dropout=0.05,
158
+ bias="none",
159
+ task_type="CAUSAL_LM",
160
+ target_modules="all-linear",
161
+ )
162
+
163
+
164
+ # ─── 5. Training config ──────────────────────────────────────────────────────
165
+ training_args = SFTConfig(
166
+ output_dir=OUTPUT_DIR,
167
+
168
+ # Data
169
+ max_length=4096,
170
+ packing=True,
171
+ dataset_num_proc=8,
172
+
173
+ # Training hyperparams
174
+ num_train_epochs=2,
175
+ per_device_train_batch_size=1,
176
+ gradient_accumulation_steps=16,
177
+ learning_rate=2e-4,
178
+ lr_scheduler_type="cosine",
179
+ warmup_ratio=0.05,
180
+ weight_decay=0.01,
181
+ max_grad_norm=1.0,
182
+ optim="paged_adamw_8bit",
183
+
184
+ # Memory optimization
185
+ gradient_checkpointing=True,
186
+ bf16=True,
187
+ tf32=True,
188
+
189
+ # Logging
190
+ logging_steps=5,
191
+ logging_first_step=True,
192
+ disable_tqdm=True,
193
+
194
+ # Saving & Hub
195
+ save_strategy="steps",
196
+ save_steps=1000,
197
+ save_total_limit=3,
198
+ push_to_hub=True,
199
+ hub_model_id=HUB_MODEL_ID,
200
+ hub_strategy="every_save",
201
+
202
+ # Monitoring
203
+ report_to="trackio",
204
+ run_name="qwen25-coder-7b-mythos-sft",
205
+ project="code-mythos",
206
+
207
+ # Misc
208
+ seed=42,
209
+ dataloader_num_workers=4,
210
+ remove_unused_columns=False,
211
+ )
212
+
213
+
214
+ # ─── 6. Create trainer and train ─────────────────────────────────────────────
215
+ print("\\n" + "=" * 60)
216
+ print("Initializing SFTTrainer...")
217
+ print("=" * 60)
218
+
219
+ trainer = SFTTrainer(
220
+ model=model,
221
+ args=training_args,
222
+ train_dataset=combined_dataset,
223
+ processing_class=tokenizer,
224
+ peft_config=peft_config,
225
+ )
226
+
227
+ trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
228
+ total_params = model.num_parameters()
229
+ print(f"Trainable: {trainable_params:,} / {total_params:,} ({100 * trainable_params / total_params:.2f}%)")
230
+
231
+ print("\\n" + "=" * 60)
232
+ print("Starting training...")
233
+ print("=" * 60)
234
+
235
+ trainer.train()
236
+
237
+ # ─── 7. Save and push final model ────────────────────────────────────────────
238
+ print("\\n" + "=" * 60)
239
+ print("Saving final model...")
240
+ print("=" * 60)
241
+
242
+ trainer.save_model(OUTPUT_DIR)
243
+ trainer.push_to_hub()
244
+
245
+ print("\\n" + "=" * 60)
246
+ print(f"βœ… Training complete! Model pushed to: https://huggingface.co/{HUB_MODEL_ID}")
247
+ print("=" * 60)