Index
2026-08-20 — Progress

WM finetune Phase 0 — 데이터 변환 · 분할 · config · 스모크

cosmos | 플랜(260820 plan) Phase 0 실행 기록 · FT-mix own 대기 제출

TL;DR

1,900
변환 ep
54.6 GB
LeRobot v3
8/8
parity 통과
15
unseen 태스크
5 s→1.4 s
aug /샘플
8000
FT-mix iter

1 배경 / 목적

플랜 Phase 0을 최단 경로로 끝내고 FT-mix(성공+실패)를 own 4 GPU에 먼저 투입한다(사용자: own 4장 가용 → 실패 섞은 run부터). 제약은 공식 트레이너/데이터셋 클래스를 그대로 쓰면서 학습 입력이 기존 base FD 평가 입력과 동일함을 테스트로 보증하는 것.

2 작업 내용

2.1 변환기 scripts/robolab_to_lerobot.py

lerobot의 writer(프레임마다 PNG → 나중 인코딩, 8M 파일)를 버리고 PyAV로 4패널 mp4 스트리밍 디코드 → 3패널 crop → per-view h264(crf 23, GOP 8, bf 0) 직접 인코딩; parquet/meta는 DROIDLeRobotDataset이 읽는 컬럼만 정확히 씀. 패널 순서 [head|left|right|wrist] 80/80 확인. cartesian_position = FK(achieved joints)→xyz+euler_xyz. episode_id 컬럼으로 keep-ranges 키를 프레임워크 패치 없이 맞춤. 재개 가능(marker)·샤딩·finalize.

데이터 품질 게이트: trace 3개(cfgon_FruitsMovingOrangeOrLimeTask_run0_env1, cfgon_FruitsOnPlateTask_run0_env1, cfgoff_FruitsMovingOrangeOrLimeTask_run0_env3)에 NaN 수천 개 → scipy SVD/lstsq가 SVD did not converge로 죽던 원인. 인덱스에 skip_reason 플래그(번호 유지), 모든 소비자가 제외. 재수집 파이프라인에도 같은 게이트 필요.

2.2 환경 scripts/cosmos3_env.sh

fd_replay 프리앰블 공용화 + torchcodec shim: PyAV가 ffmpeg 8 libs를 mangled 이름으로 번들해 torchcodec이 libavutil.so.60을 못 찾음 → .venv/ffmpeg_shim/ 심볼릭 링크. lerobot decode_video_frames(torchcodec 기본)가 학습 로더에서 쓰이므로 필수.

2.3 분할 · keep-ranges

세트ep성공/실패프레임태스크
test-A (seen task / unseen ep)5211/4158,62026
test-B (unseen task)25945/214353,71915
val7910/69115,81454
train1,513195/1,3182,130,583105

test-B: BlockStackingOrderAgnostic, BlocksInBin, BowlStackingLeftOnRight, ButterAboveRaisin, CondimentsInBin, DishesInBin, ElectronicsInBin, FruitsMovingOrangeOrLime, PickUpBluePitcher, PutTwoMugsOnShelf, RubiksCubeBehindBowl, Stack3RubiksCube, StackYellowOnRed, ThrowAwaySnacks, WhiteMugInCenterOfTable. 첫 시도에서 코호트 태스크를 제외하니 쌓기가 0개라 규칙 수정(코호트 태스크 허용, 해당 코호트 ep는 test-A→test-B). 라벨은 fd_replay_modes.gather()를 8 run dir 전체(976 ep)에 돌려 확보.

keep-ranges: 공식 use_filter_dict로 split + 사건 가중 동시 구현. 사건 = 첫 close 이후 gripper 전이 + contact detector, ±2 s. 실패 train 1,317 중 1,285 ep에 사건, 중앙값 18/ep(grip 28,606 / contact 4,843), 사건 윈도우 886,089 = 44%.

2.4 학습 config

