Andrej Karpathy commited on
Commit ·
2c062aa
1
Parent(s): f41dd3c
nit: don't mutate args, create new var for total_batch_size
Browse files- scripts/base_train.py +18 -16
scripts/base_train.py
CHANGED
|
@@ -256,13 +256,15 @@ num_scaling_params = get_scaling_params(model)
|
|
| 256 |
target_tokens = int(args.target_param_data_ratio * num_scaling_params)
|
| 257 |
|
| 258 |
# Auto-compute optimal batch size based on Power Lines paper (Bopt ∝ D^0.383), ref: https://arxiv.org/abs/2505.13738
|
| 259 |
-
|
|
|
|
| 260 |
d12_ref = build_model_meta(12) # d12 is where the optimal batch size was measured to be 2**19 tokens
|
| 261 |
d12_num_scaling_params = get_scaling_params(d12_ref)
|
| 262 |
D_REF = args.target_param_data_ratio * d12_num_scaling_params
|
| 263 |
B_REF = 2**19
|
| 264 |
-
|
| 265 |
-
|
|
|
|
| 266 |
|
| 267 |
# Calculate number of iterations. Either it is given, or from target flops, or from target data:param ratio (in that order)
|
| 268 |
assert args.num_iterations > 0 or args.target_param_data_ratio > 0 or args.target_flops > 0
|
|
@@ -272,17 +274,17 @@ if args.num_iterations > 0:
|
|
| 272 |
print0(f"Using user-provided number of iterations: {num_iterations:,}")
|
| 273 |
elif args.target_flops > 0:
|
| 274 |
# Calculate the number of iterations from the target flops (used in scaling laws analysis, e.g. runs/scaling_laws.sh)
|
| 275 |
-
num_iterations = round(args.target_flops / (num_flops_per_token *
|
| 276 |
print0(f"Calculated number of iterations from target FLOPs: {num_iterations:,}")
|
| 277 |
elif args.target_param_data_ratio > 0:
|
| 278 |
# Calculate the number of iterations from the target param data ratio (the most common use case)
|
| 279 |
-
num_iterations = target_tokens //
|
| 280 |
print0(f"Calculated number of iterations from target data:param ratio: {num_iterations:,}")
|
| 281 |
else:
|
| 282 |
raise ValueError("No training horizon specified")
|
| 283 |
-
total_tokens =
|
| 284 |
print0(f"Total number of training tokens: {total_tokens:,}")
|
| 285 |
-
print0(f"Tokens : Scaling params ratio: {
|
| 286 |
print0(f"Total training FLOPs estimate: {num_flops_per_token * total_tokens:e}")
|
| 287 |
|
| 288 |
# -----------------------------------------------------------------------------
|
|
@@ -290,22 +292,22 @@ print0(f"Total training FLOPs estimate: {num_flops_per_token * total_tokens:e}")
|
|
| 290 |
# figure out the needed gradient accumulation to reach the desired total batch size
|
| 291 |
tokens_per_fwdbwd = args.device_batch_size * args.max_seq_len # tokens per iteration for a single rank
|
| 292 |
world_tokens_per_fwdbwd = tokens_per_fwdbwd * ddp_world_size # total tokens per iteration for all ranks
|
| 293 |
-
assert
|
| 294 |
-
grad_accum_steps =
|
| 295 |
print0(f"Tokens / micro-batch / rank: {args.device_batch_size} x {args.max_seq_len} = {tokens_per_fwdbwd:,}")
|
| 296 |
print0(f"Tokens / micro-batch: {world_tokens_per_fwdbwd:,}")
|
| 297 |
-
print0(f"Total batch size {
|
| 298 |
|
| 299 |
# Batch size scaling for learning rates (hyperparameters were tuned at reference batch size 2^19)
|
| 300 |
batch_lr_scale = 1.0
|
| 301 |
reference_batch_size = 2**19
|
| 302 |
-
batch_ratio =
|
| 303 |
if batch_ratio != 1.0:
|
| 304 |
# SGD: linear scaling with batch size is standard (not used in nanochat)
|
| 305 |
# AdamW: sqrt scaling is standard
|
| 306 |
# Muon: sqrt scaling is an assumption - not fully studied, but it's a second-order-ish optimizer
|
| 307 |
batch_lr_scale = batch_ratio ** 0.5
|
| 308 |
-
print0(f"Scaling LRs by {batch_lr_scale:.4f} for batch size {
|
| 309 |
|
| 310 |
# Weight decay is tuned at d12 and its scaling seems to be \propto 1/channels^2 (or equivalently, \propto 1/depth^2 due to constant aspect ratio)
|
| 311 |
weight_decay_scaled = args.weight_decay * (12 / args.depth)**2
|
|
@@ -381,7 +383,7 @@ else:
|
|
| 381 |
# Training loop
|
| 382 |
while True:
|
| 383 |
last_step = step == num_iterations # loop runs num_iterations+1 times so that we can eval/save at the end
|
| 384 |
-
flops_so_far = num_flops_per_token *
|
| 385 |
|
| 386 |
# once in a while: evaluate the val bpb (all ranks participate)
|
| 387 |
if args.eval_every > 0 and (last_step or step % args.eval_every == 0):
|
|
@@ -501,8 +503,8 @@ while True:
|
|
| 501 |
smooth_train_loss = ema_beta * smooth_train_loss + (1 - ema_beta) * train_loss_f # EMA the training loss
|
| 502 |
debiased_smooth_loss = smooth_train_loss / (1 - ema_beta**(step + 1)) # debias the EMA
|
| 503 |
pct_done = 100 * step / num_iterations
|
| 504 |
-
tok_per_sec = int(
|
| 505 |
-
flops_per_sec = num_flops_per_token *
|
| 506 |
mfu = 100 * flops_per_sec / (gpu_peak_flops * ddp_world_size)
|
| 507 |
if step > 10:
|
| 508 |
total_training_time += dt # only count the time after the first 10 steps
|
|
@@ -560,7 +562,7 @@ get_report().log(section="Base model training", data=[
|
|
| 560 |
"Number of FLOPs per token": f"{num_flops_per_token:e}",
|
| 561 |
"Calculated number of iterations": num_iterations,
|
| 562 |
"Number of training tokens": total_tokens,
|
| 563 |
-
"Tokens : Scaling params ratio":
|
| 564 |
"DDP world size": ddp_world_size,
|
| 565 |
"warmup_ratio": args.warmup_ratio,
|
| 566 |
"warmdown_ratio": args.warmdown_ratio,
|
|
|
|
| 256 |
target_tokens = int(args.target_param_data_ratio * num_scaling_params)
|
| 257 |
|
| 258 |
# Auto-compute optimal batch size based on Power Lines paper (Bopt ∝ D^0.383), ref: https://arxiv.org/abs/2505.13738
|
| 259 |
+
total_batch_size = args.total_batch_size
|
| 260 |
+
if total_batch_size == -1:
|
| 261 |
d12_ref = build_model_meta(12) # d12 is where the optimal batch size was measured to be 2**19 tokens
|
| 262 |
d12_num_scaling_params = get_scaling_params(d12_ref)
|
| 263 |
D_REF = args.target_param_data_ratio * d12_num_scaling_params
|
| 264 |
B_REF = 2**19
|
| 265 |
+
batch_size_ratio = target_tokens / D_REF
|
| 266 |
+
total_batch_size = 2 ** round(math.log2(B_REF * batch_size_ratio ** 0.383)) # also clamp to power of 2
|
| 267 |
+
print0(f"Auto-computed optimal batch size: {total_batch_size:,} tokens")
|
| 268 |
|
| 269 |
# Calculate number of iterations. Either it is given, or from target flops, or from target data:param ratio (in that order)
|
| 270 |
assert args.num_iterations > 0 or args.target_param_data_ratio > 0 or args.target_flops > 0
|
|
|
|
| 274 |
print0(f"Using user-provided number of iterations: {num_iterations:,}")
|
| 275 |
elif args.target_flops > 0:
|
| 276 |
# Calculate the number of iterations from the target flops (used in scaling laws analysis, e.g. runs/scaling_laws.sh)
|
| 277 |
+
num_iterations = round(args.target_flops / (num_flops_per_token * total_batch_size))
|
| 278 |
print0(f"Calculated number of iterations from target FLOPs: {num_iterations:,}")
|
| 279 |
elif args.target_param_data_ratio > 0:
|
| 280 |
# Calculate the number of iterations from the target param data ratio (the most common use case)
|
| 281 |
+
num_iterations = target_tokens // total_batch_size
|
| 282 |
print0(f"Calculated number of iterations from target data:param ratio: {num_iterations:,}")
|
| 283 |
else:
|
| 284 |
raise ValueError("No training horizon specified")
|
| 285 |
+
total_tokens = total_batch_size * num_iterations
|
| 286 |
print0(f"Total number of training tokens: {total_tokens:,}")
|
| 287 |
+
print0(f"Tokens : Scaling params ratio: {total_batch_size * num_iterations / num_scaling_params:.2f}") # Chinchilla is ~20
|
| 288 |
print0(f"Total training FLOPs estimate: {num_flops_per_token * total_tokens:e}")
|
| 289 |
|
| 290 |
# -----------------------------------------------------------------------------
|
|
|
|
| 292 |
# figure out the needed gradient accumulation to reach the desired total batch size
|
| 293 |
tokens_per_fwdbwd = args.device_batch_size * args.max_seq_len # tokens per iteration for a single rank
|
| 294 |
world_tokens_per_fwdbwd = tokens_per_fwdbwd * ddp_world_size # total tokens per iteration for all ranks
|
| 295 |
+
assert total_batch_size % world_tokens_per_fwdbwd == 0
|
| 296 |
+
grad_accum_steps = total_batch_size // world_tokens_per_fwdbwd
|
| 297 |
print0(f"Tokens / micro-batch / rank: {args.device_batch_size} x {args.max_seq_len} = {tokens_per_fwdbwd:,}")
|
| 298 |
print0(f"Tokens / micro-batch: {world_tokens_per_fwdbwd:,}")
|
| 299 |
+
print0(f"Total batch size {total_batch_size:,} => gradient accumulation steps: {grad_accum_steps}")
|
| 300 |
|
| 301 |
# Batch size scaling for learning rates (hyperparameters were tuned at reference batch size 2^19)
|
| 302 |
batch_lr_scale = 1.0
|
| 303 |
reference_batch_size = 2**19
|
| 304 |
+
batch_ratio = total_batch_size / reference_batch_size
|
| 305 |
if batch_ratio != 1.0:
|
| 306 |
# SGD: linear scaling with batch size is standard (not used in nanochat)
|
| 307 |
# AdamW: sqrt scaling is standard
|
| 308 |
# Muon: sqrt scaling is an assumption - not fully studied, but it's a second-order-ish optimizer
|
| 309 |
batch_lr_scale = batch_ratio ** 0.5
|
| 310 |
+
print0(f"Scaling LRs by {batch_lr_scale:.4f} for batch size {total_batch_size:,} (reference: {reference_batch_size:,})")
|
| 311 |
|
| 312 |
# Weight decay is tuned at d12 and its scaling seems to be \propto 1/channels^2 (or equivalently, \propto 1/depth^2 due to constant aspect ratio)
|
| 313 |
weight_decay_scaled = args.weight_decay * (12 / args.depth)**2
|
|
|
|
| 383 |
# Training loop
|
| 384 |
while True:
|
| 385 |
last_step = step == num_iterations # loop runs num_iterations+1 times so that we can eval/save at the end
|
| 386 |
+
flops_so_far = num_flops_per_token * total_batch_size * step
|
| 387 |
|
| 388 |
# once in a while: evaluate the val bpb (all ranks participate)
|
| 389 |
if args.eval_every > 0 and (last_step or step % args.eval_every == 0):
|
|
|
|
| 503 |
smooth_train_loss = ema_beta * smooth_train_loss + (1 - ema_beta) * train_loss_f # EMA the training loss
|
| 504 |
debiased_smooth_loss = smooth_train_loss / (1 - ema_beta**(step + 1)) # debias the EMA
|
| 505 |
pct_done = 100 * step / num_iterations
|
| 506 |
+
tok_per_sec = int(total_batch_size / dt)
|
| 507 |
+
flops_per_sec = num_flops_per_token * total_batch_size / dt
|
| 508 |
mfu = 100 * flops_per_sec / (gpu_peak_flops * ddp_world_size)
|
| 509 |
if step > 10:
|
| 510 |
total_training_time += dt # only count the time after the first 10 steps
|
|
|
|
| 562 |
"Number of FLOPs per token": f"{num_flops_per_token:e}",
|
| 563 |
"Calculated number of iterations": num_iterations,
|
| 564 |
"Number of training tokens": total_tokens,
|
| 565 |
+
"Tokens : Scaling params ratio": total_batch_size * num_iterations / num_scaling_params,
|
| 566 |
"DDP world size": ddp_world_size,
|
| 567 |
"warmup_ratio": args.warmup_ratio,
|
| 568 |
"warmdown_ratio": args.warmdown_ratio,
|