Download scripts/result.py from OneScience-Group/RainNet: direct link, hf CLI and curl.
- Browser
- Download file 6.54 kB
-
https://huggingface.co/OneScience-Group/RainNet/resolve/main/scripts/result.py
- Command line
-
hf download hf://OneScience-Group/RainNet/scripts/result.py
-
curl -L -o result.py https://huggingface.co/OneScience-Group/RainNet/resolve/main/scripts/result.py
6.54 kB
| """Evaluate actual RainNet rollout outputs and create diagnostic figures.""" | |
| import json | |
| from pathlib import Path | |
| import matplotlib | |
| matplotlib.use("Agg") | |
| import matplotlib.pyplot as plt | |
| import numpy as np | |
| import torch | |
| import torch.nn.functional as F | |
| import yaml | |
| ROOT = Path(__file__).resolve().parents[1] | |
| def load_config(): | |
| with (ROOT / "conf/config.yaml").open(encoding="utf-8") as handle: | |
| return yaml.safe_load(handle) | |
| def csi(prediction, target, threshold): | |
| pred_event, target_event = prediction >= threshold, target >= threshold | |
| hits = np.logical_and(pred_event, target_event).sum(dtype=np.float64) | |
| false_alarms = np.logical_and(pred_event, ~target_event).sum(dtype=np.float64) | |
| misses = np.logical_and(~pred_event, target_event).sum(dtype=np.float64) | |
| denominator = hits + false_alarms + misses | |
| return float(hits / denominator) if denominator else 0.0 | |
| def fss(prediction, target, threshold, window): | |
| pred = torch.from_numpy((prediction >= threshold).astype(np.float32))[None, None] | |
| obs = torch.from_numpy((target >= threshold).astype(np.float32))[None, None] | |
| if window > 1: | |
| padding = window // 2 | |
| pred = F.avg_pool2d(pred, window, stride=1, padding=padding) | |
| obs = F.avg_pool2d(obs, window, stride=1, padding=padding) | |
| pred = pred[..., : prediction.shape[0], : prediction.shape[1]] | |
| obs = obs[..., : target.shape[0], : target.shape[1]] | |
| numerator = torch.sum((pred - obs) ** 2) | |
| denominator = torch.sum(pred**2) + torch.sum(obs**2) | |
| return float(1.0 - numerator / denominator) if denominator > 0 else 0.0 | |
| def metric_series(predictions, targets, thresholds, windows): | |
| output = [] | |
| for index, (prediction, target) in enumerate(zip(predictions, targets)): | |
| pred_rate, target_rate = prediction * 12.0, target * 12.0 | |
| output.append( | |
| { | |
| "lead_minutes": (index + 1) * 5, | |
| "mae_mm_h": float(np.mean(np.abs(pred_rate - target_rate), dtype=np.float64)), | |
| "csi": {str(t): csi(pred_rate, target_rate, t) for t in thresholds}, | |
| "fss": { | |
| str(t): {str(w): fss(pred_rate, target_rate, t, w) for w in windows} | |
| for t in thresholds | |
| }, | |
| } | |
| ) | |
| return output | |
| def main(): | |
| config = load_config() | |
| output_dir = ROOT / config["inference"]["output_dir"] | |
| required = ["inputs.npy", "predictions.npy", "targets.npy", "persistence.npy"] | |
| missing = [name for name in required if not (output_dir / name).exists()] | |
| if missing: | |
| raise FileNotFoundError(f"Missing inference outputs: {missing}") | |
| inputs = np.load(output_dir / "inputs.npy") | |
| predictions = np.load(output_dir / "predictions.npy") | |
| targets = np.load(output_dir / "targets.npy") | |
| persistence = np.load(output_dir / "persistence.npy") | |
| for name, array in (("prediction", predictions), ("target", targets), ("persistence", persistence)): | |
| if array.shape != predictions.shape or not np.isfinite(array).all(): | |
| raise ValueError(f"Invalid {name}: shape={array.shape}, finite={np.isfinite(array).all()}") | |
| thresholds = config["evaluation"]["thresholds_mm_h"] | |
| windows = config["evaluation"]["fss_windows_km"] | |
| rainnet_metrics = metric_series(predictions, targets, thresholds, windows) | |
| persistence_metrics = metric_series(persistence, targets, thresholds, windows) | |
| metrics = { | |
| "units": "mm/h", | |
| "rainnet": rainnet_metrics, | |
| "persistence": persistence_metrics, | |
| } | |
| result_dir = ROOT / config["evaluation"]["result_dir"] | |
| result_dir.mkdir(parents=True, exist_ok=True) | |
| with (result_dir / "metrics.json").open("w", encoding="utf-8") as handle: | |
| json.dump(metrics, handle, indent=2) | |
| history_path = result_dir / "train_history.json" | |
| with history_path.open(encoding="utf-8") as handle: | |
| history = json.load(handle) | |
| fig, ax = plt.subplots(figsize=(6, 4)) | |
| ax.plot(history["train_loss"], marker="o", label="Train") | |
| ax.plot(history["validation_loss"], marker="o", label="Validation") | |
| ax.set(xlabel="Epoch", ylabel="Log-Cosh loss", title="RainNet smoke training") | |
| ax.legend() | |
| fig.tight_layout() | |
| fig.savefig(result_dir / "loss.png", dpi=150) | |
| plt.close(fig) | |
| selected = [0, 5, 11] | |
| fig, axes = plt.subplots(3, 5, figsize=(16, 10)) | |
| for row, index in enumerate(selected): | |
| panels = [inputs[-1], targets[index], predictions[index], persistence[index], predictions[index] - targets[index]] | |
| titles = ["Last Input", "Truth", "RainNet Prediction", "Persistence", "Prediction Error"] | |
| for axis, panel, title in zip(axes[row], panels, titles): | |
| image = axis.imshow(panel, cmap="RdBu_r" if title == "Prediction Error" else "Blues") | |
| axis.set_title(f"{title}\n{(index + 1) * 5} min") | |
| axis.axis("off") | |
| fig.colorbar(image, ax=axis, fraction=0.046) | |
| fig.tight_layout() | |
| fig.savefig(result_dir / "forecast_comparison.png", dpi=120) | |
| plt.close(fig) | |
| leads = [item["lead_minutes"] for item in rainnet_metrics] | |
| fig, axes = plt.subplots(1, 3, figsize=(15, 4)) | |
| axes[0].plot(leads, [item["mae_mm_h"] for item in rainnet_metrics], label="RainNet") | |
| axes[0].plot(leads, [item["mae_mm_h"] for item in persistence_metrics], label="Persistence") | |
| axes[0].set(title="MAE", xlabel="Lead time (min)", ylabel="mm/h") | |
| for threshold in thresholds: | |
| axes[1].plot(leads, [item["csi"][str(threshold)] for item in rainnet_metrics], label=str(threshold)) | |
| axes[2].plot(leads, [item["fss"][str(threshold)]["20"] for item in rainnet_metrics], label=str(threshold)) | |
| axes[1].set(title="RainNet CSI", xlabel="Lead time (min)", ylabel="CSI") | |
| axes[2].set(title="RainNet FSS (20 km)", xlabel="Lead time (min)", ylabel="FSS") | |
| axes[0].legend() | |
| axes[1].legend(title="mm/h", fontsize=7) | |
| axes[2].legend(title="mm/h", fontsize=7) | |
| fig.tight_layout() | |
| fig.savefig(result_dir / "metrics.png", dpi=150) | |
| plt.close(fig) | |
| print(f"Prediction shape: {predictions.shape}") | |
| print(f"Target shape: {targets.shape}") | |
| print(f"Persistence shape: {persistence.shape}") | |
| for index in selected: | |
| item = rainnet_metrics[index] | |
| print(f"Lead {item['lead_minutes']} min MAE: {item['mae_mm_h']:.8f} mm/h") | |
| print(f"Lead {item['lead_minutes']} min CSI: {item['csi']}") | |
| print(f"Lead {item['lead_minutes']} min FSS: {item['fss']}") | |
| if __name__ == "__main__": | |
| main() | |