VinayHajare commited on
Commit
516872f
·
verified ·
1 Parent(s): c6acabc

Add training script

Browse files
Files changed (1) hide show
  1. train.py +259 -0
train.py ADDED
@@ -0,0 +1,259 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ DeepSeek-V4 Training Script
3
+ ============================
4
+ Training script for the open DeepSeek-V4 reproduction.
5
+
6
+ Implements the training recipe from the DeepSeek-V4 technical report:
7
+ - Muon optimizer (or AdamW fallback)
8
+ - FP8/BF16 mixed precision training
9
+ - Gradient checkpointing
10
+ - Distributed training via accelerate
11
+ - Progressive sequence length extension
12
+ - MTP auxiliary loss
13
+
14
+ Usage:
15
+ # Single GPU testing with nano config
16
+ python train.py --config configs/config_nano.json --dataset HuggingFaceFW/fineweb-edu-score-2 --max_steps 100
17
+
18
+ # Multi-GPU with accelerate
19
+ accelerate launch train.py --config configs/config_pro.json --dataset HuggingFaceFW/fineweb-edu-score-2
20
+ """
21
+
22
+ import os
23
+ import sys
24
+ import json
25
+ import math
26
+ import argparse
27
+ import logging
28
+ from typing import Optional
29
+ from dataclasses import dataclass, field
30
+
31
+ import torch
32
+ import torch.nn as nn
33
+ import torch.nn.functional as F
34
+ from torch.utils.data import DataLoader, IterableDataset
35
+
36
+ from transformers import (
37
+ TrainingArguments,
38
+ Trainer,
39
+ TrainerCallback,
40
+ AutoTokenizer,
41
+ get_cosine_schedule_with_warmup,
42
+ )
43
+ from datasets import load_dataset
44
+
45
+ # Add parent directory to path
46
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
47
+ from configuration_deepseek_v4 import DeepSeekV4Config
48
+ from modeling_deepseek_v4 import DeepSeekV4ForCausalLM
49
+
50
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
51
+ logger = logging.getLogger(__name__)
52
+
53
+
54
+ # =============================================================================
55
+ # Data Pipeline
56
+ # =============================================================================
57
+
58
+ class StreamingTextDataset(IterableDataset):
59
+ """Streaming dataset that tokenizes on-the-fly and packs sequences."""
60
+
61
+ def __init__(
62
+ self,
63
+ dataset_name: str,
64
+ tokenizer,
65
+ max_length: int = 2048,
66
+ split: str = "train",
67
+ text_column: str = "text",
68
+ streaming: bool = True,
69
+ ):
70
+ self.dataset = load_dataset(dataset_name, split=split, streaming=streaming)
71
+ self.tokenizer = tokenizer
72
+ self.max_length = max_length
73
+ self.text_column = text_column
74
+
75
+ def __iter__(self):
76
+ buffer = []
77
+ for example in self.dataset:
78
+ text = example.get(self.text_column, "")
79
+ if not text:
80
+ continue
81
+ tokens = self.tokenizer.encode(text, add_special_tokens=False)
82
+ buffer.extend(tokens)
83
+
84
+ while len(buffer) >= self.max_length:
85
+ chunk = buffer[:self.max_length]
86
+ buffer = buffer[self.max_length:]
87
+
88
+ input_ids = torch.tensor(chunk, dtype=torch.long)
89
+ labels = input_ids.clone()
90
+
91
+ yield {
92
+ "input_ids": input_ids,
93
+ "labels": labels,
94
+ }
95
+
96
+
97
+ def collate_fn(batch):
98
+ """Collate function for DataLoader."""
99
+ input_ids = torch.stack([item["input_ids"] for item in batch])
100
+ labels = torch.stack([item["labels"] for item in batch])
101
+ return {"input_ids": input_ids, "labels": labels}
102
+
103
+
104
+ # =============================================================================
105
+ # Custom Callbacks
106
+ # =============================================================================
107
+
108
+ class DeepSeekV4Callback(TrainerCallback):
109
+ """Custom callback for DeepSeek-V4 training monitoring."""
110
+
111
+ def on_log(self, args, state, control, logs=None, **kwargs):
112
+ if logs is None:
113
+ return
114
+
115
+ step = state.global_step
116
+ loss = logs.get("loss")
117
+ lr = logs.get("learning_rate")
118
+
119
+ if loss is not None:
120
+ # Check for training issues
121
+ if math.isnan(loss) or math.isinf(loss):
122
+ logger.error(f"Step {step}: Loss is {loss}! Training may be diverging.")
123
+ elif loss > 20 and step > 100:
124
+ logger.warning(f"Step {step}: Loss={loss:.4f} is high. Consider reducing LR.")
125
+
126
+ def on_train_begin(self, args, state, control, **kwargs):
127
+ logger.info("=" * 60)
128
+ logger.info("DeepSeek-V4 Training Started")
129
+ logger.info(f" Max steps: {args.max_steps}")
130
+ logger.info(f" Batch size: {args.per_device_train_batch_size}")
131
+ logger.info(f" Gradient accumulation: {args.gradient_accumulation_steps}")
132
+ logger.info(f" Learning rate: {args.learning_rate}")
133
+ logger.info(f" Hardware: {torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'CPU'}")
134
+ logger.info("=" * 60)
135
+
136
+
137
+ # =============================================================================
138
+ # Training Entry Point
139
+ # =============================================================================
140
+
141
+ def main():
142
+ parser = argparse.ArgumentParser(description="Train DeepSeek-V4")
143
+
144
+ # Model & Data
145
+ parser.add_argument("--config", type=str, required=True, help="Path to model config JSON")
146
+ parser.add_argument("--dataset", type=str, default="HuggingFaceFW/fineweb-edu-score-2", help="Dataset name on HF Hub")
147
+ parser.add_argument("--tokenizer", type=str, default="deepseek-ai/DeepSeek-V4-Pro", help="Tokenizer name")
148
+ parser.add_argument("--max_length", type=int, default=2048, help="Max sequence length")
149
+ parser.add_argument("--text_column", type=str, default="text", help="Text column in dataset")
150
+
151
+ # Training hyperparameters (V4 paper recipe)
152
+ parser.add_argument("--max_steps", type=int, default=10000, help="Max training steps")
153
+ parser.add_argument("--batch_size", type=int, default=4, help="Per-device batch size")
154
+ parser.add_argument("--gradient_accumulation_steps", type=int, default=8, help="Gradient accumulation")
155
+ parser.add_argument("--learning_rate", type=float, default=2.2e-4, help="Peak learning rate")
156
+ parser.add_argument("--min_lr_ratio", type=float, default=0.033, help="Min LR as ratio of peak")
157
+ parser.add_argument("--warmup_steps", type=int, default=2000, help="Warmup steps")
158
+ parser.add_argument("--weight_decay", type=float, default=0.1, help="Weight decay")
159
+ parser.add_argument("--grad_clip", type=float, default=1.0, help="Max gradient norm")
160
+
161
+ # MTP
162
+ parser.add_argument("--mtp_loss_weight", type=float, default=0.3, help="MTP auxiliary loss weight")
163
+
164
+ # Infrastructure
165
+ parser.add_argument("--output_dir", type=str, default="./output", help="Output directory")
166
+ parser.add_argument("--hub_model_id", type=str, default=None, help="Push to this HF Hub model ID")
167
+ parser.add_argument("--logging_steps", type=int, default=10, help="Log every N steps")
168
+ parser.add_argument("--save_steps", type=int, default=1000, help="Save every N steps")
169
+ parser.add_argument("--bf16", action="store_true", default=True, help="Use BF16 mixed precision")
170
+ parser.add_argument("--gradient_checkpointing", action="store_true", default=True, help="Enable gradient checkpointing")
171
+ parser.add_argument("--seed", type=int, default=42, help="Random seed")
172
+
173
+ args = parser.parse_args()
174
+
175
+ # Load config
176
+ with open(args.config) as f:
177
+ config_dict = json.load(f)
178
+ config = DeepSeekV4Config(**config_dict)
179
+
180
+ logger.info(f"Model config: {config.hidden_size}d, {config.num_hidden_layers}L, "
181
+ f"{config.n_routed_experts}E, hc_mult={config.hc_mult}")
182
+
183
+ # Initialize model
184
+ model = DeepSeekV4ForCausalLM(config)
185
+
186
+ total_params = sum(p.numel() for p in model.parameters())
187
+ logger.info(f"Total parameters: {total_params:,} ({total_params/1e9:.2f}B)")
188
+
189
+ # Load tokenizer
190
+ tokenizer = AutoTokenizer.from_pretrained(args.tokenizer, trust_remote_code=True)
191
+ if tokenizer.pad_token is None:
192
+ tokenizer.pad_token = tokenizer.eos_token
193
+
194
+ # Create dataset
195
+ train_dataset = StreamingTextDataset(
196
+ dataset_name=args.dataset,
197
+ tokenizer=tokenizer,
198
+ max_length=args.max_length,
199
+ text_column=args.text_column,
200
+ )
201
+
202
+ # Training arguments
203
+ training_args = TrainingArguments(
204
+ output_dir=args.output_dir,
205
+ max_steps=args.max_steps,
206
+ per_device_train_batch_size=args.batch_size,
207
+ gradient_accumulation_steps=args.gradient_accumulation_steps,
208
+ learning_rate=args.learning_rate,
209
+ weight_decay=args.weight_decay,
210
+ max_grad_norm=args.grad_clip,
211
+ warmup_steps=args.warmup_steps,
212
+ lr_scheduler_type="cosine",
213
+ bf16=args.bf16 and torch.cuda.is_available(),
214
+ logging_steps=args.logging_steps,
215
+ logging_strategy="steps",
216
+ logging_first_step=True,
217
+ save_steps=args.save_steps,
218
+ save_total_limit=3,
219
+ seed=args.seed,
220
+ disable_tqdm=True,
221
+ gradient_checkpointing=args.gradient_checkpointing,
222
+ push_to_hub=args.hub_model_id is not None,
223
+ hub_model_id=args.hub_model_id,
224
+ report_to="none", # override to "trackio" when using monitoring
225
+ dataloader_pin_memory=True,
226
+ remove_unused_columns=False,
227
+ )
228
+
229
+ # Create trainer
230
+ trainer = Trainer(
231
+ model=model,
232
+ args=training_args,
233
+ train_dataset=train_dataset,
234
+ data_collator=collate_fn,
235
+ callbacks=[DeepSeekV4Callback()],
236
+ )
237
+
238
+ # Train
239
+ logger.info("Starting training...")
240
+ train_result = trainer.train()
241
+
242
+ # Save final model
243
+ trainer.save_model()
244
+
245
+ # Log metrics
246
+ metrics = train_result.metrics
247
+ trainer.log_metrics("train", metrics)
248
+ trainer.save_metrics("train", metrics)
249
+
250
+ logger.info("Training complete!")
251
+ logger.info(f"Final loss: {metrics.get('train_loss', 'N/A')}")
252
+
253
+ if args.hub_model_id:
254
+ logger.info(f"Pushing to Hub: {args.hub_model_id}")
255
+ trainer.push_to_hub()
256
+
257
+
258
+ if __name__ == "__main__":
259
+ main()