exp+fix: 候选塌缩定案(steps=2 把K条候选抹平35倍)+z_scale反例;补 n_plans/brake_rate 核算§9.2刹停率;记录我在链式作业排队期间改库导致两臂未跑的事故
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"K": 8,
|
||||
"n": 256,
|
||||
"steps1": {
|
||||
"cand_spread": 0.8518,
|
||||
"first_step_norm": 1.1565,
|
||||
"clamp_saturated_rate": 0.0516,
|
||||
"pairwise_mean": 5.3647
|
||||
},
|
||||
"steps2": {
|
||||
"cand_spread": 0.0245,
|
||||
"first_step_norm": 1.0956,
|
||||
"clamp_saturated_rate": 0.0385,
|
||||
"pairwise_mean": 0.3024
|
||||
},
|
||||
"steps4": {
|
||||
"cand_spread": 0.0211,
|
||||
"first_step_norm": 1.097,
|
||||
"clamp_saturated_rate": 0.0689,
|
||||
"pairwise_mean": 0.2861
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
{
|
||||
"config": {
|
||||
"ckpt": "ml/runs/base_dag2_dag1/best.pt",
|
||||
"samples": "ml/data/samples",
|
||||
"tasks": null,
|
||||
"split": "val",
|
||||
"n": 256,
|
||||
"K": 8,
|
||||
"commit": 2,
|
||||
"fb": 2.2,
|
||||
"ki": 0.0,
|
||||
"fb_scale": 0.0,
|
||||
"fb_near": 0.2,
|
||||
"fb_fallback": 0.0,
|
||||
"kdecay": 0.9,
|
||||
"kimax": 0.5,
|
||||
"fb_every": 1,
|
||||
"safety_try": 0,
|
||||
"sel_w": 0.1,
|
||||
"dls_safety": 1,
|
||||
"fb_clear": 0.0,
|
||||
"fb_cmin": 0.3,
|
||||
"sub_refresh": 0,
|
||||
"fb_block_gain": 0.5,
|
||||
"dump": null,
|
||||
"out": "docs/experiment_round39_z15.json",
|
||||
"trace": null,
|
||||
"select": "endpoint",
|
||||
"replan": "empty",
|
||||
"safety": 1,
|
||||
"partial": 1,
|
||||
"inc_cap": 1,
|
||||
"gap_margin": 0.0,
|
||||
"cert_batch": 0,
|
||||
"steps": 1,
|
||||
"z_scale": 1.5,
|
||||
"len_w": 0.0,
|
||||
"backoff": 0,
|
||||
"max_sec": null,
|
||||
"seed": 0,
|
||||
"dls": "analytic",
|
||||
"device": "cuda"
|
||||
},
|
||||
"batch_size": 256,
|
||||
"batch_seed_rule": "same seed per batch",
|
||||
"checkpoint_sha256": "f7f686fd8ee92fb6948fbcc4ee954636065ad87bb54f661c4f8f575bc16c2ce1",
|
||||
"tasks_sha256": "249bd93457825cda98c59e64d9a096d3473c69aeb3553de1dea6d9e56be71772",
|
||||
"kin_fp": "2ebcbf17",
|
||||
"act_scale": 1.0,
|
||||
"metrics": {
|
||||
"n": 256,
|
||||
"n_ref": 256,
|
||||
"success": 0.49609375,
|
||||
"collision_free_success": 0.49609375,
|
||||
"time_ok_1p3": 0.18359375,
|
||||
"collision_rate": 0.0,
|
||||
"collision_steps_mean": 0.0,
|
||||
"travel_ratio_median": 3.52425217628479,
|
||||
"time_mean_s": 2.805511713027954,
|
||||
"brakes_mean": 10.51171875,
|
||||
"start_fail": 0.0,
|
||||
"goal_ratio_2s": 0.27530819177627563,
|
||||
"err_fail_med": 105.35015869140625,
|
||||
"err_success_med": 0.23657508194446564,
|
||||
"dls_share": 0.62890625,
|
||||
"time_budget": "teacher×2(≥3 s)",
|
||||
"speed_ratio_p50": 1.0000016689300537,
|
||||
"speed_ratio_p95": 1.0000101327896118,
|
||||
"speed_ratio_max": 1.0000101327896118,
|
||||
"over_vmax_rate": 0.0,
|
||||
"accel_ratio_p95": 1.0000016689300537,
|
||||
"accel_ratio_max": 1.0000101327896118,
|
||||
"reverse_p50": 0.32083332538604736,
|
||||
"reverse_mean": 0.3213125765323639,
|
||||
"reverse_moving_p50": 0.35158151388168335,
|
||||
"moving_share_p50": 0.9866666793823242
|
||||
},
|
||||
"simulation_wall_s": 311.9796243000019
|
||||
}
|
||||
@@ -107,6 +107,28 @@
|
||||
- **对 `backoff` 的预期与判据**:全拒意味着"连第 1 步的端点或中点已经穿进障碍",缩步能不能救取决于**穿得有多深**(穿 0.1 mm 救得回,穿 3 mm 救不回)。这个数转储里没记(只记被选中那条),所以直接由 `backoff_mean`/`reject_all_mean` 来量:
|
||||
`reject_all_mean ≈ 原全拒次数`(=backoff_mean+brakes_mean,与轮次 33 cap 的 19.55 可比),`backoff_mean/(backoff_mean+brakes_mean)` 就是"缩步能救回的比例"。这是补跑(#22)要读的第一个数。
|
||||
|
||||
## 候选为什么会"塌":两段积分把 K 条候选吸成一条(轮次 39 探针,机理定案)
|
||||
|
||||
- `ml/runs/_cand_div.py`(256 题特征、同一批噪声 z、K=8,只改段数):
|
||||
|
||||
| steps | 候选间一阶步的标准差 spread | 两两平均距离 pairwise | 第 1 步模长 | ±1 饱和率 |
|
||||
|---|---|---|---|---|
|
||||
| 1 | **0.8518** | 5.3647 | 1.1565 | 5.2% |
|
||||
| 2 | **0.0245** | 0.3024 | 1.0956 | 3.9% |
|
||||
| 4 | 0.0211 | 0.2861 | 1.0970 | 6.9% |
|
||||
|
||||
⇒ **多段积分把 K 条候选的差别抹掉了 35 倍**(0.85 → 0.024)。这不是"动作更大更危险"那么简单:安全层是在 K 条**几乎相同**的候选里挑一条能走的前缀,挑无可挑就刹停。
|
||||
- 这条与轮次 38 的实测严丝合缝:`steps=2/4/8` 到达掉 13–15 pp、刹车 ×4.5,但**开环误差与行程比都更好**——动作质量提升是真的,到达率下降是"没有备选"造成的。
|
||||
- **但"人为制造多样性"不是解法**(`z_scale=1.5`,同 K=8):刹车确实砍一半(19.55→10.51),可到到达掉到 0.4961、时限内从 0.2656 崩到 **0.1836**、行程比恶化到 3.5243、换向升到 0.32。⇒ 放大噪声换来的"能过的候选"是**分布外**的候选:安全层放行它,但它走的是绕、抖、慢的路。**K=16 的收益来自"同分布内更多候选",不是"多样性本身"**——这两个数放在一起才把这条轴的边界说清楚。
|
||||
- 判据状态:#21 的两个分支现在这样分:**"动作更危险"不是主因,"候选塌缩"是主因**(spread 35 倍 + try/K 单调曲线 + z_scale 反例三条独立证据)。剩下要量的只是成本口径(K=16 在批量认证下贵多少)。
|
||||
|
||||
## 事故:我在链式作业排队期间编辑了被 import 的库,两个臂直接没跑
|
||||
|
||||
- 时间线:`z15` 于 14:46:38 结束,链接着拉起 `safety=0 steps=1`(14:46:38)——而我为了让 `brake_rate` 有分母,正在 14:45:5x 用脚本改 `ml/sim/closed_loop.py`,一次替换把 `per = {...}` 的字典提前闭合,留下 `IndentationError: unexpected indent`。**新起的 python 进程 import 到的是半成品文件**,两个 safety-off 臂各 2 秒就退出(日志里 `=== safety=0 steps=1 done 14:46:40 ===` 的"done"只是 bash 循环走到下一行,不代表成功)。修好在 14:46:52,48 项测试全绿。
|
||||
- **教训(比我原先记的"别改正在跑的脚本"更进一层)**:链式作业会在**未来某个时刻**新起解释器,所以"只要不改正在跑的那一个进程"是不够的。规则改为:**链没结束就不编辑被 import 的库;要编辑,先等通知或先让链跑完**。并且 `=== done ===` 这种循环日志不能当成功凭据——**判成功要看产物文件在不在**(这次 `docs/experiment_round38_safetyoff_steps{1,2}.json` 根本没生成,一看便知)。
|
||||
- 两个 safety-off 臂与 `ep_waste`/`backoff`/K 代价一起并入下一批重跑(`ml/runs/_next_batch.sh`)。
|
||||
- 顺带补了验收判据缺的分母:`rollout` 现在记 `n_plans`(每题规划次数)并给 `brake_rate = brakes/n_plans`、`eval_cl` 报 `brake_rate_p50/mean` 与 `n_plans_mean`——§9.2 要的是"刹停 <2% **推理周期**",以前只有绝对次数,**这条判据实际上从来没被真正核算过**。命名上躲开一个坑:`per` 里原本有个 `'plans'` 键是转储用的逐规划字典,新键必须叫 `n_plans`,否则 `flat` 的过滤 `if k not in ('traj','plans')` 会把它静默吃掉。
|
||||
|
||||
## 试候选数的成本-收益曲线(轮次 39,四档齐全,#15 主结果)
|
||||
|
||||
- 同一 256 题、同 seed、`--inc_cap 1`、其余配方(`select=endpoint, replan=empty, commit_n=2, K=8, fb=2.2`)不变,只改**安全层允许试几条候选**:
|
||||
|
||||
+4
-1
@@ -54,6 +54,9 @@ def run(model, kin, tasks, device, bs=256, **kw):
|
||||
out.update({'reverse_p50': median(rr), 'reverse_mean': rr.mean().item(),
|
||||
'reverse_moving_p50': median(flat['reverse_rate_moving']), # 只在动了的步上算,不被刹停稀释
|
||||
'moving_share_p50': median(flat['moving_share'])}) # 活动窗内动了的步占比(=1−站住比例)
|
||||
if 'brake_rate' in flat: # §9.2 判据是"刹停 <2% 推理周期",所以要按规划次数归一,不能只报绝对次数
|
||||
out.update({'brake_rate_mean': flat['brake_rate'].mean().item(), 'brake_rate_p50': median(flat['brake_rate']),
|
||||
'n_plans_mean': flat['n_plans'].mean().item()})
|
||||
if 'backoff' in flat and flat['backoff'].any(): # backoff=缩步顶上的次数;brakes 现在只记连最小档都不安全的
|
||||
out['backoff_mean'] = flat['backoff'].mean().item() # ⇒ 原来的"全拒"事件数 = 两者之和
|
||||
out['reject_all_mean'] = (flat['backoff'] + flat['brakes']).mean().item()
|
||||
@@ -139,7 +142,7 @@ def main():
|
||||
Path(a.out).write_text(json.dumps(json_finite(result), indent=2, ensure_ascii=False, allow_nan=False) + '\n', encoding='utf-8')
|
||||
if a.dump:
|
||||
rows = []
|
||||
keys = ['done', 'ok', 't_done', 'limit', 'col', 'brakes', 'err', 'first_hit', 'dls_it', 'goal_ratio_2s']
|
||||
keys = ['done', 'ok', 't_done', 'limit', 'col', 'brakes', 'err', 'first_hit', 'dls_it', 'goal_ratio_2s', 'brake_rate', 'n_plans']
|
||||
flat = {k: torch.cat([p[k] for _, p, _ in pers]).tolist() for k in keys}
|
||||
for i, t in enumerate(tasks):
|
||||
rows.append({**t, 'i': i, **{k: flat[k][i] for k in keys}})
|
||||
|
||||
@@ -204,6 +204,7 @@ def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None,
|
||||
vratio = torch.zeros(B, device=device); aratio = torch.zeros(B, device=device); prev_exec = torch.zeros(B, 6, device=device)
|
||||
rev = torch.zeros(B, device=device); nact = torch.zeros(B, device=device); prev_sgn = torch.zeros(B, 6, device=device)
|
||||
nmov = torch.zeros(B, device=device)
|
||||
nplan = torch.zeros(B, device=device) # 每题规划次数 = 推理周期数(§9.2 的"刹停 <2% 推理周期"要这个分母)
|
||||
traj = [q.clone()] if return_traj else None
|
||||
plans = {k: [] for k in ('it', 'r', 'qe', 'commit', 'points', 'midpoints', 'gaps', 'certified')} if return_traj else None
|
||||
n_start = int(round(0.5 / DT)); moved_start = None # R2 起步失败:0.5 s 内关节总行程 < 1°
|
||||
@@ -216,6 +217,7 @@ def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None,
|
||||
for it in range(nmax):
|
||||
tnow = it * DT
|
||||
if (queue.shape[1] <= 3) if replan == 'r3' else (queue.shape[1] == 0):
|
||||
nplan += 1.0 # 验收判据要"刹停 / 推理周期"的比值,分母就是这个(批内所有行同步规划)
|
||||
r = queue.shape[1]
|
||||
pre = torch.zeros(B, 3, 6, device=device)
|
||||
qe = q
|
||||
@@ -513,6 +515,7 @@ def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None,
|
||||
side = torch.where(j1_first != 0, j1_first, torch.where(j1_max >= -j1_min, torch.ones_like(j1_max), -torch.ones_like(j1_max)))
|
||||
per = {'done': done.cpu(), 'ok': ok.cpu(), 't_done': t_done.cpu(), 'limit': limit.cpu(), 'col': col.cpu(), 'travel': travel.cpu(), 'err': e_end.cpu(),
|
||||
'side': side.cpu(), 'start_fail': start_fail.cpu(), 'brakes': brakes.cpu(), 'backoff': bo.cpu(), 'first_hit': first_hit.cpu(), 'dls_it': dls_it.cpu(),
|
||||
'n_plans': nplan.cpu(), 'brake_rate': (brakes / nplan.clamp_min(1.0)).cpu(),
|
||||
'goal_ratio_2s': ratio2.cpu(), 'speed_ratio': vratio.cpu(), 'accel_ratio': aratio.cpu(),
|
||||
# 口径与离线分解一致:换向的 (步,轴) 对数 / 活动的 (步,轴) 对数 ∈ [0,1](除以 6 个轴)
|
||||
'reverse_rate': (rev / (6.0 * nact.clamp_min(1.0))).cpu(),
|
||||
|
||||
Reference in New Issue
Block a user