CoolFace
Modelpublic

Impliedhomeland/pythia-410m-wsd-24.6B-c4-backbone

sourceHugging Faceapache-2.0updated 1mo agoView on Hugging Face
0likes
Model Card

Pythia-410M WSD C4 backbone (24.6B, complete)

Resumable training checkpoints for the C4-only backbone (branch_A) of a code-introduction timing study at 410M scale, trained under a WSD (warmup-stable-decay) learning-rate schedule. This backbone serves two roles: it is the no-code reference arm, and its intermediate snapshots are the fork points from which code-mixing branches are launched.

This is the WSD twin of `Impliedhomeland/pythia-410m-cosine-24.6B-c4-backbone`. Same architecture, same 24.6B token budget, same batch, same data, same seed. The learning-rate schedule is the only intended difference. See "LR schedule" below, then "Comparison to the cosine twin".

The backbone is complete: the full 24.6B schedule (step 12513) ran to the end of its decay phase, and all 14 snapshots plus the final checkpoint are published here. Final held-out loss is val_c4 3.0060 / val_code 3.2550.

Note on the wider study. This repo contains the backbone only. The code-mixing branches that fork from these checkpoints (42 arms across four compute budgets) are not published here. val_code on this backbone is a zero-code-exposure baseline: it was trained on C4 alone (starcoder_weight: 0.0), so its Starcoder loss reflects pure transfer, not code training.

LR schedule

The defining property of this backbone. WSD, peak 3e-4, floor 1e-6, keyed to absolute token count over a 24.6B horizon:

phasetokensfractionlearning rate
warmup0 to 2.4615B0 to 10%linear 0 to 3e-4
stable2.4615B to 22.14B10% to 90%constant 3e-4
decay22.14B to 24.60B90% to 100%3e-4 down to 1e-6

Config keys that produce it:

yaml
lr_schedule: wsd
peak_lr: 0.0003          # 3e-4
min_lr: 1.0e-06          # 1e-6
lr_t_max_tokens: 24600000000
warmup_frac: 0.1         # warmup_tokens   = 2.4615e9
decay_start_frac: 0.9    # decay_start     = 2.214e10

Two consequences worth stating explicitly:

  1. 1.Every snapshot before 22.14B sits at the full 3e-4, not at a partially annealed LR. This is the practical reason to prefer a WSD backbone for fork-based studies: forks taken at 20%, 50% and 80% of the schedule all start from the same LR, so differences between branches are not confounded by where the fork happened to fall on a decaying curve. Under the cosine twin they are.
  2. 2.Only the last 10% is annealed. branch_A_22.14B_step11261.pt is the last stable-phase checkpoint and the start of decay; branch_A_23.37B, branch_A_23.99B and the 24.60B final are partway down and at the bottom of the decay respectively.

The schedule is keyed to absolute tokens, so a branch forked at 19.68B continues the same WSD curve rather than restarting it.

What is here

Every file is a full PyTorch checkpoint containing weights + optimizer state + RNG state, so training continues rather than merely warm-starting:

keycontents
model292 tensors, 405.3M params, fp32
optimizerAdamW state (exp_avg, exp_avg_sq)
completed_steps, global_tokensposition in the schedule
configthe fully resolved run config
torch_rng, numpy_rngRNG states, so the data stream continues
val_c4, val_codeheld-out loss at that point

That is why each file is 4.86 GB rather than ~1.6 GB weights-only.

Checkpoints

branch_A_*.pt are the fork points, named by token position. The "fork for" column names an example branch forked there (arms exist at four compute budgets; the % differs per budget, the @ is the fork fraction).

filesteptokensfractionLR phasefork for
trunk_branchpoint.pt12522.46B10%warmup end*@10
branch_A_4.92B_step2503.pt25034.92B20%stable6.25%@20
branch_A_7.38B_step3754.pt37547.38B30%stable(snapshot only)
branch_A_9.84B_step5005.pt50059.84B40%stable8.33%@40
branch_A_12.30B_step6257.pt625712.30B50%stable10%@50
branch_A_14.76B_step7508.pt750814.76B60%stable12.5%@60
branch_A_17.22B_step8759.pt875917.22B70%stable16.6%@70
branch_A_18.45B_step9385.pt938518.45B75%stable20%@75
branch_A_19.68B_step10010.pt1001019.68B80%stable25%@80
branch_A_20.17B_step10261.pt1026120.17B82%stable100%@82
branch_A_20.91B_step10636.pt1063620.91B85%stable33.3%@85
branch_A_22.14B_step11261.pt1126122.14B90%decay start50%@90
branch_A_23.37B_step11887.pt1188723.37B95%decaying100%@95
branch_A_23.99B_step12200.pt1220023.99B97.5%decaying100%@97.5
`branch_A_24.60B_step12513.pt`1251324.60B100%fully decayedfinal
latest.pt1251324.60B--identical to the final

branch_A_24.60B_step12513.pt is the one to use for the finished no-code 410M reference model. trunk_branchpoint.pt is the 2.4615B warmup-end root that branch_A itself forked from, and is the fork point for the @10 arms.

branch_A_7.38B_step3754.pt (30%) was snapshotted but no arm in the current sweep forks there. It is included for trajectory work.

Also included: dm_branch_A.yaml (run config), resolved_config.json, metrics.jsonl (full eval history, 36 points), and code/ (the training scripts needed to resume).

Training setup