신규 experiment action_fd_robolab_{mix,succ}_nano (action_policy_droid_nano deepcopy + 델타; config.py import 1줄) factory get_action_fd_sft_dataset: mode=forward_dynamics · ee_pose 10D quantile · chunk 16 · 480 concat_view · format_prompt_as_json=True · idle_frames/additional_view_description 제거 · augment="bc" keys_to_skip_loading=[net_ema.] (action head 로드) · keys_to_select 6그룹(llm2action 제외) · lr 5e-5 LambdaCosine(warmup 200, f_min 0.1) encode_exact_durations=[17] · loss_scale=10 (TOML 스키마 금지 → python) · run_validation=False (validation_step가 pass) datasets (mix): succ ratio 2 / fail_event 1 / fail_uniform 1 ← RankPartitionedDataLoader는 rank 단위 배정 → 4 rank 양자화 TOML: shard 4 · max_samples_per_batch 16 · grad_accum 2 (global 128) · max_iter 8000 = cycle · save_iter 1000 제출: sbm "bash scripts/train_fd_robolab.sh mix" --qos=own --gres=gpu:4 -c 48 --mem 300GB --time=30:00:00 --container=nvcr.io/nvidia/cuda:12.8.0-devel-ubuntu22.04

2.5 정합성 테스트 scripts/test_wm_dataset_parity.py

검사기준실데이터 8샘플
action (dataset 경로 vs fd_replay_prep FK 경로, quantile 후)max|Δ| < 1e-37.7e-5 – 1.6e-4
video (concat_view → 736×544 pad, aug off)mean|Δ| < 4/2552.1 – 3.5 /255
prompt (학습 JSON vs inference.action._format_prompt)문자열 동일8/8 동일

3 스모크 결과와 수정

스모크결과해석
87812 (own 4 GPU, 4-ep smoke 데이터, 30 iter)iter 1–24 진행(loss 1.0–1.6), 40 s/iter(3회 중 1회만 4.5 s), GPU util ~10%, GPU당 55–63 GB; iter 25에서 hang → 30분 후 NCCL watchdog(ALLGATHER timeout) → SIGABRT데이터로더 병목 + worker 데드락
원인 1 (확정, 벤치): 공식 ColorJitter(b/c/s/hue)의 hue(RGB↔HSV)가 51프레임 float 텐서에서 1스레드 5.1 s (no-hue 0.94 s, crop+resize 0.26 s). worker당 ~6 s/샘플 × batch 8 = 48 s, 6 worker → 1 sample/s/rank → 32 samples/step ≈ 32 s + 연산 4.5 s ≈ 40 s — 관측과 일치. 디코드는 17프레임 0.07 s로 무관.
원인 2 (유력): 메인 프로세스 OMP_NUM_THREADS=4로 OpenMP 풀을 만든 뒤 worker fork → GNU OpenMP 비 fork-safe → worker 데드락 → 그 rank가 collective에 못 들어감.
수정: augment="bc"(crop+resize+brightness/contrast): 단일 프로세스 1.40 s/샘플, fork 8 worker 8.96 samples/s → 10 worker면 32 samples/step ≈ 3 s < 연산 4.5 s → GPU-bound 예상 ~5 s/iter. OMP_NUM_THREADS=MKL=OPENBLAS=1, worker 10, -c 48. spawn은 벤치에서 10분+ 멈춰(대용량 pickling) 불채택.

스모크 2차(87867, extra, 실데이터)는 클러스터 119/120으로 대기 중. 본 run 87870(own)은 사용자 own 쿼터(mot_v1b 4 + ev_mid10k 2 + longmem_real 1 GPU, mem 1.43/1.8 TB)가 비면 자동 시작 — 사용자 결정 "own 대기".

4 Takeaway

의미

인터페이스 동일성을 테스트로 고정했으므로 base vs FT의 차이는 데이터에만 귀속된다. 쿡북과 vendored 프레임워크 사이의 불일치(format_prompt_as_json 미전달, TOML 금지 키)와 로더 함정(hue aug, fork/OMP, rank 양자화 혼합비, torchcodec shim)은 전부 우리 쪽에서 흡수했고 memory/context에 기록했다. 데이터 품질 게이트(NaN trace)는 재수집 때도 필요하다.

5 Next Steps

즉시

  • 스모크 2차 s/iter 확인(예상 ~5 s) · FT-mix 87870 시작 감시(첫 iteration 정상 여부) · ckpt 용량 확인 후 save_iter 조정
  • scripts/export_fd_ckpt.sh로 스모크 ckpt export → fd_replay 2 ep 경로 검증
  • FT-succ는 own 4 GPU가 추가로 비면 제출(동일 iter)

병행(Phase 2 선행)

  • 물체 pose 로깅 훅 = RoboLab/robolab/eval/episode.py:run_episodeenv.step() 루프(157행)
  • E2 counterfactual 스크립트 · VLM judge 초안

상세: claude/260820/progress-wm_ft_phase0.md · 체크리스트 claude/plan.md