Download scripts/train.py from OneScience-Group/ML-MODIS: direct link, hf CLI and curl.
- Browser
- Download file 4.61 kB
-
https://huggingface.co/OneScience-Group/ML-MODIS/resolve/main/scripts/train.py
- Command line
-
hf download hf://OneScience-Group/ML-MODIS/scripts/train.py
-
curl -L -o train.py https://huggingface.co/OneScience-Group/ML-MODIS/resolve/main/scripts/train.py
4.61 kB
| #!/usr/bin/env python3 | |
| """Train month-by-target ML-MODIS forests, optionally task-parallel under torchrun.""" | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import os | |
| import sys | |
| from pathlib import Path | |
| import numpy as np | |
| import torch | |
| import yaml | |
| ROOT = Path(__file__).resolve().parents[1] | |
| sys.path.insert(0, str(ROOT / "model")) | |
| from ml_modis import BootstrapRandomForestRegressor, feature_names, regression_metrics, validate_multimodal_keys | |
| def args_parser() -> argparse.Namespace: | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument("--config", default=str(ROOT / "conf/config.yaml")) | |
| parser.add_argument("--data", default=None) | |
| parser.add_argument("--checkpoint", default=None) | |
| parser.add_argument("--paper-model", action="store_true") | |
| parser.add_argument("--trees", type=int, default=None) | |
| return parser.parse_args() | |
| def distributed_context() -> tuple[int, int]: | |
| world = int(os.environ.get("WORLD_SIZE", "1")) | |
| rank = int(os.environ.get("RANK", "0")) | |
| if world > 1: | |
| torch.distributed.init_process_group(backend="gloo") | |
| return rank, world | |
| def main() -> None: | |
| args = args_parser() | |
| config = yaml.safe_load(Path(args.config).read_text()) | |
| settings = dict(config["model"]) | |
| if args.paper_model: | |
| settings.update(config["paper_model"]) | |
| if args.trees is not None: | |
| settings["trees"] = args.trees | |
| data_path = ROOT / (args.data or config["data"]["path"]) | |
| with np.load(data_path) as archive: | |
| data = {key: archive[key] for key in archive.files} | |
| validate_multimodal_keys(data) | |
| rank, world = distributed_context() | |
| months = config["data"]["months"] | |
| targets = config["data"]["variables"]["targets"]["names"] | |
| tasks = [(int(month), target_index, target) for month in months | |
| for target_index, target in enumerate(targets)] | |
| local_models = {} | |
| for task_index, (month, target_index, target) in enumerate(tasks): | |
| if task_index % world != rank: | |
| continue | |
| mask = (data["month"] == month) & (data["year"] != config["train"]["excluded_year"]) | |
| x, y = data["X"][mask], data["Y"][mask, target_index] | |
| model = BootstrapRandomForestRegressor( | |
| n_trees=int(settings["trees"]), min_leaf=int(settings["min_leaf"]), | |
| max_features=int(settings["max_features"]), bootstrap_fraction=float(settings["bootstrap_fraction"]), | |
| max_depth=settings["max_depth"], split_candidates=int(settings["split_candidates"]), | |
| seed=int(config["runtime"]["seed"] + task_index * 1009), | |
| ).fit(x, y) | |
| oob, counts = model.oob_predict(x) | |
| local_models[f"{month}:{target}"] = { | |
| "state": model.state_dict(), "oob_metrics": regression_metrics(y[counts > 0], oob[counts > 0]), | |
| "train_samples": int(mask.sum()), "excluded_year": int(config["train"]["excluded_year"]), | |
| } | |
| print(f"rank={rank} trained month={month} target={target} samples={mask.sum()}", flush=True) | |
| if world > 1: | |
| gathered = [None] * world if rank == 0 else None | |
| torch.distributed.gather_object(local_models, gathered, dst=0) | |
| if rank == 0: | |
| local_models = {key: value for shard in gathered for key, value in shard.items()} | |
| if rank == 0: | |
| checkpoint = ROOT / (args.checkpoint or config["paths"]["checkpoint"]) | |
| checkpoint.parent.mkdir(parents=True, exist_ok=True) | |
| model_config = { | |
| "architecture": "BootstrapRandomForestRegressor", "settings": settings, | |
| "targets": targets, "months": months, "input_features": 114, | |
| "feature_names": feature_names(), "excluded_year": int(config["train"]["excluded_year"]), | |
| } | |
| torch.save({"model": local_models, "model_config": model_config, | |
| "format_version": config["format_version"], | |
| "training": {"paper_model": args.paper_model, "distributed_world_size": world}}, checkpoint) | |
| summary = {key: value["oob_metrics"] for key, value in sorted(local_models.items())} | |
| metrics_path = ROOT / config["paths"]["training_metrics"] | |
| metrics_path.parent.mkdir(parents=True, exist_ok=True) | |
| metrics_path.write_text(json.dumps({"format_version": config["format_version"], | |
| "models": summary}, indent=2, allow_nan=False) + "\n") | |
| print(json.dumps({"checkpoint": str(checkpoint), "models": len(local_models), "oob": summary}, indent=2)) | |
| if world > 1: | |
| torch.distributed.destroy_process_group() | |
| if __name__ == "__main__": | |
| main() | |