architecturePythia-410M (litgpt pythia-410m), seq len 2048
tokens24.6B (step 12513), schedule completed
LR scheduleWSD, peak 3e-4, floor 1e-6 (see above)
warmup10% of 24.6B = 2.4615B, linear 0 to peak
batchglobal 960 sequences, micro 16, so 1,966,080 tokens/step
optimizerAdamW, betas (0.9, 0.95), wd 0.1, grad clip 1.0
precisionbf16 autocast, fp32 master weights
dataC4 only (starcoder_weight: 0.0), block order seed 1
parallelismDDP, 4x H100 (SXM, 80 GB), micro_batch 16 at ~548K tok/s

global_batch 960 rather than 1024 is deliberate: 1024 is not divisible by 3, which makes a 3-GPU DDP lane impossible. Because of it, step counts are not comparable to 1024-batch runs at the same token budget. Compare by token count.

Validation curve

Held-out loss, 512 packed blocks. val_code is Starcoder, on which this backbone was never trained, so it is a zero-code-exposure baseline and its decline is pure transfer from C4.

steptokensfractionval_c4val_code
25034.92B20%3.71194.1731
37547.38B30%3.45243.8391
50059.84B40%3.31983.7102
625712.30B50%3.24223.5600
750814.76B60%3.18383.5207
875917.22B70%3.14083.4407
938518.45B75%3.12613.4216
1001019.68B80%3.11193.3798
1026120.17B82%3.10433.3667
1063620.91B85%3.09763.3915
1126122.14B90%3.08663.3637
1188723.37B95%3.04443.3289
1220023.99B97.5%3.02233.2727
1251324.60B100%3.00603.2550

The decay phase is visible: val_c4 moves only 0.0143 across the whole 80% to 90% stable stretch (3.1119 to 3.0866), then 0.0806 across the final 10% as the LR anneals (3.0866 to 3.0060).

Comparison to the cosine twin

Identical except for the LR schedule. Final held-out loss:

backboneLR scheduleval_c4val_code
WSD3e-4 stable, decay to 1e-6 in last 10%3.00603.2550
cosine3e-4 cosine to 3e-53.07273.3789

The WSD run ends lower on both, which is the expected direction for a schedule that holds peak LR far longer and anneals hard at the end. Treat this as a single-seed observation on one 410M run, not a general claim about the two schedules.

Note the fork-point sets differ between the two repos. The cosine backbone snapshots at 15/25/35/45% (fine-grained early) while this one snapshots at 75/82/85/97.5% (fine-grained late), because the two sweeps ask about different regions of the schedule. Both cover 20/30/40/50/60/70/80/90/95/100%.

Using these checkpoints

Load the final model

python
import torch
from litgpt.config import Config
from litgpt.model import GPT

ck  = torch.load("branch_A_24.60B_step12513.pt", map_location="cpu", weights_only=False)
cfg = Config.from_name("pythia-410m", block_size=2048)
m   = GPT(cfg); m.load_state_dict(ck["model"]); m.eval()
print(ck["completed_steps"], ck["global_tokens"], ck["val_c4"], ck["val_code"])

Fork a code branch from a mid-schedule point

Point init_from at the fork whose token position you want code to start at, and set the mix:

yaml
init_from: branch_A_19.68B_step10010.pt   # code enters at 80% of the schedule
data_mode: continue
lr_schedule: wsd
peak_lr: 0.0003
min_lr: 1.0e-06
warmup_frac: 0.1
decay_start_frac: 0.9
lr_t_max_tokens: 24600000000
starcoder_weight: 0.25                    # W, the code fraction after the fork
mix_start_frac: 0.8                       # must match the fork's fraction
end_frac: 1.0
bash
hf download Impliedhomeland/pythia-410m-wsd-24.6B-c4-backbone --include 'code/*' --local-dir .
torchrun --standalone --nproc_per_node=4 code/train.py --config your_branch.yaml

Keep the four LR keys identical to the values above when forking. The schedule is a function of absolute token count, so a fork that changes peak_lr, warmup_frac or decay_start_frac is no longer continuing this curve and is not comparable to the other arms.

train.py auto-resumes from latest.pt if one is present in ckpt_dir, restoring optimizer and RNG state.

Dependencies: torch, litgpt, numpy, pyyaml (plus datasets/transformers for prepare_data.py).

What is NOT in this repo, and what you must rebuild

The tokenized data pools. The loader replays a deterministic block order over pre-tokenized memmaps, too large to host here:

poolsize
c4_train.bin80.2 GB
code_train.bin14.0 GB
c4_val.bin, code_val.bin~30 MB each

Rebuild with code/prepare_data.py, which pulls C4 and StarcoderData and writes the .bin/.json pair the loader expects. Pre-tokenized pools for the smaller sweeps are published at `Impliedhomeland/midtrain-bridge-data`.

Exact reproducibility caveat, stated plainly. RNG state is restored, but the data order is reconstructed from c4_base_seed: 1 and code_interleave_seed: 0 over your rebuilt pool. If your tokenization differs at all (tokenizer version, shard order, document filtering), the resumed run consumes different tokens from the original. The optimizer trajectory continues correctly, but the data sequence is not guaranteed identical across a rebuild. Byte-identical continuation requires the original .bin files, which are not published.

Intended use

A research artifact for continual-pretraining and data-mixing work, and the starting point for the fork-based experiment design it belongs to. It is not instruction-tuned, aligned, or safety-filtered, and it has had no post-training of any kind. Even the fully-decayed 24.6B final is a 410M base model trained on 24.6B tokens, so its generations are correspondingly limited; the earlier snapshots are mid-schedule and weaker still. Inherits the licensing and content characteristics of C4.