File size: 2,761 Bytes
78738de
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#!/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()