Index
2026-05-18 — Analysis

Pi0.5-Delta IDM — observation.state 2-timestep 버그 발견·수정·검증

GR00T-Dreams | pi05_delta 경로 버그 근본원인 확정 + statefix 검증 + 공정 재실험 착수

TL;DR

3
무효 결과
~5×
기존 오차 비율
1줄
Fix 코드
≈4933
statefix SR 검증
4
공정 재실험

1 배경 / 목적

Pi0.5-Delta IDM은 두 프레임(x_t, x_{t+16})의 픽셀 차분을 입력으로 pseudo-action을 labeling하는 접근법이다. v1(state-off) / v2(state-on) / normfix 세 단계를 거쳤으나 EEF mm가 각각 562 / 540 / 396 mm로 SigLIP2 baseline 107mm 대비 3.7~5.2×에 머물렀고, SR은 모든 run에서 ≈0%였다.

반복된 이상 신호: loss는 수렴하나 closed-loop SR=0이 지속. normfix(−27%) 적용 후에도 396mm. 사용자는 일관되게 "코드 잘못/improper experiment일 확률 높다"고 지적했으나, "NaN 없으면 버그 없음", "SR=0은 forward-IDM이라 원래 그런 것"으로 안심했던 판단이 반복적으로 틀렸다.

이번 세션의 목적은 체계적인 통제 실험으로 "delta 가설 기각"이 정당한지 확인하는 것이었다. 조사 방향: ① chunk=16 호환성, ② in-train eval 버그, ③ pi05_delta 코드 경로 자체.

2 작업 내용

Step 1 — chunk=16 confound 가설 수립 및 검증 (4933)

B1(first mode, 단일 프레임)은 사실상 plain pi05와 동일 입력인데도 부진. wandb config diff에서 working plain pi05와 B1의 차이를 추출했더니 3개뿐이었다: chunk_size(16/50), n_action_steps(16/25), policy.type(pi05_delta/pi05). lr·optimizer·wd·batch 등 모든 HP가 동일. launch_pi05_b0_ref.sh:7 주석에 "chunk=16→50 pi05_base head 호환"이 명시되어 있어 chunk 교란 가능성을 검증해야 했다.

통제 실험 설계 (4933): recipe: launch_pi05_b0_ref.sh (working SR recipe) 변경: CHUNK=16 NACT=16 EXP_TAG=_chunk16 ← 이것만 변경 나머지: policy=pi05 (plain), pi05_base pretrained, mg30_real_h50, closed-loop eval 결과 판정: 3-8K step SR이 working(dpjjrqle) 수준으로 상승하면 → chunk OK, pi05_delta 경로 문제 flat-0이면 → chunk 교란이 근본 원인

Step 2 — 5001: B1-first pi05_delta vs 4933 동일 척도 비교

Fix-2(first→closed-loop eval 라우팅)를 적용한 B1(5001)을 resume @8000 후 4933과 동일 척도(closed-loop SR)로 비교. 유일한 차이 = policy.type=pi05_delta.

Step 3 — 근본 원인 추적 및 확정

4933 정상(SR 상승) + 5001 flat-0 → pi05_delta 코드 경로 단일 용의자로 좁혀진 뒤 소스를 탐색했다.

// lerobot/datasets/factory.py:62 resolve_delta_timestamps( delta_timestamps, # observation_delta_indices = [0, 16] features, ) → key.startswith("observation.")에 ALL 매칭 → observation.images.wrist ✓ (의도) → observation.state ✓ (의도치 않음 — 버그) 결과: dataset이 observation.state를 (2, 16) 2-timestep으로 반환 monkey-patch 3종은 observation.images.* 만 collapse state는 안 건드림 ↓ 학습: observation.state = (2, 16) malformed 2-step eval: observation.state = (16,) 1-step (build_delta_batch / plain pi05) → train/eval 불일치 → loss 수렴하나 closed-loop 전멸
영향 범위: state-on 트랙 전부 오염 — v2(state-on), normfix(state-on), B1(state-on), B2(state-on). v1은 state-off라 무관하나 v1도 562mm로 부진 → (A)normfix 축 버그는 별개. state-bug는 v2 이후 트랙을 특히 오염.

