Download scripts/inference.py from OneScience-Group/NeuralConvection-2D-CompactFNO: direct link, hf CLI and curl.
- Browser
- Download file 4.96 kB
-
https://huggingface.co/OneScience-Group/NeuralConvection-2D-CompactFNO/resolve/main/scripts/inference.py
- Command line
-
hf download hf://OneScience-Group/NeuralConvection-2D-CompactFNO/scripts/inference.py
-
curl -L -o inference.py https://huggingface.co/OneScience-Group/NeuralConvection-2D-CompactFNO/resolve/main/scripts/inference.py
4.96 kB
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import sys | |
| import time | |
| from pathlib import Path | |
| from typing import Any | |
| import numpy as np | |
| import torch | |
| from torch.utils.data import DataLoader, Dataset | |
| import yaml | |
| PROJECT_ROOT = Path(__file__).resolve().parents[1] | |
| if str(PROJECT_ROOT) not in sys.path: | |
| sys.path.insert(0, str(PROJECT_ROOT)) | |
| from model.neural_convection_fno import build_model_from_config | |
| from scripts.train import load_pair, normalize_x, normalize_y, ssim_2d, tensor_to_numpy | |
| class InferenceDataset(Dataset): | |
| def __init__(self, x: torch.Tensor, y: torch.Tensor, stats: dict[str, torch.Tensor]) -> None: | |
| self.x = x | |
| self.y = y | |
| self.stats = stats | |
| def __len__(self) -> int: | |
| return int(self.x.shape[0]) | |
| def __getitem__(self, idx: int) -> tuple[torch.Tensor, torch.Tensor]: | |
| return normalize_x(self.x[idx], self.stats), normalize_y(self.y[idx], self.stats) | |
| def load_config(path: str | Path) -> dict[str, Any]: | |
| with open(path, "r", encoding="utf-8") as handle: | |
| return yaml.safe_load(handle) | |
| def resolve(path: str | Path) -> Path: | |
| path = Path(path) | |
| return path if path.is_absolute() else PROJECT_ROOT / path | |
| def select_device(name: str) -> torch.device: | |
| if name == "auto": | |
| return torch.device("cuda" if torch.cuda.is_available() else "cpu") | |
| return torch.device(name) | |
| def save_json(path: Path, payload: dict[str, Any]) -> None: | |
| path.parent.mkdir(parents=True, exist_ok=True) | |
| with open(path, "w", encoding="utf-8") as handle: | |
| json.dump(payload, handle, indent=2) | |
| def main() -> None: | |
| parser = argparse.ArgumentParser(description="Run compact FNO inference on a NeuralConvection split.") | |
| parser.add_argument("--config", default=str(PROJECT_ROOT / "config/config.yaml")) | |
| parser.add_argument("--split", default=None) | |
| parser.add_argument("--checkpoint", default=None) | |
| parser.add_argument("--device", default=None) | |
| args = parser.parse_args() | |
| config = load_config(args.config) | |
| data_cfg = config["data"] | |
| infer_cfg = config["inference"] | |
| split = args.split or infer_cfg["split"] | |
| device = select_device(args.device or config["experiment"].get("device", "auto")) | |
| root = resolve(data_cfg["root_dir"]) | |
| stats = torch.load(resolve(data_cfg["stats_path"]), map_location="cpu") | |
| x, y = load_pair(root, split, data_cfg["x_file"], data_cfg["y_file"]) | |
| loader = DataLoader( | |
| InferenceDataset(x, y, stats), | |
| batch_size=int(infer_cfg["batch_size"]), | |
| shuffle=False, | |
| num_workers=int(data_cfg.get("num_workers", 0)), | |
| pin_memory=bool(data_cfg.get("pin_memory", True)) and device.type == "cuda", | |
| ) | |
| model = build_model_from_config(config).to(device) | |
| ckpt_path = resolve(args.checkpoint or infer_cfg["checkpoint_path"]) | |
| checkpoint = torch.load(ckpt_path, map_location=device) | |
| model.load_state_dict(checkpoint["model_state_dict"]) | |
| model.eval() | |
| preds: list[np.ndarray] = [] | |
| targets: list[np.ndarray] = [] | |
| inputs: list[np.ndarray] = [] | |
| start = time.perf_counter() | |
| for x_batch, y_batch in loader: | |
| x_batch = x_batch.to(device, non_blocking=True) | |
| pred = tensor_to_numpy(model(x_batch)) | |
| preds.append(pred) | |
| targets.append(tensor_to_numpy(y_batch)) | |
| inputs.append(tensor_to_numpy(x_batch)) | |
| elapsed = time.perf_counter() - start | |
| pred_all = np.concatenate(preds, axis=0) | |
| target_all = np.concatenate(targets, axis=0) | |
| input_all = np.concatenate(inputs, axis=0) | |
| channel_names = list(data_cfg["channel_names"]) | |
| metrics = { | |
| "split": split, | |
| "checkpoint_path": str(ckpt_path), | |
| "num_samples": int(pred_all.shape[0]), | |
| "time_seconds_per_sample": elapsed / max(1, pred_all.shape[0]), | |
| "per_channel": {}, | |
| "paper_table5_compact_fno_2d": config["paper_table5_compact_fno_2d"], | |
| } | |
| for idx, name in enumerate(channel_names): | |
| mse = float(np.mean((pred_all[:, idx] - target_all[:, idx]) ** 2)) | |
| ssim = float(np.mean([ssim_2d(pred_all[i, idx], target_all[i, idx]) for i in range(pred_all.shape[0])])) | |
| metrics["per_channel"][name] = {"mse": mse, "ssim": ssim} | |
| paper = config["paper_table5_compact_fno_2d"]["channels"].get(name, {}) | |
| print( | |
| f"{name}: mse={mse:.8e} ssim={ssim:.6f} paper_mse={paper.get('mse')} paper_ssim={paper.get('ssim')}", | |
| flush=True, | |
| ) | |
| out_path = resolve(infer_cfg["output_path"]) | |
| out_path.parent.mkdir(parents=True, exist_ok=True) | |
| np.savez_compressed(out_path, pred_norm=pred_all, target_norm=target_all, input_norm=input_all, channel_names=np.array(channel_names)) | |
| save_json(resolve(infer_cfg["metrics_path"]), metrics) | |
| print(f"saved_predictions={out_path}", flush=True) | |
| print(f"saved_metrics={resolve(infer_cfg['metrics_path'])}", flush=True) | |
| if __name__ == "__main__": | |
| main() | |