feat(abs-target): 方案 B 初版——Y 改 16 步绝对目标角(/180),训练/评估/rollout/ckpt 适配;修复前缀噪声增广对绝对目标的标签污染(eps 直接加到绝对位置=18°);实测一步生成误差 31°、闭环 0%,此配方不成立
This commit is contained in:
+23
-10
@@ -58,8 +58,12 @@ def open_loop(model, kin, vdata, stepv, fk_idx, n=1024, K=8):
|
||||
act, _, _ = model.sample_k(X, K)
|
||||
act = act.float().clamp(-1, 1)
|
||||
q = q_from_feat(X)
|
||||
qt = q + (Y * stepv).sum(1)
|
||||
qh = q[:, None] + (act * stepv).sum(2) # [B,K,6]
|
||||
if bool(getattr(model, 'abs_target', False)):
|
||||
qt = Y[:, -1] * 180.0
|
||||
qh = act[:, :, -1] * 180.0 # [B,K,6]
|
||||
else:
|
||||
qt = q + (Y * stepv).sum(1)
|
||||
qh = q[:, None] + (act * stepv).sum(2) # [B,K,6]
|
||||
pt = fk_points(kin, qt, fk_idx)[:, None]
|
||||
ph = fk_points(kin, qh.reshape(-1, 6), fk_idx).view(X.shape[0], K, -1, 3)
|
||||
err = (ph - pt).norm(dim=-1).mean(-1) # [B,K] mm
|
||||
@@ -98,13 +102,13 @@ def main():
|
||||
kin = Kin(device=dev)
|
||||
# 2026-10-19(审查 F11):val 只取主数据目录(第一个 --samples);DAgger 目录的样本全部当 train
|
||||
data = Samples(a.samples, device=dev, split='train', val_dirs=a.samples[:1])
|
||||
act_scale = data.act_scale
|
||||
act_scale = data.act_scale; abs_t = bool(data.abs_target)
|
||||
val_tasks = [t for t in load_tasks(a.samples[:1]) if t['split'] == 'val'][:tc['eval_tasks']]
|
||||
try: vdata = Samples(a.samples[:1], device=dev, split='val')
|
||||
except SystemExit: vdata = None
|
||||
if vdata is not None and vdata.n == 0: vdata = None
|
||||
print(f'train samples {data.n}, val tasks {len(val_tasks)}, act_scale {act_scale}, data_ver {data.data_ver}, device {dev}', flush=True)
|
||||
model = build(cfg).to(dev); model.act_scale = act_scale
|
||||
print(f'train samples {data.n}, val tasks {len(val_tasks)}, act_scale {act_scale}, abs_target {abs_t}, data_ver {data.data_ver}, device {dev}', flush=True)
|
||||
model = build(cfg).to(dev); model.act_scale = act_scale; model.abs_target = abs_t
|
||||
print(f'params {sum(p.numel() for p in model.parameters()) / 1e6:.2f} M', flush=True)
|
||||
opt = torch.optim.AdamW(model.parameters(), lr=tc['lr'], betas=tuple(tc['betas']), weight_decay=tc['wd'])
|
||||
step0, best, bad_evals = 0, -1.0, 0
|
||||
@@ -112,11 +116,14 @@ def main():
|
||||
s0 = float(ck.get('act_scale', 1.0))
|
||||
if abs(s0 - act_scale) > 1e-9:
|
||||
raise SystemExit(f'{src} 的 act_scale={s0} 与样本 act_scale={act_scale} 不一致:标签尺度变了,须从头训练(FRESH_FROM=prep)')
|
||||
a0 = bool(ck.get('abs_target', False))
|
||||
if a0 != abs_t:
|
||||
raise SystemExit(f'{src} 的 abs_target={a0} 与样本 {abs_t} 不一致:标签口径变了,须从头训练(FRESH_FROM=prep)')
|
||||
if init_ck is not None and not a.resume:
|
||||
check_scale(init_ck, a.init)
|
||||
model.load_state_dict(init_ck.get('ema') if init_ck.get('ema') is not None else init_ck['model'])
|
||||
print(f'微调:权重取自 {a.init}({"EMA" if init_ck.get("ema") is not None else "原始"}权重),优化器重置', flush=True)
|
||||
ema = copy.deepcopy(model).eval(); ema.act_scale = act_scale
|
||||
ema = copy.deepcopy(model).eval(); ema.act_scale = act_scale; ema.abs_target = abs_t
|
||||
for p_ in ema.parameters(): p_.requires_grad_(False)
|
||||
if a.resume:
|
||||
ck = torch.load(a.resume, map_location=dev); check_scale(ck, a.resume)
|
||||
@@ -131,7 +138,8 @@ def main():
|
||||
lr_at = lambda s: tc['lr'] * (s / tc['warmup'] if s < tc['warmup'] else 0.5 * (1 + math.cos(math.pi * (s - tc['warmup']) / max(1, total - tc['warmup']))))
|
||||
amp = dev.startswith('cuda') and tc.get('amp') == 'bf16'
|
||||
stepv = torch.tensor(STEP, device=dev)
|
||||
astep = stepv * act_scale # 动作 → 度
|
||||
# 方案 B:绝对目标标签(/180 归一化)→ 每单位动作 = 180°;增量口径 = act_scale·STEP
|
||||
astep = torch.full((6,), 180.0, device=dev) if abs_t else stepv * act_scale
|
||||
fk_idx = torch.linspace(0, kin.cap_a.shape[0] - 1, tc['fk_caps'], device=dev).long()
|
||||
ema_d = float(tc.get('ema', 0.999))
|
||||
warm = 0 if model.head == 'wta' else int(tc.get('p_neq_warm', 5000))
|
||||
@@ -144,7 +152,7 @@ def main():
|
||||
saver = Saver(tc.get('ckpt_min', 10)) # 2026-10-18:每 10 分钟 + 收到停止请求时存 last.pt(原子写)
|
||||
def save(name, step, extra=None):
|
||||
d = {'model': model.state_dict(), 'ema': ema.state_dict(), 'opt': opt.state_dict(), 'cfg': cfg, 'step': step, 'best': best,
|
||||
'bad_evals': bad_evals, 'fp': kin.fp, 'act_scale': act_scale, 'data_ver': data.data_ver}
|
||||
'bad_evals': bad_evals, 'fp': kin.fp, 'act_scale': act_scale, 'abs_target': abs_t, 'data_ver': data.data_ver}
|
||||
if name == 'last.pt': d['rng'] = rng_state()
|
||||
if extra: d.update(extra)
|
||||
atomic_save(d, os.path.join(a.out, name))
|
||||
@@ -172,12 +180,17 @@ def main():
|
||||
l_sm = torch.zeros((), device=dev); l_fk = torch.zeros((), device=dev)
|
||||
if xh.shape[0] > 0:
|
||||
d2 = xh[:, 2:] - 2 * xh[:, 1:-1] + xh[:, :-2]
|
||||
if abs_t: d2 = d2 * 180.0 / (sum(STEP) / len(STEP)) # 绝对目标 → 与增量口径同量纲(度/STEP)
|
||||
l_sm = d2.pow(2).mean()
|
||||
if tc['w_fk'] > 0 and xh.shape[0] > 0:
|
||||
q = q_from_feat(Xf)
|
||||
ks = torch.tensor([3, 7, 11, 15], device=dev)
|
||||
qh = q[:, None] + torch.cumsum(xh.clamp(-1, 1) * astep, 1)[:, ks]
|
||||
qt = q[:, None] + torch.cumsum(Yf * astep, 1)[:, ks]
|
||||
if abs_t:
|
||||
qh = (xh.clamp(-1, 1) * 180.0)[:, ks]
|
||||
qt = (Yf * 180.0)[:, ks]
|
||||
else:
|
||||
qh = q[:, None] + torch.cumsum(xh.clamp(-1, 1) * astep, 1)[:, ks]
|
||||
qt = q[:, None] + torch.cumsum(Yf * astep, 1)[:, ks]
|
||||
B4 = qh.shape[0] * 4
|
||||
ph = fk_points(kin, qh.reshape(B4, 6), fk_idx); pt = fk_points(kin, qt.reshape(B4, 6), fk_idx)
|
||||
l_fk = ((ph - pt).norm(dim=-1) / 10).mean()
|
||||
|
||||
Reference in New Issue
Block a user