| from __future__ import annotations |
|
|
| import json |
| from pathlib import Path |
|
|
| import gradio as gr |
| import numpy as np |
| import plotly.graph_objects as go |
| from model import RecurrentFeatures |
| from safetensors.numpy import load_file |
| from train import make_problem |
|
|
| PROJECT_DIR = Path(__file__).resolve().parent |
| ARTIFACT_DIR = PROJECT_DIR / "artifacts" / "evolino-pocket" |
| REPORT = json.loads((ARTIFACT_DIR / "evaluation.json").read_text(encoding="utf-8")) |
|
|
|
|
| def load_model(name: str) -> RecurrentFeatures: |
| tensors = load_file(ARTIFACT_DIR / name) |
| return RecurrentFeatures( |
| input_weight=tensors["input_weight"], |
| recurrent_weight=tensors["recurrent_weight"], |
| bias=tensors["bias"], |
| readout=tensors["readout"], |
| ) |
|
|
|
|
| EVOLVED = load_model("evolved_features.safetensors") |
| RANDOM = load_model("random_control.safetensors") |
| SERIES, TARGETS, SPLIT = make_problem() |
| EVOLVED_PREDICTIONS = EVOLVED.predict(SERIES) |
| RANDOM_PREDICTIONS = RANDOM.predict(SERIES) |
|
|
|
|
| def inspect_forecast(start: int, horizon: int, points: int) -> tuple[go.Figure, dict]: |
| horizon_index = int(horizon) - 1 |
| indices = SPLIT["test"][int(start) : int(start) + int(points)] |
| truth = TARGETS[indices, horizon_index] |
| evolved = EVOLVED_PREDICTIONS[indices, horizon_index] |
| random = RANDOM_PREDICTIONS[indices, horizon_index] |
| figure = go.Figure() |
| figure.add_scatter(y=truth, name="True future", line={"width": 3}) |
| figure.add_scatter(y=evolved, name="Evolved features") |
| figure.add_scatter(y=random, name="Random control", line={"dash": "dot"}) |
| figure.update_layout( |
| template="plotly_dark", |
| title=f"Mackey–Glass direct forecast, horizon +{int(horizon)}", |
| xaxis_title="Held-out test step", |
| yaxis_title="Normalized value", |
| ) |
| metrics = { |
| "horizon": int(horizon), |
| "evolved_window_rmse": float(np.sqrt(np.mean((evolved - truth) ** 2))), |
| "random_window_rmse": float(np.sqrt(np.mean((random - truth) ** 2))), |
| **REPORT["aggregate"], |
| } |
| return figure, metrics |
|
|
|
|
| with gr.Blocks(title="Evolino Pocket") as demo: |
| gr.Markdown( |
| "# Evolino Pocket\n" |
| "Inspect an Evolino-inspired hybrid: mutation search shapes a recurrent " |
| "feature generator while ridge regression solves its multihorizon " |
| "readout exactly." |
| ) |
| with gr.Row(): |
| start = gr.Slider(0, 650, value=20, step=1, label="Test window start") |
| horizon = gr.Slider(1, 12, value=12, step=1, label="Forecast horizon") |
| points = gr.Slider(30, 150, value=100, step=10, label="Visible points") |
| initial = inspect_forecast(20, 12, 100) |
| chart = gr.Plot(value=initial[0]) |
| metrics = gr.JSON(value=initial[1]) |
| button = gr.Button("Inspect forecast", variant="primary") |
| button.click( |
| inspect_forecast, |
| inputs=[start, horizon, points], |
| outputs=[chart, metrics], |
| ) |
|
|
|
|
| if __name__ == "__main__": |
| demo.launch() |
|
|