Files
Robot_Arm/ml/eval/check_r2.py
T

47 lines
2.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""方案 R2 数据侧检查:近邻样本的标签是否自洽(时间律不可观测成分会让相同输入对应不同动作)。
python -m ml.eval.check_r2 --samples ml/data/samples --out ml/data/r2.json
做法:特征逐维标准化;随机取 Q 个查询样本,在 P 个样本池里找最近邻(排除同一任务,避免同轨迹相邻帧冒充"近邻");
取距离最近的 5% 近邻对,比较前 3 步(实际提交的动作)标签:std ≈ RMS(ΔY)/√2,单位为每步最大步长 STEP 的比例。
判据:label_std < 0.10(即 < 10% 步长)。起步失败率在闭环仿真结果的 start_fail 字段(acceptance 汇总)。
2026-10-18 新增(审查:R2 无执行点)。GPU 上约 1 分钟;CPU 上把 --pool 降到 50000。"""
import argparse, json, math
import torch
from ml.data import Samples
def main():
ap = argparse.ArgumentParser()
ap.add_argument('--samples', nargs='+', default=['ml/data/samples']); ap.add_argument('--out', default='ml/data/r2.json')
ap.add_argument('--queries', type=int, default=20000); ap.add_argument('--pool', type=int, default=200000)
ap.add_argument('--frac', type=float, default=0.05); ap.add_argument('--seed', type=int, default=0)
ap.add_argument('--device', default='cuda' if torch.cuda.is_available() else 'cpu')
a = ap.parse_args()
d = Samples(a.samples, device=a.device, split='train')
g = torch.Generator(device=a.device); g.manual_seed(a.seed)
P = min(a.pool, d.n); Q = min(a.queries, d.n)
pi = torch.randperm(d.n, device=a.device, generator=g)[:P]; qi = torch.randperm(d.n, device=a.device, generator=g)[:Q]
X = d.X.float(); mu, sd = X[pi].mean(0), X[pi].std(0).clamp(min=1e-6)
Xp, Xq = (X[pi] - mu) / sd, (X[qi] - mu) / sd
tp, tq = d.TID[pi], d.TID[qi]
best_d = torch.empty(Q, device=a.device); best_j = torch.empty(Q, dtype=torch.long, device=a.device)
for s in range(0, Q, 1024):
D = torch.cdist(Xq[s:s + 1024], Xp)
D = torch.where(tq[s:s + 1024, None] == tp[None], torch.full_like(D, float('inf')), D)
v, j = D.min(1); best_d[s:s + 1024] = v; best_j[s:s + 1024] = j
fin = torch.isfinite(best_d)
thr = torch.quantile(best_d[fin], a.frac)
m = fin & (best_d <= thr)
Yq, Yp = d.Y[qi][m, :3].float(), d.Y[pi][best_j[m], :3].float()
rms = (Yq - Yp).pow(2).mean(-1).mean(-1).sqrt() # 每对、6 轴 × 3 步平均
std = (rms / math.sqrt(2))
out = {'pairs': int(m.sum()), 'nn_dist_threshold': thr.item(), 'label_std_mean': std.mean().item(),
'label_std_p90': torch.quantile(std, 0.9).item(), 'pass': std.mean().item() < 0.10,
'rule': 'label_std_mean < 0.10(10% 步长)'}
print(json.dumps(out, indent=1))
json.dump(out, open(a.out, 'w'), indent=1)
if __name__ == '__main__':
main()