Download code/modal_app/train_dory.py from ProCreations/Dory-Beam-4B: direct link, hf CLI and curl.
- Browser
- Download file 3.4 kB
-
https://huggingface.co/ProCreations/Dory-Beam-4B/resolve/main/code/modal_app/train_dory.py
- Command line
-
hf download hf://ProCreations/Dory-Beam-4B/code/modal_app/train_dory.py
-
curl -L -o train_dory.py https://huggingface.co/ProCreations/Dory-Beam-4B/resolve/main/code/modal_app/train_dory.py
3.4 kB
| """Train Dory1.0-4B on Beam traces on Modal (torchrun + DDP on B200s). | |
| modal run modal_app/train_dory.py::download_dory # one-time: base weights -> Volume | |
| modal run modal_app/train_dory.py --gpus 1 --run smoke --extra "--max-steps 20" | |
| modal run modal_app/train_dory.py --gpus 8 --run r1 --data sft/sft_verified.jsonl | |
| Data files are paths inside the `beam-dory-data` Volume (upload with `modal volume put`). | |
| Checkpoints land in the `beam-dory-ckpt` Volume under runs/<run>/. | |
| """ | |
| import shlex | |
| import subprocess | |
| import time | |
| import modal | |
| APP = modal.App("beam-dory-train") | |
| weights = modal.Volume.from_name("beam-weights", create_if_missing=True, version=2) | |
| data = modal.Volume.from_name("beam-dory-data", create_if_missing=True, version=2) | |
| ckpt = modal.Volume.from_name("beam-dory-ckpt", create_if_missing=True, version=2) | |
| W, D, C = "/weights", "/data", "/ckpt" | |
| DORY_REPO = "nvidia/NVIDIA-Nemotron-Labs-Dory1.0-4B-BF16" | |
| image = ( | |
| modal.Image.debian_slim(python_version="3.12") | |
| .pip_install("torch==2.14.1", "safetensors", "transformers>=5.20", "jinja2", "numpy", | |
| "huggingface_hub[hf_xet]>=0.34") | |
| .env({"PYTHONUNBUFFERED": "1", "TOKENIZERS_PARALLELISM": "false"}) | |
| .add_local_dir("dory", "/proj/dory") | |
| .add_local_dir("scripts", "/proj/scripts") | |
| ) | |
| def download_dory(): | |
| from huggingface_hub import snapshot_download | |
| snapshot_download(DORY_REPO, local_dir=f"{W}/dory-4b", allow_patterns=["*.json", "*.jinja", "*.safetensors"]) | |
| weights.commit() | |
| print(subprocess.run(["ls", "-la", f"{W}/dory-4b"], capture_output=True, text=True).stdout) | |
| def train(run: str, data_files: list, nproc: int = 0, extra: str = ""): | |
| """nproc=0 uses every GPU in the container (whichever fallback config was allocated).""" | |
| if not nproc: | |
| out_ = subprocess.run(["nvidia-smi", "--query-gpu=name", "--format=csv,noheader"], capture_output=True, text=True) | |
| names = [l for l in out_.stdout.splitlines() if l.strip()] | |
| nproc = len(names) | |
| print(f"got {nproc}x {names[0]}", flush=True) | |
| if "--grad-accum" not in extra: # keep the global batch of 8 GPUs x 1 sequence on smaller fallbacks | |
| extra += f" --grad-accum {max(1, 8 // nproc)}" | |
| out = f"{C}/runs/{run}" | |
| cmd = ["torchrun", "--standalone", f"--nproc_per_node={nproc}", "/proj/scripts/train_dory.py", | |
| "--model", f"{W}/dory-4b", "--out", out, "--data", *[f"{D}/{f}" for f in data_files], | |
| *shlex.split(extra)] | |
| print("cmd:", " ".join(cmd), flush=True) | |
| t0 = time.time() | |
| proc = subprocess.run(cmd, cwd="/proj") | |
| ckpt.commit() | |
| print(f"exit {proc.returncode} after {(time.time() - t0) / 60:.1f} min", flush=True) | |
| if proc.returncode != 0: | |
| raise RuntimeError(f"training failed with exit code {proc.returncode}") | |
| def main(run: str, data: str = "sft/sft_verified.jsonl", gpus: int = 8, extra: str = ""): | |
| """Interactive runs (smoke tests). Long runs: deploy, then spawn via scripts/spawn_train.py.""" | |
| files = [f.strip() for f in data.split(",") if f.strip()] | |
| train.with_options(gpu=f"B200:{gpus}").remote(run, files, gpus, extra) | |