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 "✗"))
|