Download scripts/train.py from OneScience-Group/DINCAE: direct link, hf CLI and curl.
- Browser
- Download file 5.13 kB
-
https://huggingface.co/OneScience-Group/DINCAE/resolve/main/scripts/train.py
- Command line
-
hf download hf://OneScience-Group/DINCAE/scripts/train.py
-
curl -L -o train.py https://huggingface.co/OneScience-Group/DINCAE/resolve/main/scripts/train.py
5.13 kB
| """Train DINCAE with random cloud masking and masked Gaussian NLL.""" | |
| import json | |
| import os | |
| import sys | |
| from datetime import date | |
| from pathlib import Path | |
| import numpy as np | |
| import torch | |
| import yaml | |
| from torch.nn.parallel import DistributedDataParallel | |
| from torch.utils.data import DataLoader, Dataset, DistributedSampler | |
| ROOT = Path(__file__).resolve().parents[1] | |
| sys.path.insert(0, str(ROOT)) | |
| from model.dincae import DINCAE, build_input, masked_gaussian_nll | |
| class SSTDataset(Dataset): | |
| def __init__(self, arrays, indices, cloud_fraction, seed): | |
| self.data, self.indices = arrays, list(indices) | |
| self.cloud_fraction, self.seed = float(cloud_fraction), int(seed) | |
| def __len__(self): | |
| return len(self.indices) | |
| def __getitem__(self, item): | |
| index = self.indices[item] | |
| timestamp = date.fromisoformat(str(self.data["timestamps"][index])) | |
| observed = self.data["observed_anomaly"].copy() | |
| precision = self.data["precision"].copy() | |
| rng = np.random.default_rng(self.seed + index) | |
| original_valid = np.isfinite(observed[index]) & self.data["ocean_mask"] | |
| withheld = original_valid & (rng.random(original_valid.shape) < self.cloud_fraction) | |
| observed[index, withheld] = np.nan | |
| precision[index, withheld] = 0.0 | |
| inputs = build_input(observed, precision, index, self.data["longitude"], | |
| self.data["latitude"], timestamp.timetuple().tm_yday) | |
| target = self.data["sst_anomaly"][index][None].astype(np.float32) | |
| return torch.from_numpy(inputs), torch.from_numpy(target), torch.from_numpy(withheld[None]) | |
| def main(): | |
| config = yaml.safe_load((ROOT / "conf/config.yaml").read_text()) | |
| rank = int(os.environ.get("RANK", "0")) | |
| world_size = int(os.environ.get("WORLD_SIZE", "1")) | |
| local_rank = int(os.environ.get("LOCAL_RANK", "0")) | |
| distributed = world_size > 1 | |
| if distributed: | |
| backend = "nccl" if torch.cuda.is_available() else "gloo" | |
| torch.distributed.init_process_group(backend=backend) | |
| requested = config["runtime"]["device"] | |
| use_cuda = torch.cuda.is_available() and requested != "cpu" | |
| device = torch.device(f"cuda:{local_rank}" if use_cuda else "cpu") | |
| if use_cuda: | |
| torch.cuda.set_device(device) | |
| torch.manual_seed(int(config["seed"]) + rank) | |
| path = ROOT / config["data"]["root"] / config["data"]["file"] | |
| with np.load(path) as loaded: | |
| arrays = {key: loaded[key] for key in loaded.files} | |
| train_count = min(int(config["data"]["train_samples"]), len(arrays["timestamps"]) - 1) | |
| dataset = SSTDataset(arrays, range(train_count), config["data"]["random_cloud_fraction"], config["seed"]) | |
| sampler = DistributedSampler(dataset, shuffle=True, seed=int(config["seed"])) if distributed else None | |
| loader = DataLoader(dataset, batch_size=int(config["training"]["batch_size"]), | |
| shuffle=sampler is None, sampler=sampler, | |
| num_workers=int(config["runtime"]["num_workers"])) | |
| model = DINCAE(**config["model"]).to(device) | |
| if distributed: | |
| model = DistributedDataParallel(model, device_ids=[local_rank] if use_cuda else None) | |
| optimizer = torch.optim.Adam(model.parameters(), lr=float(config["training"]["learning_rate"])) | |
| history = [] | |
| for epoch in range(int(config["training"]["epochs"])): | |
| if sampler is not None: | |
| sampler.set_epoch(epoch) | |
| model.train() | |
| total, batches = 0.0, 0 | |
| for inputs, target, mask in loader: | |
| inputs, target, mask = inputs.to(device), target.to(device), mask.to(device) | |
| optimizer.zero_grad(set_to_none=True) | |
| output = model(inputs) | |
| loss = masked_gaussian_nll(output, target, mask, | |
| config["training"]["gamma"], config["training"]["delta"]) | |
| loss.backward() | |
| optimizer.step() | |
| total, batches = total + float(loss.detach()), batches + 1 | |
| history.append({"epoch": epoch + 1, "masked_gaussian_nll": total / max(batches, 1)}) | |
| if rank == 0: | |
| print(f"epoch={epoch + 1} loss={history[-1]['masked_gaussian_nll']:.6f}") | |
| if rank == 0: | |
| raw_model = model.module if distributed else model | |
| checkpoint = ROOT / config["paths"]["checkpoint"] | |
| checkpoint.parent.mkdir(parents=True, exist_ok=True) | |
| torch.save({"model": raw_model.state_dict(), "model_config": config["model"], | |
| "gamma": config["training"]["gamma"], "delta": config["training"]["delta"], | |
| "format_version": "dincae_checkpoint_v1"}, checkpoint) | |
| metrics = ROOT / config["paths"]["training_metrics"] | |
| metrics.parent.mkdir(parents=True, exist_ok=True) | |
| metrics.write_text(json.dumps({"history": history, "world_size": world_size, | |
| "parameter_count": sum(p.numel() for p in raw_model.parameters())}, indent=2)) | |
| print(f"checkpoint={checkpoint}") | |
| if distributed: | |
| torch.distributed.destroy_process_group() | |
| if __name__ == "__main__": | |
| main() | |