agentic-rl-main / scripts /analyze_rlsd_log.py
Jack04810's picture
Add files using upload-large-folder tool
d13981b verified
Raw History Blame Contribute Delete
4.34 kB
#!/usr/bin/env python3
"""Analyze RLSD training log health."""
from __future__ import annotations
import argparse
import ast
import re
import sys
from collections import Counter
from pathlib import Path
def parse_metrics(text: str) -> list[dict]:
metrics = []
for m in re.finditer(r"\{'loss':[^\n]+\}", text):
try:
metrics.append(ast.literal_eval(m.group()))
except (SyntaxError, ValueError):
pass
return metrics
def alert_counts(text: str) -> Counter:
return Counter(re.findall(r"\[ALERT\] (\w+)", text))
def health_snapshots(text: str) -> list[str]:
return re.findall(r"\[health\] step snapshot \| ([^\n]+)", text)
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("log_path")
args = parser.parse_args()
text = Path(args.log_path).read_text(encoding="utf-8", errors="replace")
alerts = alert_counts(text)
metrics = parse_metrics(text)
health = health_snapshots(text)
print("# RLSD 训练健康分析\n")
print("## 进度")
prog = re.findall(r"(\d+)%\|[^|]+\| (\d+)/(\d+)", text)
if prog:
pct, cur, total = prog[-1]
print(f"- 当前: step {cur}/{total} ({pct}%), epoch≈{metrics[-1].get('epoch', '?') if metrics else '?'}")
print(f"- 已记录 metrics 行数: {len(metrics)}")
print("\n## 防泄露指标 (RLSD 核心验收)")
if metrics:
last = metrics[-1]
checks = [
("opsd_on_correct_rate == 0", last.get("routing/opsd_on_correct_rate", -1) == 0.0),
("privileged_suffix_has_gold == 0", last.get("teacher/privileged_suffix_has_gold_rate", -1) == 0.0),
("leakage skipped only", last.get("routing/opsd_skipped_leakage", 0) == 0.0),
]
for name, ok in checks:
print(f"- [{'PASS' if ok else 'FAIL'}] {name}")
print(f"- routing/opsd_on_correct_rate: {last.get('routing/opsd_on_correct_rate', 'N/A')}")
print(f"- teacher/privileged_suffix_has_gold_rate: {last.get('teacher/privileged_suffix_has_gold_rate', 'N/A')}")
print(f"- routing/opd_teacher_call_rate: {last.get('routing/opd_teacher_call_rate', 'N/A')}")
print("\n## 学习信号 (是否真正在学)")
if metrics:
for idx in [0, 50, 100, 200, 500, 1000, len(metrics) - 1]:
if idx >= len(metrics):
continue
m = metrics[idx]
print(
f"- step {idx:4d}: acc={m.get('rewards/accuracy/mean', 0):.3f} "
f"fmt={m.get('rewards/format/mean', 0):.3f} "
f"len={m.get('completions/mean_length', 0):.1f} "
f"degen={m.get('completions/degenerate_rate', 0):.3f} "
f"grpo_zero={m.get('signal/grpo_zero_loss_rate', 0):.3f} "
f"loss={m.get('loss', 0):.4f}"
)
print("\n## ALERT 分布 (Top 10)")
for k, v in alerts.most_common(10):
print(f"- {k}: {v}")
print("\n## 最新 batch 生成样例 (step 1099 附近)")
samples = re.findall(r"decode_skip_special='([^']{1,80})'", text[-8000:])
if samples:
print(f"- 典型输出: {samples[:4]}")
print("\n## 综合判断")
if metrics:
last = metrics[-1]
degen = last.get("completions/degenerate_rate", 0)
acc = last.get("rewards/accuracy/mean", 0)
fmt = last.get("rewards/format/mean", 0)
grpo_zero = last.get("signal/grpo_zero_loss_rate", 1)
anti_leak_ok = (
last.get("routing/opsd_on_correct_rate", 1) == 0
and last.get("teacher/privileged_suffix_has_gold_rate", 1) == 0
)
learning_ok = acc > 0.05 and fmt > 0.3 and degen < 0.5 and grpo_zero < 0.9
print(f"- 防泄露路由: {'健康' if anti_leak_ok else '异常'}")
print(f"- 学习效果: {'不健康 (退化/零信号)' if not learning_ok else '尚可'}")
if degen >= 0.9:
print(" → 模型已塌缩为极短重复输出 (如 ' Sund\\n'),GRPO 无法介入")
if grpo_zero >= 0.99:
print(" → GRPO 全程零梯度,仅靠 self-OPSD 在更新")
if last.get("routing/sft_replaced_ratio", 0) == 0:
print(" → 在线 SFT 冷启动未触发 (可能组内总有部分样本非全错)")
return 0
if __name__ == "__main__":
sys.exit(main())