File size: 5,599 Bytes
216004d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#!/usr/bin/env python3
"""IDBD vs Autostep vs value 的真实回路对照分析。

回答: **步长分化到底带不带来收益?**

臂:
  idbd        IDBD, mu=0.05, alpha0=0.2   (我们之前唯一能找到的活点)
  auto        Autostep, mu=1e-2, alpha0=0.1 (论文推荐默认, **未针对本任务调参**)
  auto2       Autostep, mu=0.05, alpha0=0.2 (IDBD 的活点; 看 Autostep 是否不敏感)
  value       手工价值函数 (固定公式排序器) —— 已证等价于随机

判据:
  1) 步长是否分化: alpha_std > 0 且 max/min > 1.5
  2) 分化是否带来收益: 相对 value 的 any_time 提升, Welch t 检验
  3) Autostep 是否免调参: 两档 (mu=1e-2/alpha0=0.1) 与 (mu=0.05/alpha0=0.2)
     的**表现是否接近** —— 接近 = 免调参成立
  4) 可靠标准: std < mean/2
"""
import glob
import json
import math
import os
import re
import sys

import numpy as np

ROOT = sys.argv[1] if len(sys.argv) > 1 else "/work/liuyuanjie/headless"


def t_sf(x, df):
    """t 分布上尾概率 (stdlib 数值积分, 避免依赖 scipy)。"""
    if x <= 0:
        return 0.5 + (0.5 - t_sf(-x, df))
    c = math.gamma((df + 1) / 2) / (math.sqrt(df * math.pi) * math.gamma(df / 2))
    n, hi = 20000, x + 60.0
    h = (hi - x) / n
    s = 0.0
    for i in range(n + 1):
        t = x + i * h
        w = 1 if i in (0, n) else (4 if i % 2 else 2)
        s += w * c * (1 + t * t / df) ** (-(df + 1) / 2)
    return s * h / 3


rows = {}
for f in glob.glob(os.path.join(ROOT, "results/rl_*/lm4_wave_results.json")):
    tag = os.path.basename(os.path.dirname(f))
    m = re.match(r"rl_(.+?)_s(\d+)$", tag)
    if not m:
        continue
    arm, seed = m.group(1), int(m.group(2))
    try:
        d = json.load(open(f))
    except Exception as e:
        print("  [坏文件]", tag, e)
        continue
    v = d.get("replay")
    if isinstance(v, dict) and v.get("any_time_acc") is not None:
        rows.setdefault(arm, []).append((seed, v))

if not rows:
    sys.exit("没有可用结果")

print("=" * 100)
print("步长自适应在真实回路的效果 (lm4, replay 臂, stream=revisit, 12 轮)")
print("=" * 100)
print("%-8s %-3s %9s %8s %6s %9s %9s | %-24s %s"
      % ("arm", "n", "any_time", "std", "CV%", "replay", "最差遗忘",
         "alpha std / max-min", "algo"))
print("-" * 100)
for arm, vs in sorted(rows.items()):
    at = np.array([v["any_time_acc"] for _, v in vs], float)
    if len(at) < 2:
        print("%-8s %-3d %9.4f %8s %6s %9s %9s" % (arm, len(at), at.mean(),
              "-", "-", "-", "-") + "  (待更多 seed)")
        continue
    rp = np.array([v["final_mean_acc"] for _, v in vs], float)
    wf = np.array([v["worst_case_forget"] for _, v in vs], float)
    st = vs[0][1].get("proposer_stats", {})
    al = st.get("alpha", {})
    cv = 100 * at.std(ddof=1) / at.mean() if len(at) > 1 else float("nan")
    print("%-8s %-3d %9.4f %8.4f %6.1f %9.4f %9.4f | %-24s %s"
          % (arm, len(at), at.mean(), at.std(ddof=1) if len(at) > 1 else 0, cv,
             rp.mean(), wf.mean(),
             "%.4f / %.2fx" % (al.get("std", 0), al.get("ratio_max_min", 0)),
             st.get("algo", "-")))

print()
print("=== 判据 1: 步长是否分化 (alpha_std>0 且 ratio>1.5) ===")
for arm, vs in sorted(rows.items()):
    al = vs[0][1].get("proposer_stats", {}).get("alpha", {})
    s, r = al.get("std", 0), al.get("ratio_max_min", 0)
    print("  %-8s std=%.4f ratio=%.2f  %s" % (arm, s, r,
          "分化 ✓" if (s > 0 and r > 1.5) else ("未分化" if s == 0 else "弱")))

if len(rows) > 1:
    print()
    print("=== 判据 2: 两两 Welch t 检验 (any_time) ===")
    keys = sorted(rows)
    print("  %-26s %9s %7s %9s  %s" % ("对比", "Δmean", "t", "p", "判定"))
    for i, a in enumerate(keys):
        for b in keys[i + 1:]:
            A = np.array([v["any_time_acc"] for _, v in rows[a]], float)
            B = np.array([v["any_time_acc"] for _, v in rows[b]], float)
            if len(A) < 2 or len(B) < 2:
                print("  %-26s %+9.4f %7s %9s  %s"
                      % ("%s - %s" % (a, b), A.mean() - B.mean(), "-", "-",
                         "n<2, 待更多 seed"))
                continue
            se = math.sqrt(A.var(ddof=1) / len(A) + B.var(ddof=1) / len(B))
            t = (A.mean() - B.mean()) / se
            df = se ** 4 / ((A.var(ddof=1) / len(A)) ** 2 / (len(A) - 1)
                            + (B.var(ddof=1) / len(B)) ** 2 / (len(B) - 1))
            p = 2 * t_sf(abs(t), df)
            print("  %-26s %+9.4f %7.2f %9.4f  %s"
                  % ("%s - %s" % (a, b), A.mean() - B.mean(), t, p,
                     "显著" if p < 0.05 else "不显著"))

print()
print("=== 判据 3: Autostep 免调参性 (两档超参的表现差) ===")
if "auto" in rows and "auto2" in rows:
    a1 = np.mean([v["any_time_acc"] for _, v in rows["auto"]])
    a2 = np.mean([v["any_time_acc"] for _, v in rows["auto2"]])
    print("  auto (论文默认 mu=1e-2,a0=0.1) %.4f  vs  auto2 (mu=0.05,a0=0.2) %.4f"
          % (a1, a2))
    print("  差 %.4f -> %s" % (abs(a1 - a2),
          "免调参成立 (两档表现接近) ✓" if abs(a1 - a2) < 0.03
          else "仍对超参敏感 ✗"))
else:
    print("  (需要 auto 与 auto2 两个臂都完成)")

print()
print("=== 可靠标准 (leo: std < mean/2) ===")
for arm, vs in sorted(rows.items()):
    at = np.array([v["any_time_acc"] for _, v in vs], float)
    s = at.std(ddof=1) if len(at) > 1 else 0
    print("  %-8s std=%.4f  mean/2=%.4f  %s"
          % (arm, s, at.mean() / 2, "✓" if s < at.mean() / 2 else "✗"))