yangluo commited on
Commit
9d01f39
Β·
verified Β·
1 Parent(s): 3b6ace2

Upload train_sft_hf_cloud.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. train_sft_hf_cloud.py +290 -0
train_sft_hf_cloud.py ADDED
@@ -0,0 +1,290 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # /// script
3
+ # requires-python = ">=3.10"
4
+ # dependencies = [
5
+ # "torch",
6
+ # "trl>=0.12.0",
7
+ # "peft>=0.7.0",
8
+ # "transformers>=4.36.0",
9
+ # "accelerate>=0.24.0",
10
+ # "bitsandbytes",
11
+ # "datasets",
12
+ # "trackio",
13
+ # ]
14
+ # ///
15
+ """
16
+ SFT Training Script for Hugging Face Jobs (Cloud Training)
17
+
18
+ This script is designed to run on Hugging Face Jobs infrastructure.
19
+ It uses PEP 723 inline dependencies for automatic installation.
20
+
21
+ Supports both Dense and MoE models with 8-bit quantization.
22
+
23
+ Usage:
24
+ # Submit via hf_jobs MCP tool
25
+ hf_jobs("uv", {
26
+ "script": "https://huggingface.co/yangluo/univer-api-training-scripts/resolve/main/train_sft_hf_cloud.py",
27
+ "flavor": "a100-80gb-x1",
28
+ "timeout": "4h",
29
+ "secrets": {"HF_TOKEN": "$HF_TOKEN"},
30
+ })
31
+
32
+ # Or submit via Makefile
33
+ make train-cloud
34
+ """
35
+
36
+ import os
37
+
38
+ import torch
39
+ import trackio
40
+ from datasets import load_dataset
41
+ from huggingface_hub import login
42
+ from peft import LoraConfig
43
+ from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
44
+ from trl import SFTConfig, SFTTrainer
45
+
46
+ # ═══════════════════════════════════════════════════════════════
47
+ # CONFIGURATION - Modify these values as needed
48
+ # ═══════════════════════════════════════════════════════════════
49
+
50
+ # Model - Qwen3-Coder-Next is an 80B MoE model with 3B active params
51
+ MODEL_NAME = "Qwen/Qwen3-Coder-Next"
52
+
53
+ # Dataset
54
+ DATASET_NAME = "yangluo/univer-api-sft"
55
+
56
+ # Output - Where to push the trained model
57
+ HUB_MODEL_ID = "yangluo/univer-api-qwen3-next-coder"
58
+
59
+ # Training hyperparameters (optimized for large MoE model)
60
+ NUM_EPOCHS = 2
61
+ BATCH_SIZE = 2
62
+ GRADIENT_ACCUMULATION_STEPS = 8 # Effective batch = 2 * 8 = 16
63
+ LEARNING_RATE = 5e-5 # Lower LR for larger model
64
+
65
+ # LoRA configuration (Attention-only for MoE)
66
+ LORA_R = 16
67
+ LORA_ALPHA = 32
68
+ LORA_DROPOUT = 0.05
69
+ # Only target Attention layers for MoE models
70
+ # MLP layers (gate_proj, up_proj, down_proj) are in 512 experts - too many params
71
+ LORA_TARGET_MODULES = ["q_proj", "k_proj", "v_proj", "o_proj"]
72
+
73
+ # Quantization - 8-bit for 80B model on single A100
74
+ USE_8BIT = True
75
+
76
+ # Trackio monitoring
77
+ TRACKIO_PROJECT = "univer-api-training"
78
+ TRACKIO_RUN_NAME = "qwen3-coder-next-sft"
79
+
80
+ # ═══════════════════════════════════════════════════════════════
81
+ # SYSTEM PROMPT - Used to format training examples
82
+ # ═══════════════════════════════════════════════════════════════
83
+
84
+ SYSTEM_PROMPT = """You are a Univer API expert. Generate JavaScript code to perform spreadsheet operations using the Univer API.
85
+ The code should be an async arrow function that uses the univerAPI global object.
86
+
87
+ IMPORTANT PRINCIPLES:
88
+ 1. ALWAYS use spreadsheet formulas (setFormula) for calculations like sum, average, max, min, count, etc.
89
+ - Use setFormula('=SUM(...)'), setFormula('=AVERAGE(...)'), etc.
90
+ - Do NOT use JavaScript calculations (reduce, Math.max, etc.) for these operations
91
+ - Formulas are preferred because they update dynamically when data changes
92
+ 2. Use the correct API for the task:
93
+ - To delete rows: use deleteRows(), NOT clear()
94
+ - To insert rows: use insertRows(), NOT manual approaches
95
+ 3. ONLY use documented Univer API methods. Do NOT invent or guess method names."""
96
+
97
+ # ═══════════════════════════════════════════════════════════════
98
+ # TRAINING SCRIPT
99
+ # ═══════════════════════════════════════════════════════════════
100
+
101
+
102
+ def main():
103
+ # Login to Hugging Face Hub using token from environment
104
+ hf_token = os.environ.get("HF_TOKEN")
105
+ if hf_token:
106
+ print("Logging in to Hugging Face Hub...")
107
+ login(token=hf_token)
108
+ else:
109
+ print("WARNING: HF_TOKEN not found in environment!")
110
+ print("Model will NOT be pushed to Hub!")
111
+
112
+ print("=" * 60)
113
+ print(" UNIVER API SFT TRAINING (HF Cloud)")
114
+ print("=" * 60)
115
+ print(f"Model: {MODEL_NAME}")
116
+ print(f"Dataset: {DATASET_NAME}")
117
+ print(f"Output: {HUB_MODEL_ID}")
118
+ print(f"Epochs: {NUM_EPOCHS}")
119
+ print(f"Batch: {BATCH_SIZE} x {GRADIENT_ACCUMULATION_STEPS} = {BATCH_SIZE * GRADIENT_ACCUMULATION_STEPS}")
120
+ print(f"LR: {LEARNING_RATE}")
121
+ print(f"LoRA: r={LORA_R}, alpha={LORA_ALPHA}")
122
+ print(f"Targets: {LORA_TARGET_MODULES}")
123
+ print(f"8-bit: {USE_8BIT}")
124
+ print("=" * 60)
125
+
126
+ # ─────────────────────────────────────────────────────
127
+ # Load Dataset
128
+ # ─────────────────────────────────────────────────────
129
+ print("\n[1/5] Loading dataset...")
130
+ dataset = load_dataset(DATASET_NAME)
131
+ print(f" Train: {len(dataset['train'])} examples")
132
+ print(f" Validation: {len(dataset['validation'])} examples")
133
+
134
+ # ─────────────────────────────────────────────────────
135
+ # Load Tokenizer & Model
136
+ # ─────────────────────────────────────────────────────
137
+ print(f"\n[2/5] Loading model: {MODEL_NAME}")
138
+
139
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME, trust_remote_code=True)
140
+ if tokenizer.pad_token is None:
141
+ tokenizer.pad_token = tokenizer.eos_token
142
+
143
+ # Configure quantization
144
+ if USE_8BIT:
145
+ print(" Using 8-bit quantization...")
146
+ bnb_config = BitsAndBytesConfig(load_in_8bit=True)
147
+ model = AutoModelForCausalLM.from_pretrained(
148
+ MODEL_NAME,
149
+ quantization_config=bnb_config,
150
+ device_map="auto",
151
+ trust_remote_code=True,
152
+ )
153
+ else:
154
+ model = AutoModelForCausalLM.from_pretrained(
155
+ MODEL_NAME,
156
+ torch_dtype=torch.bfloat16,
157
+ device_map="auto",
158
+ trust_remote_code=True,
159
+ )
160
+
161
+ # Print GPU memory usage
162
+ if torch.cuda.is_available():
163
+ allocated = torch.cuda.memory_allocated() / 1024**3
164
+ print(f" Model loaded, GPU memory: {allocated:.1f} GB")
165
+
166
+ # ─────────────────────────────────────────────────────
167
+ # LoRA Configuration
168
+ # ─────────────────────────────────────────────────────
169
+ print("\n[3/5] Configuring LoRA...")
170
+ lora_config = LoraConfig(
171
+ r=LORA_R,
172
+ lora_alpha=LORA_ALPHA,
173
+ lora_dropout=LORA_DROPOUT,
174
+ target_modules=LORA_TARGET_MODULES,
175
+ bias="none",
176
+ task_type="CAUSAL_LM",
177
+ )
178
+ print(f" LoRA rank: {LORA_R}, alpha: {LORA_ALPHA}")
179
+ print(f" Target modules: {LORA_TARGET_MODULES}")
180
+
181
+ # ─────────────────────────────────────────────────────
182
+ # Format Data
183
+ # ─────────────────────────────────────────────────────
184
+ print("\n[4/5] Formatting dataset...")
185
+
186
+ def format_example(example):
187
+ messages = [
188
+ {"role": "system", "content": SYSTEM_PROMPT},
189
+ {"role": "user", "content": example["instruction"]},
190
+ {"role": "assistant", "content": example["code"]},
191
+ ]
192
+ text = tokenizer.apply_chat_template(
193
+ messages,
194
+ tokenize=False,
195
+ add_generation_prompt=False,
196
+ )
197
+ return {"text": text}
198
+
199
+ train_dataset = dataset["train"].map(
200
+ format_example,
201
+ remove_columns=dataset["train"].column_names,
202
+ )
203
+ eval_dataset = dataset["validation"].map(
204
+ format_example,
205
+ remove_columns=dataset["validation"].column_names,
206
+ )
207
+ print(f" Formatted {len(train_dataset)} training examples")
208
+
209
+ # ─────────────────────────────────────────────────────
210
+ # Training Configuration
211
+ # ─────────────────────────────────────────────────────
212
+ print("\n[5/5] Setting up trainer...")
213
+
214
+ # Calculate total steps for logging
215
+ total_steps = (len(train_dataset) // (BATCH_SIZE * GRADIENT_ACCUMULATION_STEPS)) * NUM_EPOCHS
216
+ print(f" Estimated total steps: {total_steps}")
217
+
218
+ training_args = SFTConfig(
219
+ # Output & Hub
220
+ output_dir="./outputs",
221
+ push_to_hub=True,
222
+ hub_model_id=HUB_MODEL_ID,
223
+ hub_strategy="every_save",
224
+ # Training
225
+ num_train_epochs=NUM_EPOCHS,
226
+ per_device_train_batch_size=BATCH_SIZE,
227
+ per_device_eval_batch_size=BATCH_SIZE,
228
+ gradient_accumulation_steps=GRADIENT_ACCUMULATION_STEPS,
229
+ learning_rate=LEARNING_RATE,
230
+ lr_scheduler_type="cosine",
231
+ warmup_ratio=0.1,
232
+ optim="adamw_torch",
233
+ weight_decay=0.01,
234
+ max_grad_norm=1.0,
235
+ bf16=True,
236
+ # Logging
237
+ logging_steps=10,
238
+ logging_first_step=True,
239
+ report_to="trackio",
240
+ project=TRACKIO_PROJECT,
241
+ run_name=TRACKIO_RUN_NAME,
242
+ # Evaluation
243
+ eval_strategy="steps",
244
+ eval_steps=100,
245
+ # Checkpointing
246
+ save_strategy="steps",
247
+ save_steps=200,
248
+ save_total_limit=2,
249
+ load_best_model_at_end=True,
250
+ metric_for_best_model="eval_loss",
251
+ greater_is_better=False,
252
+ )
253
+
254
+ trainer = SFTTrainer(
255
+ model=model,
256
+ args=training_args,
257
+ train_dataset=train_dataset,
258
+ eval_dataset=eval_dataset,
259
+ processing_class=tokenizer,
260
+ peft_config=lora_config,
261
+ )
262
+
263
+ # ─────────────────────────────────────────────────────
264
+ # Train
265
+ # ─────────────────────────────────────────────────────
266
+ print("\n" + "=" * 60)
267
+ print("Starting training...")
268
+ print("=" * 60)
269
+
270
+ trainer.train()
271
+
272
+ # ─────────────────────────────────────────────────────
273
+ # Save & Push
274
+ # ─────────────────────────────────────────────────────
275
+ print("\nSaving and pushing to Hub...")
276
+ trainer.save_model()
277
+ trainer.push_to_hub()
278
+
279
+ # Finish Trackio
280
+ trackio.finish()
281
+
282
+ print("\n" + "=" * 60)
283
+ print(" TRAINING COMPLETE!")
284
+ print("=" * 60)
285
+ print(f"Model: https://huggingface.co/{HUB_MODEL_ID}")
286
+ print(f"Monitor: https://huggingface.co/spaces/yangluo/trackio")
287
+
288
+
289
+ if __name__ == "__main__":
290
+ main()