Download code/scripts/pretrain.py from viai957/align: direct link, hf CLI and curl.
- Browser
- Download file 8.12 kB
-
https://huggingface.co/viai957/align/resolve/main/code/scripts/pretrain.py
- Command line
-
hf download hf://viai957/align/code/scripts/pretrain.py
-
curl -L -o pretrain.py https://huggingface.co/viai957/align/resolve/main/code/scripts/pretrain.py
8.12 kB
| #!/usr/bin/env python | |
| """Pretraining stage entry (section-14): gates -> wrap -> train -> checkpoint. | |
| torchrun --nproc_per_node=4 scripts/pretrain.py configs/pretrain.toml --optimizer.lr=3e-5 | |
| The Pretraining pillar, OLMoE-shaped and honestly toy-scaled: continued | |
| pretraining of the base model on a capped (~50M token), STREAMED slice of a | |
| raw-text corpus, packed into the repo's shard format by | |
| align/data/pretrain_corpus.py. The objective is the SAME packed next-token NLL | |
| the SFT stage computes -- every pretrain row stores prompt_len 0, so | |
| `collate_sft` labels every token (labels == input_ids, no masking) and | |
| `make_sft_loss_adapter` runs unchanged. One loss path, two stages. | |
| Thin by design, mirroring scripts/sft.py: everything this drives is built and | |
| tested elsewhere; what lives here is ORDER: | |
| build_currency -> smoke_7b -> probe_20step -> rss_preflight -> train | |
| Differences from the SFT entry, each deliberate: | |
| * NO golden-regression bridge. The golden anchor certifies the SFT eval | |
| stack (MATH-500/GSM8K extraction against known scores); a base-style | |
| pretrain checkpoint has no such anchor. configs/pretrain.toml sets | |
| gates.require_golden_receipt: false, which run_entry_gates honors with a | |
| loud warning rather than silence. | |
| * NO accuracy quality gate at exit. The stage's product is a checkpoint and | |
| its train-NLL trajectory; downstream SFT is its consumer and its judge. | |
| IMPORT ORDER IS LOAD-BEARING. `apply_resource_guards()` sets | |
| PYTORCH_CUDA_ALLOC_CONF, the /apps cache paths and the thread caps, and torch | |
| reads all of them AT IMPORT. So this module imports only stdlib, the guard, | |
| the config, and `stage_gates` (which is asserted torch-free); torch and every | |
| align module that pulls it in are imported INSIDE main(), after the guards run. | |
| tests/test_pretrain_entry.py enforces this by AST, exactly as the SFT test | |
| does for scripts/sft.py. | |
| Resume semantics: rerunning after a kill picks up the gate ledger, skips gates | |
| that already passed, and resumes the Trainer from the latest micro-step | |
| checkpoint. Partial checkpoints are deleted at startup. | |
| """ | |
| from __future__ import annotations | |
| import logging | |
| import os | |
| import sys | |
| from pathlib import Path | |
| # The package is not installed in the venv; without this a bare `python scripts/pretrain.py` (the documented | |
| # launch line) dies with ModuleNotFoundError unless PYTHONPATH is exported (same fix as scripts/sft.py). | |
| sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) | |
| from align.config import load_config | |
| from align.train.stage_gates import ( | |
| GateLedger, | |
| assert_build_is_current, | |
| assert_probe_resolved, | |
| eval_source_hash, | |
| parse_args, | |
| run_entry_gates, | |
| ) | |
| from align.utils.guard import apply_resource_guards | |
| logger = logging.getLogger(__name__) | |
| def build_model(cfg): | |
| """Imported here, not at module scope: this pulls torch in.""" | |
| from align.train.stage_smoke import build_stage_model | |
| return build_stage_model(cfg) | |
| def run_gates(cfg, ledger): | |
| """The entry sequence. Refuses to proceed on a stale or unstamped corpus | |
| build before it touches a GPU. The golden bridge is intentionally absent | |
| (see module docstring); run_entry_gates still calls require_golden_receipt | |
| internally, which configs/pretrain.toml downgrades to a warning via | |
| gates.require_golden_receipt: false.""" | |
| assert_build_is_current(cfg.data.manifest_path, cfg) | |
| banner = run_entry_gates(cfg, ledger) | |
| assert_probe_resolved(banner) | |
| return banner | |
| def train(cfg, ledger, model, banner): | |
| """One pretraining epoch through the section-07 Trainer. | |
| Identical wiring to the SFT stage on purpose: same sampler construction | |
| (the SAME sampler the entry gates measured the horizon from), same | |
| collate, same fused loss adapter. The pretraining objective difference | |
| lives entirely in the data -- prompt_len 0 makes collate_sft emit | |
| labels == input_ids.""" | |
| from align.data.loader import collate_sft | |
| from align.train.sft_loss import make_sft_loss_adapter | |
| from align.train.stage_smoke import build_training_sampler | |
| from align.train.trainer import Trainer | |
| sampler, dataset = build_training_sampler(cfg) | |
| def adapter(m, batch): | |
| rows = [dataset[i] for i in batch] | |
| collated = collate_sft(rows, pad_token_id=cfg.data.pad_token_id).to("cuda") | |
| return make_sft_loss_adapter(require_fused=True)(m, (collated.input_ids, collated.labels)) | |
| trainer = Trainer( | |
| cfg=cfg, | |
| model=model, | |
| sampler=sampler, | |
| loss_adapter=adapter, | |
| run_dir=os.path.join(cfg.paths.checkpoint_dir, "pretrain"), | |
| stage="pretrain", | |
| ) | |
| trainer.resume_from_latest() | |
| summary = trainer.train() | |
| record_train_gate(ledger, trainer, summary) | |
| return trainer | |
| def record_train_gate(ledger, trainer, summary) -> dict: | |
| """Record the 'train' gate for what `trainer.train()` ACTUALLY did (same contract as | |
| scripts/sft.py::record_train_gate; duplicated because scripts/ is not a package). | |
| THE STOPPED-RECORDED-AS-PASSED DEFECT (H2 review, 2026-10-13; the sibling the first | |
| fix missed). `train()` returns normally after a COORDINATED STOP (SIGTERM or | |
| train.max_wall_seconds) and this used to record `passed=True` unconditionally, so the | |
| ledger certified a half-trained checkpoint as a finished stage while `main` exited | |
| 128+n / 82. A stopped run is resumable, neither finished nor failed; the ledger has no | |
| third state, so it is `passed=False` with a `STOPPED (resumable)` detail, and the real | |
| verdict overwrites it when the resumed run finishes. | |
| """ | |
| step = summary.get("optimizer_step") | |
| if getattr(trainer, "stop_exit_code", 0): | |
| return ledger.record( | |
| "train", | |
| passed=False, | |
| detail=f"STOPPED (resumable): {trainer.stop_reason} stop at optimizer_step={step}; " | |
| "checkpointed, not finished", | |
| ) | |
| return ledger.record("train", passed=True, detail=str(step)) | |
| def main(argv=None) -> int: | |
| args = parse_args(argv) | |
| # FIRST: every env var torch reads at import, plus the heavy-job flock and | |
| # the /apps path assertions. `heavy=True` also runs the host preflight; | |
| # `rss_cfg` adds the full-FT RSS projection on top of the generic floor. | |
| cfg = load_config(args.config, args.overrides) | |
| apply_resource_guards(cpu_threads=8, heavy=True, label="pretrain-stage-14", rss_cfg=cfg) | |
| logging.basicConfig( | |
| level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s: %(message)s" | |
| ) | |
| # BEFORE anything touches CUDA -- the duplicate-GPU defect (see | |
| # scripts/sft.py): torchrun assigns no devices, so every rank's `.cuda()` | |
| # lands on device 0 unless init_distributed sets one per local rank. | |
| from align.utils.dist import init_distributed | |
| run_dir = os.path.join(cfg.paths.checkpoint_dir, "pretrain") | |
| # run_dir: the NCCL flight-recorder dump lands under it (never /tmp). | |
| init_distributed(run_dir=run_dir) | |
| from align.train.stage_gates import assert_distinct_devices | |
| assert_distinct_devices() | |
| ledger = GateLedger(run_dir, eval_source_hash=eval_source_hash()) | |
| banner = run_gates(cfg, ledger) | |
| from align.registry import render_banner | |
| print(render_banner(banner, title=f"align pretrain stage :: {cfg.run_name}")) | |
| model = build_model(cfg) | |
| trainer = train(cfg, ledger, model, banner) | |
| if trainer.stop_exit_code: | |
| # A coordinated stop (SIGTERM / train.max_wall_seconds) saved and | |
| # returned: the stage is NOT finished, so do not fall into its | |
| # post-train eval or announce completion. 82 = wall budget (the | |
| # supervisor will not relaunch it), 128+n = the signal. | |
| print(f"{trainer.stop_reason} stop: checkpointed, exiting {trainer.stop_exit_code}", file=sys.stderr) | |
| return trainer.stop_exit_code | |
| checkpoint = trainer.latest_checkpoint() | |
| print(f"Pretrain stage complete. SFT base checkpoint: {checkpoint}") | |
| return 0 | |
| if __name__ == "__main__": | |
| from align.exit_codes import run_with_exit_codes | |
| raise SystemExit(run_with_exit_codes(main)) | |