Download scripts/train.py from OneScience-Group/StableNN-Phys: direct link, hf CLI and curl.
- Browser
- Download file 4.75 kB
-
https://huggingface.co/OneScience-Group/StableNN-Phys/resolve/main/scripts/train.py
- Command line
-
hf download hf://OneScience-Group/StableNN-Phys/scripts/train.py
-
curl -L -o train.py https://huggingface.co/OneScience-Group/StableNN-Phys/resolve/main/scripts/train.py
4.75 kB
| import json | |
| import os | |
| from pathlib import Path | |
| import sys | |
| import numpy as np | |
| import torch | |
| import torch.distributed as dist | |
| from torch.nn.parallel import DistributedDataParallel | |
| from torch.utils.data import DataLoader, DistributedSampler, TensorDataset | |
| import yaml | |
| ROOT = Path(__file__).resolve().parents[1] | |
| if str(ROOT) not in sys.path: | |
| sys.path.insert(0, str(ROOT)) | |
| from model.stablenn_phys import StableNNPhys, rollout, rollout_loss | |
| CHECKPOINT_FORMAT_VERSION = "stablenn_phys_checkpoint_v1" | |
| def main(): | |
| cfg = yaml.safe_load((ROOT / "conf/config.yaml").read_text()) | |
| torch.manual_seed(cfg["seed"]) | |
| distributed = int(os.environ.get("WORLD_SIZE", "1")) > 1 | |
| world_size = int(os.environ.get("WORLD_SIZE", "1")) | |
| use_accelerator = torch.cuda.is_available() and torch.cuda.device_count() >= world_size | |
| if distributed: | |
| dist.init_process_group("nccl" if use_accelerator else "gloo") | |
| rank = dist.get_rank() if distributed else 0 | |
| local_rank = int(os.environ.get("LOCAL_RANK", "0")) | |
| device = torch.device(f"cuda:{local_rank}" if use_accelerator else "cpu") | |
| if use_accelerator: | |
| torch.cuda.set_device(device) | |
| raw = np.load(ROOT / cfg["data"]["file"]) | |
| tensors = [torch.from_numpy(raw[name].astype(np.float32)) for name in ("initial", "target", "surface", "advection")] | |
| source = torch.from_numpy(raw["source"].astype(np.int64)) | |
| dataset = TensorDataset(*tensors, source) | |
| sampler = DistributedSampler(dataset, shuffle=True, seed=cfg["seed"]) if distributed else None | |
| loader = DataLoader(dataset, batch_size=cfg["train"]["batch_size"], sampler=sampler, | |
| shuffle=sampler is None) | |
| all_state = torch.from_numpy(raw["target"].astype(np.float32)) | |
| state_mean = all_state.mean((0, 1)).to(device) | |
| state_std = all_state.std((0, 1), unbiased=False).clamp_min(1e-5).to(device) | |
| tendency_mean = torch.zeros(68, device=device) | |
| tendency_std = torch.cat((torch.full((34,), 0.02), torch.full((34,), 2e-7))).to(device) | |
| layer_mass = torch.from_numpy(raw["layer_mass"].astype(np.float32)).to(device) | |
| model = StableNNPhys(cfg["model"]["hidden_size"]).to(device) | |
| if distributed: | |
| model = DistributedDataParallel(model, device_ids=[local_rank] if device.type == "cuda" else None) | |
| optimizer = torch.optim.Adam(model.parameters(), lr=cfg["train"]["learning_rate"]) | |
| history = [] | |
| for epoch in range(cfg["train"]["epochs"]): | |
| if sampler: | |
| sampler.set_epoch(epoch) | |
| total = 0.0 | |
| for initial, target, surface, advection, _ in loader: | |
| initial, target, surface, advection = [x.to(device) for x in (initial, target, surface, advection)] | |
| prediction, _ = rollout(model, initial, surface, advection, state_mean, state_std, | |
| tendency_mean, tendency_std, float(raw["dt_seconds"])) | |
| loss = rollout_loss(prediction, target, layer_mass.expand(initial.shape[0], -1), cfg["train"]["loss_mode"]) | |
| optimizer.zero_grad() | |
| loss.backward() | |
| torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) | |
| optimizer.step() | |
| total += loss.item() * initial.shape[0] | |
| value = total / len(dataset) | |
| history.append(value) | |
| if rank == 0: | |
| print(f"epoch={epoch + 1} loss={value:.6g}") | |
| if rank == 0: | |
| module = model.module if distributed else model | |
| checkpoint = ROOT / cfg["paths"]["checkpoint"] | |
| checkpoint.parent.mkdir(parents=True, exist_ok=True) | |
| payload = {"format_version": CHECKPOINT_FORMAT_VERSION, "model": module.state_dict(), | |
| "optimizer_state_dict": optimizer.state_dict(), "epoch": cfg["train"]["epochs"], | |
| "model_config": dict(cfg["model"]), "training_config": dict(cfg["train"]), | |
| "normalization": {"state_mean": state_mean.cpu(), "state_std": state_std.cpu(), | |
| "tendency_mean": tendency_mean.cpu(), "tendency_std": tendency_std.cpu()}, | |
| "variables": {"input": ["sL[34]", "qT[34]", "SHF", "LHF", "SOLIN"], | |
| "output": ["dsL_dt[34]", "dqT_dt[34]"]}, "dt_seconds": float(raw["dt_seconds"])} | |
| torch.save(payload, checkpoint) | |
| metrics = ROOT / cfg["paths"]["training_metrics"] | |
| metrics.parent.mkdir(parents=True, exist_ok=True) | |
| metrics.write_text(json.dumps({"loss": history, "world_size": dist.get_world_size() if distributed else 1, | |
| "paper_model": cfg["train"]["paper_model"]}, indent=2)) | |
| print(f"saved {checkpoint}") | |
| if distributed: | |
| dist.destroy_process_group() | |
| if __name__ == "__main__": | |
| main() | |