Spaces:
Running
Running
Download scripts/plot_rewards.py from shriom23/MedFlow-OpenEnv: direct link, hf CLI and curl.
- Browser
- Download file 4.45 kB
-
https://huggingface.co/spaces/shriom23/MedFlow-OpenEnv/resolve/main/scripts/plot_rewards.py
- Command line
-
hf download hf://spaces/shriom23/MedFlow-OpenEnv/scripts/plot_rewards.py
-
curl -L -o plot_rewards.py https://huggingface.co/spaces/shriom23/MedFlow-OpenEnv/resolve/main/scripts/plot_rewards.py
4.45 kB
| """Generate reward plots from episode JSON logs. | |
| Usage: | |
| python scripts/plot_rewards.py --logs outputs/logs --out outputs/plots | |
| Produces: | |
| - outputs/plots/reward_curve.png (mean per-step reward curve) | |
| - outputs/plots/reward_hist.png (histogram / boxplot of total rewards) | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| from pathlib import Path | |
| import json | |
| import math | |
| import matplotlib.pyplot as plt | |
| try: | |
| import numpy as np | |
| except Exception: | |
| np = None | |
| def load_episodes(log_dir: Path): | |
| files = sorted(log_dir.glob("episode_*.json")) | |
| episodes = [] | |
| for p in files: | |
| try: | |
| d = json.loads(p.read_text(encoding="utf-8")) | |
| except Exception: | |
| continue | |
| # try common shapes | |
| total = d.get("total_reward") if isinstance(d, dict) else None | |
| if total is None: | |
| total = d.get("reward") if isinstance(d, dict) else None | |
| steps = d.get("steps") or d.get("episode") or d.get("trajectory") or [] | |
| if total is None and steps: | |
| total = sum(s.get("reward", 0) for s in steps if isinstance(s, dict)) | |
| # extract per-step rewards | |
| per_step = [] | |
| if steps and isinstance(steps, list): | |
| for s in steps: | |
| if isinstance(s, dict): | |
| per_step.append(float(s.get("reward", 0))) | |
| else: | |
| # if step is numeric | |
| try: | |
| per_step.append(float(s)) | |
| except Exception: | |
| pass | |
| episodes.append({"name": p.name, "total": total, "per_step": per_step}) | |
| return episodes | |
| def ensure_out(dirpath: Path): | |
| dirpath.mkdir(parents=True, exist_ok=True) | |
| def plot_reward_curve(episodes, out_path: Path): | |
| # build aligned per-step arrays | |
| per_steps = [e["per_step"] for e in episodes if e["per_step"]] | |
| if not per_steps: | |
| print("No per-step data found to plot reward curve.") | |
| return | |
| max_len = max(len(p) for p in per_steps) | |
| if np is not None: | |
| arr = np.full((len(per_steps), max_len), np.nan, dtype=float) | |
| for i, p in enumerate(per_steps): | |
| arr[i, : len(p)] = p | |
| mean = np.nanmean(arr, axis=0) | |
| std = np.nanstd(arr, axis=0) | |
| x = list(range(1, len(mean) + 1)) | |
| else: | |
| # fallback: compute mean manually | |
| sums = [0.0] * max_len | |
| counts = [0] * max_len | |
| for p in per_steps: | |
| for i, v in enumerate(p): | |
| sums[i] += v | |
| counts[i] += 1 | |
| mean = [sums[i] / counts[i] if counts[i] else 0.0 for i in range(max_len)] | |
| std = [0.0] * max_len | |
| x = list(range(1, max_len + 1)) | |
| plt.figure(figsize=(9, 4)) | |
| plt.plot(x, mean, label="mean reward per step") | |
| try: | |
| plt.fill_between(x, [m - s for m, s in zip(mean, std)], [m + s for m, s in zip(mean, std)], alpha=0.2) | |
| except Exception: | |
| pass | |
| plt.xlabel("Step") | |
| plt.ylabel("Reward") | |
| plt.title("Mean per-step reward (shaded = std)") | |
| plt.grid(alpha=0.2) | |
| plt.legend() | |
| plt.tight_layout() | |
| plt.savefig(out_path / "reward_curve.png", dpi=150) | |
| plt.close() | |
| def plot_total_hist(episodes, out_path: Path): | |
| totals = [e["total"] for e in episodes if e["total"] is not None] | |
| if not totals: | |
| print("No total reward data found to plot histogram.") | |
| return | |
| plt.figure(figsize=(6, 4)) | |
| plt.subplot(1, 2, 1) | |
| plt.hist(totals, bins=min(30, max(5, len(totals)//2)), alpha=0.7) | |
| plt.xlabel("Total reward") | |
| plt.ylabel("Count") | |
| plt.title("Total reward distribution") | |
| plt.subplot(1, 2, 2) | |
| plt.boxplot(totals, vert=False) | |
| plt.title("Total reward (box)") | |
| plt.tight_layout() | |
| plt.savefig(out_path / "reward_hist.png", dpi=150) | |
| plt.close() | |
| def main(): | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument("--logs", type=Path, default=Path("outputs/logs")) | |
| parser.add_argument("--out", type=Path, default=Path("outputs/plots")) | |
| args = parser.parse_args() | |
| if not args.logs.exists(): | |
| print(f"Logs directory not found: {args.logs}") | |
| return | |
| ensure_out(args.out) | |
| episodes = load_episodes(args.logs) | |
| print(f"Found {len(episodes)} episode files in {args.logs}") | |
| plot_reward_curve(episodes, args.out) | |
| plot_total_hist(episodes, args.out) | |
| print(f"Saved plots to {args.out}") | |
| if __name__ == "__main__": | |
| main() | |