TL;DR
- 질문: base Cosmos3-Nano FD는 "어떻게 집든 집히고 안 떨어지는" 세계를 그린다(N=4에서 낙하 재현 14%, specificity 20%, under-commit 편향). RoboLab 정책 rollout의 성공 + 실패 trajectory로 FD finetune하면 실패 물리(낙하·전도·헛집기·충돌 붕괴)를 더 길게·정확히 재현하고, 같은 맥락 다른 액션 → 다른 결과를 아는가?
- 설계 핵심: 학습 인터페이스를 base FD와 완전히 동일하게 고정 — ee_pose 10D quantile · JSON 프롬프트 · chunk 16 · concat_view 480 · domain droid_lerobot · 사전학습 action head 로드 → 기존
fd_replay_* 평가 파이프라인에 --checkpoint-path만 바꿔 꽂는다.
- 데이터: 기존 8 run dir 2,400 ep(영상 보존 성공 261 / 실패 1,642, 프레임 20:1)로 즉시 시작. 실패 에피소드의 클립은 대부분 정상 조작이므로 윈도우 단위로 성공 : 실패-사건(±2s keep-ranges) : 실패-균일 = 1:1:1 혼합. RoboLab 재수집(물체 pose 로깅 + 성공 전량 보존)을 병행해 Phase 2에 합류.
- 비교: base / FT-succ(성공만, 도메인 적응 대조) / FT-mix — own 4 GPU × 12–24h × 2 run. 평가 E1(v6 프로토콜, N=2/4/8) + E2 counterfactual(자연 쌍 cross-assignment, 조기-release 편집) + E3 seen/unseen 태스크 tier.
1 배경 / 목적
FD replay v4–v6(리포트)에서 base WM의 한계가 정량화됐다: 재접지 1–2초(N=2)에서는 낙하 88% · 전도 70% · 타워 붕괴 10/10을 재현하지만 3.2–4.3초(N=4)에서 낙하 14%, specificity 20%로 붕괴하고, 편향은 일관되게 under-commit(계속 쥐고 있음·스택 계속 서 있음)이다. base는 실로봇 DROID 성공 시연으로 학습됐고 NVIDIA 정책 레시피도 success split만 쓴다 — 실패 물리를 본 적이 거의 없다.
기존 한계: 긴 horizon에서 "집으면 집히고, 안 떨어지고, 안 무너지는" 낙관 세계. grasp_miss는 물체 pose가 없어 측정 불가.
가설 (사전 등록)
| ID | 가설 | 지표 | 성공 기준 |
| H1 | FT-mix는 실패 사건을 더 긴 horizon에서 재현 | E1 N=4 사건 재현율, beats_copy | base(drop 14%·stack 55%) 대비 ≥ +30%p, N=2 ≥ 85% 유지 |
| H2 | 성공은 여전히 성공으로 통과 (낙관↔비관 뒤집힘 없음) | E1 specificity, BA | N=2 spec ≥ 80%, BA ≥ 88%; N=4 BA ≥ 75% (base 60%) |
| H3 | 같은 맥락, 다른 액션 → 다른 결과 | E2 cross-assignment, 조기-release 낙하 | ≥ 80% / ≥ 70% |
| H4 | 개선은 도메인 적응만이 아니라 실패 데이터 덕분 | FT-mix − FT-succ | N=4 사건 재현율 ≥ +15%p (미달 → 도메인 적응 해석, Phase 2 강화) |
| H5 | unseen 태스크 일반화 | E3 test-B tier | seen 대비 하락 ≤ 15%p |
2 설계 결정과 근거 (코드로 확인)
| 결정 | 선택 | 근거 |
| 학습 모드 | forward_dynamics 전용 | 공식 factory get_action_droid_sft_dataset는 mode를 안 넘겨 기본 joint(fd/id/policy 랜덤)로 돈다 → 우리 factory에서 명시 |
| 액션 공간 | ee_pose 10D [Δpos3, Δrot6d, grip], DROID quantile | 추론이 EMBODIMENT_TO_RAW_ACTION_DIM["droid_lerobot"]=10을 assert. 정책 레시피의 joint_pos 8D면 기존 FD 평가·사전학습 action head 모두 버려야 함 |
| 사전학습 action head | 로드 유지 | keys_to_skip_loading에서 action2llm/action_pos_embed/action_modality_embed 제거 — base WM이 FD에 이미 쓰는 head |
| 프롬프트 | JSON (format_prompt_as_json=True) | 추론 코드는 항상 ActionPromptJsonFormatter. 공식 SFT factory는 이 플래그를 안 넘김(README "JSON at both train and eval"과 코드 불일치) → 우리가 맞춤. caption = RoboLab instruction, CFG dropout 10% |
| chunk / 뷰 / 해상도 | 16 (17프레임) / concat_view / 480 tier | base FD·fd_replay와 동일. wrist 640×360 위 + L/R 320×180 아래 = 640×540 → 736×544 reflection-pad |
| 혼합 단위 | 윈도우: 성공 : 실패-사건 : 실패-균일 = 1:1:1 | 실패 에피소드도 16프레임 클립 대부분은 정상 조작(사용자 지적). 신호는 에피소드 라벨이 아니라 사건 프레임. 사건 윈도우는 공식 use_filter_dict keep-ranges 재사용(프레임워크 패치 0, episode_id로 키 포맷만 맞춤) |
| lr / 옵티마이저 | 5e-5 cosine warmup 200 / 내장 FusedAdam | 2e-4는 fresh action head용; 사전학습 WM 소규모 FT는 LIBERO 레시피 5e-5 전례. venv에 apex 없음 확인 — FusedAdam은 프레임워크 내장 |
| 정규화 stats | DROID quantile 그대로 (clamp 10–13%) | 재적합하면 base와 인터페이스가 달라져 비교 불가. 리스크로 기록 |
공식 코드 vs 신규 코드
공식 그대로: cosmos_framework.scripts.train · DROIDLeRobotDataset · ActionTransformPipeline · convert_model_to_dcp · export_model
신규: scripts/robolab_to_lerobot.py (RoboLab → LeRobot v3 success/·failure/, DROID 피처명)
scripts/build_wm_split.py · scripts/build_event_keep_ranges.py · scripts/test_wm_dataset_parity.py
configs/.../action_fd_robolab_nano.py (+ get_action_fd_sft_dataset) · examples/toml/sft_config/action_fd_robolab_{succ,mix}.toml
scripts/train_fd_robolab.sh (sbm용, #SBATCH 없음) · scripts/export_fd_ckpt.sh · scripts/fd_counterfactual.py · scripts/fd_vlm_judge.py
데이터 변환 스키마 (RoboLab → LeRobot v3)
| 피처 (DROID 이름) | 소스 | 비고 |
observation.image.{wrist_image_left, exterior_image_1_left, exterior_image_2_left} | 센서 mp4 2560×360의 패널 0/1/2 (head 버림) | 640×360 @15Hz, 프레임 수 = T−1 |
observation.state.cartesian_position [6] | panda_fk_link8(achieved[:, :7]) → xyz + euler_xyz | fd_replay_prep와 동일 FK; euler round-trip 단위테스트 <1e-5 |
action.gripper_position [1] | executed_action[:, 7] | 1=닫힘, 데이터셋이 1−g 변환 |
task | instruction 문자열 | JSON 프롬프트 description |
episode_id (episodes parquet 추가) | {tag}_{task}_run{run}_env{env} | keep-ranges 키 |
정합성 보증: 변환 후 DROIDLeRobotDataset(mode="forward_dynamics", action_space="ee_pose")[i]의 정규화 action이 같은 에피소드·시작 스텝의 fd_replay_prep action_chunk*.json과 max|Δ| < 1e-4, 비디오 합성이 cond_step*.png와 픽셀 일치, 프롬프트 문자열 diff 0 — 이게 "학습 입력 = 기존 평가 입력"의 증거가 된다.
3 실험 구성
데이터 현황 (기존 8 run dir)
| 에피소드(영상 보존) | 프레임 | 중앙 길이 | 시간 |
| 성공 | 261 | 126,989 | 259 스텝 | 2.4h |
| 실패 | 1,642 | 2,533,650 | 900 스텝 | 46.9h |
성공 희소는 KEEP_SUCCESS=2 pruning 탓 → Phase 2 재수집에서 전량 보존. 물체 pose는 trace/hdf5/log 어디에도 없음 → grasp_miss GT는 Phase 2에서.
분할
- test-A (seen task / unseen ep): 기존 코호트 v4 48 + v5 24 + v6 16 + v6b 16 (≈100 ep, base 수치 보유)
- test-B (unseen task): 15 태스크 통째 제외 (쌓기 ≥4, drop-prone ≥6, grasp_miss ≥2, 각 성공 ≥2 & 실패 ≥4) — Phase 0에서 확정 후 불변
- val 5% (성공/실패 층화, loss 분리 모니터링) / train 나머지
학습 run
| run | 데이터 | 자원 | 비고 |
| FT-succ | success train (≈12만 윈도우) | own 4 GPU, 12–24h | 도메인 적응 대조. 에폭 많음 → 과적합 감시 |
| FT-mix | succ : fail_event(keep-ranges ±2s) : fail_uniform = 1:1:1 | 동일 iter | 본 실험 |
global batch 128 = 4 rank × 16 samples × accum 2 (스모크에서 32 시도) · lr 5e-5 cosine(warmup 200, f_min 0.1) · loss_scale 10 · grad_clip 1.0 · EMA power
max_iter = floor(20h·3600 / s_iter) (스모크 30 iter로 측정, 500 단위) · save_iter 500 · val 250 iter (성공/실패 entry 분리)
제출: sbm "bash scripts/train_fd_robolab.sh mix" --qos=own --gres=gpu:4 -c 32 --mem 800GB --time=26:00:00 --container=nvcr.io/nvidia/cuda:12.8.0-devel-ubuntu22.04
평가
| 평가 | 내용 | 규모 |
| E1 FD replay v6 프로토콜 | base / FT-succ / FT-mix × N=2/4/8, 사건 정렬 윈도우, 사건 재현율 · advanced/beats_copy(LPIPS) · pre-flight recall/spec/BA · 장면 무결성, copy 베이스라인 | test-A ≈100 + test-B 코호트 ≈32, ≈9잡 × 1 GPU |
| E2(a) 자연 쌍 cross-assignment | 같은 태스크·같은 초기 장면(frame0 LPIPS<0.05 가드)의 성공 S / 실패 F: 각자 실제 프레임으로 N=2, 사건 청크 예측이 자기 실제 결과에 더 가까운가 | ≥ 40 쌍 |
| E2(b) 조기-release 편집 | 성공 에피소드 이송 구간 중간에 gripper만 open으로 편집 → 예측에서 물체가 그리퍼를 떠나는가 + 원본 예측과 LPIPS 발산 (GT는 Phase 2 sim 실행으로) | ≥ 30 |
| E3 일반화 tier | E1/E2를 test-A(seen) vs test-B(unseen)로 분리 — 장면 암기 vs 물리 학습 구분 | — |
| E4 grasp_miss (Phase 2) | 물체 pose GT 기반 파지/lift 판정 | 재수집 후 |
4 Phase · 컴퓨트 · 리스크
| Phase | 내용 | GPU-h | 일정 |
| 0 | 변환기·분할·keep-ranges·config·DCP 변환 · 스모크(4 GPU 30 iter → export → fd_replay 2 ep) | ~6 | D+0–2 |
| 1 | FT-succ / FT-mix 학습 + E1–E3 + 리포트 | ~160 | D+2–6 |
| 2 | 재수집(물체 pose 로깅, KEEP_SUCCESS=all, NUM_ENVS=10, 30태스크 샤드 × 2 sweep, extra) · 섭동 replay 정책(조기 release/파지 오프셋/과속/충돌) · FT-mix-v2 · E4 | ~175 | 병행 시작, D+6–10 합류 |
주요 리스크: (1) 과적합 — benchmark env 간 초기 장면 동일 + 성공 12만 윈도우 수 에폭 반복 → val 분리 감시·중간 ckpt E1-lite·test-B tier; (2) 4 GPU 메모리(H200 8-shard에서 32 samples 한계) → 스모크 16/32; (3) quantile clamp 10–13%(base와 동일 조건이라 비교는 공정); (4) lerobot libsvtav1 미지원 → libx264 fallback; (5) detector 오탐은 가중 용도라 허용.
5 Takeaway & Next
의미
이 플랜의 가치는 "finetune하면 좋아진다"가 아니라 무엇 때문에 좋아졌는지를 분리하는 데 있다: FT-succ 대조로 도메인 적응을, test-B로 장면 암기를, E2로 "액션에 따라 결과가 갈리는가"를 각각 떼어 본다. 인터페이스를 base와 완전히 동일하게 묶어 두었기 때문에 개선/악화는 전부 데이터의 차이로 귀속된다.
즉시 다음
- Phase 0.2 변환기 + 0.3 분할 + 0.4 keep-ranges → login CPU 검증(정합성 단위테스트 3종)
- 0.5 config 3파일 + 0.6 ckpt 변환 → 스모크 3종 → s/iter로 max_iter 확정 → FT-succ/FT-mix 제출
- 학습 도는 동안 runner 물체 pose 로깅 패치 + 재수집 샤드 제출(extra)
상세: claude/260820/plan-wm_failure_finetune.md, 체크리스트: claude/plan.md