Step 4 — Fix 구현 (train_bc.py)

// resfit/lerobot/scripts/train_bc.py (make_dataset/aux 직후 ~line 462) if cfg.policy.type == "pi05_delta": for key in ["observation.state"]: if key in dataset.delta_indices: dataset.delta_indices.pop(key) dataset.delta_timestamps.pop(key, None) print("[pi05_delta] dropped observation.state from delta_indices") # 결과: state가 plain pi05와 동일하게 단일 현재프레임으로 로드 # 이미지는 2-step 유지 (의도된 동작), eval 무수정 → train/eval 일치

Step 5 — 검증 + statefix 재실험 (5031)

CPU 스모크: state shape (2,16)→(16,), image (2,3,128,128) 유지 PASS. 5031(first_sf) launch 후 wandb 로그에서 "dropped observation.state from delta_indices" 마커 확인. closed-loop SR 추적으로 4933과 비교.

B1/B2 입력 mode 구현 (부산물)

버그 탐색 과정에서 delta_mode 3종(delta/first/2frame) 구현도 완료했다 — 모델 코드 0 변경, config field + monkey-patch 라우팅만.

파일변경 내용
configuration_pi05_delta.pydelta_mode field + __post_init__ (2frame 시 empty_cameras=3 자동)
modeling_pi05_delta.py_preprocess_images_first_only, _preprocess_images_two_frame 추가
convert_to_delta_input_policy.py_MODE_TO_FN 라우팅 (config.delta_mode로 train/eval 자동 일관)
train_bc.pySTATEFIX + Fix-2 per-mode eval 라우팅 (_delta_offline 분기)
idm_intrain_eval.py신규 — mode-correct offline in-train eval (delta/2frame)
launch_pi05_delta_idm.shDELTA_MODE / GRAD_CKPT / EXP_TAG env hook 추가

3 결과

조사 타임라인 — 가설 순차 소거

가설 A — normfix
396mm(−27%). (A)정규화 버그 실재 확인. 그러나 잔여 396mm = baseline 3.7× → 원가설 기각 유지했었음 (결국 오진).
가설 B — chunk=16 교란 (4933)
plain pi05, chunk=16 통제 실험. SR 1K=0.05, 2K=0.60, 3K=0.65 — working 수준. chunk=16 교란 기각.
확정 — pi05_delta 경로 버그 (5001 vs 4933)
5001 (B1-first pi05_delta, 동일 척도): post-resume 9K~13K SR = 0/0/0/0.05/0.05 flat. 4933(동일 recipe, plain pi05)는 2K=0.60. 유일 차이 policy.typepi05_delta 코드 경로 버그 확정.
근본원인 확정 — factory.py state malform
observation.state shape (2, 16) 실증 확인. monkey-patch 3종 미처리 → train/eval 완전 불일치. v1/v2/normfix 562/540/396mm 전부 무효 선언.
수정 검증 — statefix (5031)
5031 first_sf closed-loop SR: 0→0.300.800.650.750.650.95 ≈ 4933 band. statefix 작동 확정.

핵심 대조 — 4933 vs 5001 SR (동일 closed-loop 척도)

실험configstepSR판정
4933plain pi05, chunk=16, working recipe2K→5K0.60→0.80chunk OK, 기준선
5001pi05_delta first mode, Fix-2 fixed eval9K→13K0/0/0/0.05/0.05pi05_delta 경로 버그 확정
4933과 5001의 유일한 차이 = policy.type. SR 0.60 vs 0.00. pi05_delta 코드 경로 버그가 원인이며 normfix/chunk/eval-harness는 모두 무관.

이전 결과 무효 선언 요약

실험EEF mmstate-on?버그 영향유효성
v1 (state-off)562.1Nostate 축 무관. 단 normfix(A) 버그 포함❌ 무효 (normfix 미적용)
v2 (state-on)539.9Yesstate (2,16) malform 완전 오염❌ 무효
normfix (state-on)395.9Yesstate malform 오염 + normfix partial fix❌ 무효
SigLIP2 baseline (Latent-IDM A)107별개 아키텍처, 무관✅ 유효

