diag: F 段——MeanFlow 一步采样残留噪声(开环 1/2/4/8 段与零噪声的幅度/抖动/残差-噪声相关;闭环 4 段、零噪声);sample_k 加 steps、rollout 加 steps/z_scale;删 E 段;本机执行清单更新

This commit is contained in:
Hark
2026-10-18 19:10:32 +00:00
parent cafa038110
commit 1bf59e7175
4 changed files with 52 additions and 124 deletions
+31 -54
View File
@@ -8,8 +8,7 @@
B. 同一噪声下,用两份特征得到的动作差(16 步末端关节差,度)——A 有差但 B 很小说明差异无害;
C. 闭环失败形态:首次碰撞时刻分布、碰撞步数、首次碰撞的组对、全程最小目标误差、目标误差进度比;
以及同一批题开安全层(safety=True,近似部署)的到达率 / 刹停次数。
E. 速度:开环动作幅度 / 老师幅度、抹掉 dq 后的幅度(是否「抄上一帧速度」);
从老师轨迹中段起步的闭环,带老师速度 vs 静止起步(是否只坏在起步)。
F. 一步采样残留噪声:开环比较 1/2/4/8 段采样与零噪声的幅度、抖动、残差与噪声相关;闭环用 4 段采样、零噪声各跑一遍。
用法(仓库根目录):
PYTHONUTF8=1 python -m ml.eval.diag_cl --ckpt ml/runs/base/last.pt --n 256 --out ml/runs/diag_cl.json
"""
@@ -124,65 +123,43 @@ def main():
'safety_on': {'reach': reach_s / n, 'collided': col_s / n, 'brakes_p50': pct(brakes_s, .5)}}
out['C']['goal_err_ratio_p50'] = {'%.1fs' % ts: pct(v, .5) for ts, v in prog.items()} # <1 = 在靠近目标
# ---------- E:速度与「抄速度」(2026-10-19 加) ----------
# 上一轮 D 段:不用子目标 / 用老师真实途经点,结果与基线完全一样(0% / 99.6%),子目标排除。
# C 段 2 s 只把目标误差缩小 18.5%:方向对、但走得极慢。开环(数据集里的老师状态)却几乎精确。
# 嫌疑:特征里的 dq(上一帧速度)在老师数据里总是满速,模型学会「照上一帧速度往下走」;
# 闭环从静止起步 dq=0,模型就给小动作,小动作又喂回 dq,一直慢,慢慢挤进自碰撞。
# ---------- F:一步采样残留噪声(2026-10-19 加) ----------
# 前几轮:子目标、抄速度、起步都已排除;但动作幅度在开环、起点、闭环三处一致是老师的 1.4–1.6 倍,
# 而开环 16 步末端 FK 误差只有 ~6 mm。幅度大、终点却准 = 每步动作里叠着来回抵消的抖动。
# 嫌疑:MeanFlow 一步生成 x = z − u(z,0,1) 没把噪声 z 去干净(只有 25% 样本学区间平均速度、14k 步),
# 闭环每次只执行前 3 步,抖动不抵消,表现为「动得多、走得少」,再挤进碰撞。
# F0:开环 val 随机 4096 行,比较 1/2/4/8 段采样和零噪声(z=0):幅度比、抖动比、残差与 z 的相关、前 3 步与 16 步末关节误差
# F1:同一批闭环题,用 4 段采样、零噪声各跑一遍,对比 C 的基线
stepv = torch.tensor(STEP, device=dev)
gen = torch.Generator(device=dev).manual_seed(11)
# E0:开环,val 全部时刻随机 4096 行。动作幅度 / 老师幅度;把 dq 抹成 0 后幅度与 16 步末端差
ri = torch.randperm(vd.X.shape[0], generator=torch.Generator().manual_seed(3))[:4096].to(dev)
Xr, Yr = vd.X[ri].float(), vd.Y[ri].float()
zr = torch.randn(len(ri), 1, H, 6, device=dev, generator=gen)
def amp(X):
with torch.no_grad(): ac, _, _ = model.sample_k(X, 1, zr)
return ac[:, 0].clamp(-1, 1)
a_full = amp(Xr); X0dq = Xr.clone(); X0dq[:, 15:21] = 0; a_0dq = amp(X0dq)
ymag = Yr.abs().mean().item()
moving = Xr[:, 15:21].abs().max(-1).values > 0.3 # 老师此刻在动(|dq| > 30% 满速)
out['E0_openloop'] = {'n': len(ri), 'teacher_amp': ymag, 'policy_amp_ratio': a_full.abs().mean().item() / ymag,
'dq_zeroed_amp_ratio': a_0dq.abs().mean().item() / ymag,
'dq_zeroed_amp_ratio_on_moving_rows': a_0dq[moving].abs().mean().item() / max(1e-6, Yr[moving].abs().mean().item()),
'dq_zeroed_end_diff_deg_p50': pct(((a_full - a_0dq) * stepv).sum(1).abs().max(-1).values.tolist(), .5),
'start_rows_amp_ratio': None}
st = first.nonzero().flatten()[:4096]
with torch.no_grad(): a_st, _, _ = model.sample_k(vd.X[st].float(), 1, torch.randn(len(st), 1, H, 6, device=dev, generator=gen))
out['E0_openloop']['start_rows_amp_ratio'] = a_st.clamp(-1, 1).abs().mean().item() / max(1e-6, vd.Y[st].float().abs().mean().item())
# E1:从老师轨迹中段(每题第 30% 行)起步闭环,带老师上一帧速度 vs 静止起步,同一批题同一噪声
tid_all = vd.TID.long(); starts = first.nonzero().flatten().tolist() + [len(tid_all)]
mid_rows = []
for s0, s1 in zip(starts[:-1], starts[1:]):
if int(tid_all[s0]) in by_tid and s1 - s0 >= 4: mid_rows.append(s0 + int(0.3 * (s1 - s0)))
mid_rows = mid_rows[:a.n]
Xm = vd.X[mid_rows].float()
qm = torch.atan2(Xm[:, 0:12:2], Xm[:, 1:12:2]) * 180 / torch.pi
qm = torch.where(kin.cont, qm, kin.clamp_box(qm))
qpm = qm - Xm[:, 15:21] * stepv
mtasks = []
for r_, q_ in zip(mid_rows, qm.tolist()):
t = dict(by_tid[int(tid_all[r_])]); t['q0'] = q_; t['exec_s'] = float(vd.V[r_]) * 5; mtasks.append(t)
rem = [t['exec_s'] for t in mtasks]
ym = vd.Y[mid_rows].float().abs().mean().item()
for tag, use_v in (('E1_mid_teacher_vel', True), ('E1_mid_zero_vel', False)):
rc = cc_ = 0; ratio1 = []; spd = []
for i in range(0, len(mtasks), a.bs):
ch = mtasks[i:i + a.bs]
zr = torch.randn(len(ri), 1, H, 6, device=dev, generator=torch.Generator(device=dev).manual_seed(11))
ymag = Yr.abs().mean().item(); yjit = (Yr[:, 1:] - Yr[:, :-1]).abs().mean().item()
f0 = {'teacher_amp': ymag, 'teacher_jitter': yjit}
for tag, steps, zs in (('1step', 1, 1.0), ('2step', 2, 1.0), ('4step', 4, 1.0), ('8step', 8, 1.0), ('1step_z0', 1, 0.0)):
with torch.no_grad(): ac, _, _ = model.sample_k(Xr, 1, zr * zs, steps=steps)
ac = ac[:, 0]; acc = ac.clamp(-1, 1); res = ac - Yr
zc = zr[:, 0] - zr[:, 0].mean(); rc_ = res - res.mean()
corr = ((zc * rc_).sum() / (zc.pow(2).sum().sqrt() * rc_.pow(2).sum().sqrt()).clamp(min=1e-9)).item() if zs > 0 else None
e3 = ((acc[:, :3] - Yr[:, :3]) * stepv).sum(1).abs().max(-1).values
e16 = ((acc - Yr) * stepv).sum(1).abs().max(-1).values
f0[tag] = {'amp_ratio': acc.abs().mean().item() / ymag, 'jitter_ratio': (acc[:, 1:] - acc[:, :-1]).abs().mean().item() / yjit,
'resid_corr_z': corr, 'first3_err_deg_p50': pct(e3.tolist(), .5), 'end16_err_deg_p50': pct(e16.tolist(), .5)}
out['F0_openloop_sampler'] = f0
for tag, kw in (('F1_4step', {'steps': 4}), ('F1_z0', {'z_scale': 0.0})):
rc = cc_ = 0; me = []; r2 = []
for i in range(0, len(tasks), a.bs):
ch = tasks[i:i + a.bs]
with torch.no_grad():
_, pp = rollout(model, kin, ch, dev, K=8, safety=False, seed=i, return_traj=True,
qp0=qpm[i:i + a.bs] if use_v else None)
_, pp = rollout(model, kin, ch, dev, K=8, safety=False, seed=i, return_traj=True, **kw)
tr = pp['traj'].to(dev); B, Tn, _ = tr.shape
k1 = min(Tn - 1, int(round(1.0 / DT)))
k2 = min(Tn - 1, int(round(2.0 / DT)))
Tt = torch.tensor([t['T'] for t in ch], dtype=torch.float32, device=dev)
Rt = torch.tensor([t['R'] if t.get('R') else [0.] * 9 for t in ch], dtype=torch.float32, device=dev)
e0, _ = goal_err(kin, tr[:, 0], Tt, Rt); e1, _ = goal_err(kin, tr[:, k1], Tt, Rt)
ratio1 += (e1 / e0.clamp(min=1e-3)).tolist()
dd = (tr[:, 1:k1 + 1] - tr[:, :k1]) / stepv
spd.append(dd.abs().mean().item())
rc += int(pp['done'].sum()); cc_ += int((pp['col'] > 0).sum())
nm = len(mtasks)
out[tag] = {'n': nm, 'teacher_remaining_s_p50': pct(rem, .5), 'reach': rc / nm, 'collided': cc_ / nm,
'goal_err_ratio_1s_p50': pct(ratio1, .5), 'speed_ratio_first_1s': sum(spd) / len(spd) / max(1e-6, ym)}
e0, _ = goal_err(kin, tr[:, 0], Tt, Rt); e2, _ = goal_err(kin, tr[:, k2], Tt, Rt)
r2 += (e2 / e0.clamp(min=1e-3)).tolist()
rc += int(pp['done'].sum()); cc_ += int((pp['col'] > 0).sum()); me += pp['err'].tolist()
out[tag] = {'reach': rc / n, 'collided': cc_ / n, 'goal_err_ratio_2s_p50': pct(r2, .5), 'end_goal_err_mm_p50': pct(me, .5)}
s = json.dumps(out, indent=1, ensure_ascii=False); print(s)
os.makedirs(os.path.dirname(a.out) or '.', exist_ok=True)
open(a.out, 'w', encoding='utf-8').write(s)