Download scripts/inference.py from OneScience-Group/IMPROVER-AIFS: direct link, hf CLI and curl.
- Browser
- Download file 4.32 kB
-
https://huggingface.co/OneScience-Group/IMPROVER-AIFS/resolve/main/scripts/inference.py
- Command line
-
hf download hf://OneScience-Group/IMPROVER-AIFS/scripts/inference.py
-
curl -L -o inference.py https://huggingface.co/OneScience-Group/IMPROVER-AIFS/resolve/main/scripts/inference.py
4.32 kB
| """Run fold-held-out inference for every valid date, lead, variable, and station.""" | |
| 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)) | |
| from model.improver_aifs import ImproverAIFS | |
| from fake_data import generate_chunk | |
| def main(): | |
| config = yaml.safe_load((ROOT / "conf/config.yaml").read_text()) | |
| meta = np.load(ROOT / config["data"]["root"] / "protocol.npz", allow_pickle=True) | |
| checkpoint = torch.load(ROOT / config["paths"]["checkpoint"], map_location="cpu", weights_only=False) | |
| fold_states = checkpoint.get("model", {}).get("fold_states", []) | |
| if checkpoint["format_version"] != config["data"]["format_version"] or len(fold_states) != 2: | |
| raise ValueError("checkpoint must contain two valid-time folds") | |
| dates, leads, stations = 2, 241, config["data"]["station_count"] | |
| raw = np.empty((dates, leads, 3, stations), np.float32) | |
| truth = np.empty_like(raw); expected = np.empty_like(raw) | |
| probabilities = [np.empty((dates, leads, len(v), stations), np.float32) for v in config["model"]["thresholds"]] | |
| thresholds = [torch.tensor(v, dtype=torch.float32) for v in config["model"]["thresholds"]] | |
| chunk = config["data"]["station_chunk_size"] | |
| for fold in range(2): | |
| model = ImproverAIFS(checkpoint["model_config"]); model.load_state_dict(fold_states[fold]); model.eval() | |
| for start in range(0, stations, chunk): | |
| section = slice(start, min(start + chunk, stations)) | |
| patches, analyses, elevation = generate_chunk(meta, fold, section, False, int(config["seed"]) + 100) | |
| source_expected, source_probabilities = [], [] | |
| with torch.no_grad(): | |
| for source in range(3): | |
| patch = torch.from_numpy(patches[:, :, :, source]) | |
| patch[:, :, 0] += (-0.0098 * torch.from_numpy(elevation)).view(1, 1, -1, 1, 1) | |
| patch -= model.bias[source, :, :, section].unsqueeze(0).unsqueeze(-1).unsqueeze(-1) | |
| centre = patch[..., 1, 1] | |
| source_expected.append(centre) | |
| raw_probabilities = [] | |
| for variable, values in enumerate(thresholds): | |
| width = model.fuzzy_widths[variable] | |
| p = ((patch[:, :, variable].unsqueeze(2) - values.view(1, 1, -1, 1, 1, 1) + width) / (2 * width)).clamp(0, 1) | |
| raw_probabilities.append(model.neighborhood(model.recursive_filter(p))) | |
| source_probabilities.append(model.calibrate(raw_probabilities, source)) | |
| blend = model.blend_expected(torch.stack(source_expected, dim=3)) | |
| blend_probability = model.blend_probabilities(source_probabilities) | |
| raw[fold, ..., section] = patches[0, :, :, 0, :, 1, 1] | |
| truth[fold, ..., section] = analyses[0] | |
| expected[fold, ..., section] = blend[0].numpy() | |
| for variable in range(3): probabilities[variable][fold, ..., section] = blend_probability[variable][0].numpy() | |
| arrays = [raw, truth, expected, *probabilities] | |
| if not all(np.isfinite(array).all() for array in arrays): | |
| raise ValueError("inference produced non-finite values") | |
| if not all(((array >= 0) & (array <= 1)).all() for array in probabilities): | |
| raise ValueError("probabilities must be within [0, 1]") | |
| output = ROOT / config["paths"]["inference"] | |
| output.parent.mkdir(parents=True, exist_ok=True) | |
| payload = {"raw_aifs": raw, "analyses": truth, "blend_expected": expected, "valid_dates": meta["valid_dates"], "lead_hours": meta["lead_hours"], | |
| "station_id": meta["station_id"], "station_latitude": meta["station_latitude"], "station_longitude": meta["station_longitude"], | |
| "variables": meta["variables"], "units": meta["units"], "fold_id": np.arange(2)} | |
| for variable, name in enumerate(meta["variables"]): | |
| payload[f"thresholds_{name}"] = thresholds[variable].numpy(); payload[f"probability_blend_{name}"] = probabilities[variable] | |
| np.savez_compressed(output, **payload) | |
| print(f"saved={output.relative_to(ROOT)} expected_shape={expected.shape} folds=2 stations=569 thresholds=61/47/49") | |
| if __name__ == "__main__": | |
| main() | |