statefix 검증 — 5031 vs 4933 SR-vs-step

Step5031 first_sf (statefix)4933 plain pi05 (baseline)
~2K0.300.60
~4K0.800.75
~6K0.650.80
~10K0.75
~17K0.95
statefix 작동 확정: 5031 SR이 4933 band(0.65–0.95)에 진입. 구버전 버그-run(flat-0)과 완전히 다른 패턴. "pi05_delta first ≈ plain pi05" 예측이 검증됨.

공정 재실험 상태 (statefix 후)

Job계정실험목적신호
5114minhoparkdelta_sf (★핵심)버그 없는 delta-IDM 첫 신뢰 가능한 eef_mmeval/eef_mm 수렴값
5115minhopark2frame_sf대체 입력 scheme (자연 이미지 2개)eval/eef_mm
5031taewoongkangfirst_sfstatefix 검증용 (✅ 완료)closed-loop SR ≈ 4933
4933taewoongkangplain pi05 chunk=16통제 baselineclosed-loop SR
판정 기준 (5114 delta_sf): eef_mm ~100–250mm 수렴 → delta-IDM 실제로 쓸만함, 원 기각은 오진이었음. ~400–600mm plateau → delta-입력 본질적 한계 (이번엔 공정하게). 결과: 260518-pi05_delta_closed_loop_eval.html 참조.

4 Takeaway

사용자 직관이 정확히 적중했다

사용자는 반복해서 "코드 잘못/improper experiment일 확률 높다"를 주장했다. "값이 NaN 아니면 버그 없음", "SR=0은 forward-IDM이라 원래 그런 것", "cold라 0"이라는 안이한 설명이 반복적으로 틀렸다. 진실은 체계적인 통제 실험(4933, 5001)과 소스 확인(factory.py)으로만 도달했다.

버그의 교훈 — 광범위한 prefix match는 위험하다

key.startswith("observation.")는 delta_indices를 image-only로 제한하려는 의도였으나 state까지 매칭. 이 1-줄 조건이 수주간의 실험 결과를 전부 무효화했다. state처럼 "당연히 delta를 안 쓰겠지"라는 암묵적 가정에 대해 명시적 예외처리가 필요했다. fix(pop state from delta_indices)는 1줄이지만 그 영향은 결정적이었다.

조사 방법론 — 어떻게 찾아야 했나

효과적이었던 방법: (1) config diff — working vs broken run의 정확한 차이 3개만 추출, (2) 통제 실험 — 가설을 하나씩 소거(chunk→eval라우팅→pi05_delta 경로), (3) 실증 검증 — 주장이 아니라 shape 출력으로 확인. "NaN 없음 = 버그 없음"은 잘못된 기준 — malformed input이라도 loss는 수렴할 수 있다.

5 Next Steps

5114 delta_sf eef_mm 수렴 판정 (최우선)

statefix + 공정 input(pixel delta) 조건에서 처음으로 신뢰 가능한 eef_mm 측정이 이루어진다. wandb run pi05_delta_idm_4gpu_state_on_sf_acc2, Monitor bxlsvyh9i. 20K 수렴값으로 SigLIP2 107mm 대비 판정 — 결과는 closed-loop eval 카드에 기록됨.

5115 2frame_sf (대체 입력)

delta-입력 대신 x_t와 x_{t+16}을 각각 별도 image token으로 넣는 scheme. B200 OOM 이력(4823→grad_ckpt fix) 있으므로 OOM 시 BATCH=8 fallback. delta_sf 결과 대비 "자연 이미지 2개 vs 차분" 비교 가능.

재발 방지

lerobot/pi05_base 로드 시 embed_tokens.weight missing 경고는 Gemma tied-embedding 양성 artifact — pretrained 미로드 아님. 알람 금지.
② checkpoint rotation race: 학습 중 잡의 특정 step ckpt를 eval에 pin하면 rotation으로 소멸 위험. 완료된 잡의 최종 ckpt 사용.
③ in-train eval: delta/2frame은 offline-replay eval, first/plain pi05는 closed-loop eval. 혼용 금지.