fix(train): 首次正式预训练(21850 步闭环到达 0%)排查后修正——FK/平滑改加在配对去噪估计上(原加在随机噪声一步采样上=回归条件均值,多峰塌成单峰,实测候选标准差 0.030/幅度 0.151);时间嵌入最高角频率 1000→50、MF 分支 fp32、p_neq 前 5000 步为 0(实测 du/dt 均方根 6–12 vs v 1.1);增广 40%/3°/12 步收回;新增 dagger 阶段(策略闭环状态请老师重规划 ×2 轮微调);开训前老师轨迹回放自检;评估加开环误差与候选多样性,best/早停按到达率+开环误差
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
"""老师轨迹回放自检(2026-10-18 首次正式预训练后新增;train 阶段开训前自动跑,约 1 分钟):
|
||||
把 gen 写出的老师 50 Hz 轨迹(keep=true)直接放进闭环仿真用的 PyTorch 运动学 / 碰撞 / 到达判据里检查。
|
||||
老师轨迹在 JS 侧已认证无碰、已到达;如果这里判出碰撞或未到达,说明仿真口径(kin.json、间隙、到达门槛)和数据对不上,
|
||||
那么闭环指标本身不可信,训练多少步都没用 —— 直接停下(rc 5)。
|
||||
python -m ml.eval.replay_teacher --n 512 --out ml/runs/replay_teacher.json
|
||||
判据:到达率(cat≠D)≥ 0.95,碰撞率 ≤ 0.01。"""
|
||||
import argparse, glob, json, os, sys
|
||||
import torch
|
||||
|
||||
from ml.common.kin_torch import Kin
|
||||
from ml.sim.closed_loop import goal_err
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument('--shards', default='ml/data/shard_*.jsonl'); ap.add_argument('--n', type=int, default=512)
|
||||
ap.add_argument('--out', default='ml/runs/replay_teacher.json')
|
||||
ap.add_argument('--device', default='cuda' if torch.cuda.is_available() else 'cpu')
|
||||
a = ap.parse_args()
|
||||
kin = Kin(device=a.device)
|
||||
recs = []
|
||||
for f in sorted(glob.glob(a.shards)):
|
||||
for line in open(f, encoding='utf-8'):
|
||||
r = json.loads(line)
|
||||
if r.get('keep') and r.get('traj') and r.get('cat') != 'D': recs.append(r)
|
||||
if len(recs) >= a.n: break
|
||||
if len(recs) >= a.n: break
|
||||
if not recs: sys.exit('没有可回放的老师轨迹(先跑 gen)')
|
||||
reach = col = 0; worst_gap = []
|
||||
for r in recs:
|
||||
q = torch.tensor(r['traj'], dtype=torch.float32, device=a.device)
|
||||
mid = 0.5 * (q[1:] + q[:-1])
|
||||
g = kin.gaps(torch.cat([q, mid], 0), 0.5)['gm'].min(-1).values
|
||||
hit = bool((g <= 0).any()); col += hit; worst_gap.append(g.min().item())
|
||||
T = torch.tensor([r['T']], dtype=torch.float32, device=a.device)
|
||||
R = torch.tensor([r['R'] if r.get('R') else [0.] * 9], dtype=torch.float32, device=a.device)
|
||||
e, oe = goal_err(kin, q[-1:], T, R)
|
||||
reach += bool((e < 1).item() and (oe < 0.5).item())
|
||||
n = len(recs)
|
||||
res = {'n': n, 'reach': reach / n, 'collision': col / n, 'min_gap_mm_p10': sorted(worst_gap)[n // 10],
|
||||
'pass': reach / n >= 0.95 and col / n <= 0.01, 'fp': kin.fp}
|
||||
os.makedirs(os.path.dirname(a.out) or '.', exist_ok=True)
|
||||
json.dump(res, open(a.out, 'w', encoding='utf-8'), indent=1)
|
||||
print('老师轨迹回放:到达 %.1f%%、碰撞 %.1f%%(%d 条)' % (100 * res['reach'], 100 * res['collision'], n), flush=True)
|
||||
sys.exit(0 if res['pass'] else 5)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user