#!/usr/bin/env python3 """Đánh giá MỘT model bất kỳ trên PositionPlayEnv với mẫu lớn. Dùng để chốt câu hỏi thống kê (Q có tăng thật không) sau smoke test — eval 200 cú chỉ có ~27 cú pot, SE của Q|pot ~±0.05, cần n lớn hơn. Chạy từ gốc repo: # model stage 2 (obs đủ 6 chiều): python scripts/eval_position.py models/ppo_pos_20260714_204549/best_model.zip --episodes 1000 # model stage 1 (position-blind, cắt obs 4 chiều): python scripts/eval_position.py models/ppo_20260711_210650/final_model.zip --episodes 1000 --obs-slice 4 """ from __future__ import annotations import argparse import sys from pathlib import Path sys.stdout.reconfigure(encoding="utf-8") sys.stderr.reconfigure(encoding="utf-8") ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT / "src")) sys.path.insert(0, str(ROOT / "scripts")) def main(): p = argparse.ArgumentParser() p.add_argument("model_path", help="đường dẫn model (.zip)") p.add_argument("--algo", choices=["ppo", "sac"], default="ppo") p.add_argument("--episodes", type=int, default=1000) p.add_argument("--obs-slice", type=int, default=None, help="cắt obs còn N chiều đầu (model stage 1: 4)") p.add_argument("--pos-coef", type=float, default=None, help="khớp với run cần chấm (chỉ ảnh hưởng reward mean)") p.add_argument("--aim-mode", choices=["best_cut", "any"], default=None, help="khớp env lúc train (ảnh hưởng aim_cos/reward mean, " "không ảnh hưởng pot/Q)") args = p.parse_args() import numpy as np from stable_baselines3 import PPO, SAC from train_position import evaluate, print_stats cls = PPO if args.algo == "ppo" else SAC model = cls.load(args.model_path) print(f"== {args.model_path} trên PositionPlayEnv ==") print(f" (deterministic, {args.episodes} cú" + (f", obs cắt {args.obs_slice} chiều" if args.obs_slice else "") + ")\n") stats = evaluate(model, n_episodes=args.episodes, pos_coef=args.pos_coef, obs_slice=args.obs_slice, aim_mode=args.aim_mode) print_stats(stats) # SE xấp xỉ cho Q|pot để đọc kết quả cho đúng n_pot = round(stats["pot_rate"] * args.episodes) if n_pot > 1: # Q ∈ [0,1], std thô ~0.25 (ước lượng bảo thủ) se = 0.25 / np.sqrt(n_pot) print(f"\n n cú pot ≈ {n_pot} → SE(Q|pot) ≈ ±{se:.3f}" f" (Q|pot ± 2·SE = [{stats['q_mean_on_pot']-2*se:.3f}, " f"{stats['q_mean_on_pot']+2*se:.3f}])") if __name__ == "__main__": main()