StableNN-Phys / scripts /train.py
zhangrenchao's picture
Upload folder using huggingface_hub
7f71cfd verified
Raw
History Blame Contribute Delete
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()