Index
2026-08-03 — Plan

pi0.5 × MolmoSpaces 파인튜닝 — 구현 계획 재수립 (TRAIN_PLAN_V2)

SceneMem | molmobot-data → LeRobot → pi05_droid full finetune → bench-v2

TL;DR

183GB
B200 × 8
2
camera views
15K
target steps
4
변환 오류
36.0%
논문 레퍼런스 SR

1 배경 / 목적

TRAIN_PLAN.md(2026-08-01)에 따라 molmobot-data → LeRobot 변환까지 진행된 상태에서 이후 단계의 구현 계획이 필요했다. 다만 세 가지가 계획 작성 시점과 달라졌거나 미확인이었다.

기존 한계 ①: 계획이 "7× RTX 3090 24GB"를 전제로 FSDP 샤딩·LoRA 강등·외부 80GB GPU 확보 같은 우회 경로를 §3.3 전체에 깔아놨다. 현재는 B200 클러스터다.
기존 한계 ②: 다운로드된 molmobot 데이터가 몇 뷰인지, 학습에 몇 개를 써야 하는지 미확인. 계획서는 "pi05는 right_image 필수"라고 적었다.
기존 한계 ③: norm stats 재사용 여부와 액션 키(절대관절위치 vs delta) 미확정. 계획서 §2.3은 "사전학습 stats 재사용은 하지 않는다"였다.

2 작업 내용

openpi / molmospaces / 변환 산출물 3곳을 전수 확인했다. 추측 없이 파일·실측으로만 판단.

3 결과

3.1 GPU 환경 변화 — 계획이 대폭 단순해짐

항목구 계획 (3090 24GB)신 계획 (B200 183GB)
full FT 70GB 요구불가 → FSDP 7장 필수1장에 여유 2.5배
fsdp_devices7 (강제)1
ema_decayNone으로 꺼야 함 (−13GB)0.99 유지
LoRA스모크용 강등 논쟁완전히 불필요
batch_size56 (7의 배수 강제)256 (8의 배수)
디스크 여유3.0 TB (제약)67 TB (무제약)

3.2 변환 로직 자체는 검증 통과 PASS

액션-상태 정렬 정확: |action[i+1] − qpos[i]| 평균 0.004–0.031 rad vs |qpos[i+1] − qpos[i]| 평균 0.002–0.015 rad → 액션이 상태보다 약 2스텝 앞선 목표값. identity 매핑이 아니다.
gripper 방향 일치: 우리 데이터 평균 98.0/255 = 0.384 vs DROID state gripper 평균 0.400. 뒤집혔다면 ~0.6이 나왔어야 한다.
fps 정합: 소스 mp4 실측 15.15 Hz = eval policy_dt_ms=66.0 → 구 계획 §5의 fps 불일치 리스크 해소.

또한 joint_pos[0]은 더미(로봇 실제 자세와 무관), joint_pos[-1]{} 센티널 → 변환기의 앞뒤 trim 로직이 정확.

3.3 발견한 오류 4건 재변환 필요

#항목현재맞는 값
1exo 카메라randomized_zed2_analogue_1droid_shoulder_light_randomization
2이미지 기하624×352 → 224×224 plain squashresize_with_pad (224×126 + 상하 49px 패딩)
3카메라 수3캠 (right_image 강제)2캠 (right_wrist_0_rgb = zeros, mask False)
4저장 형식PNG dtype:"image"video dtype:"video"
기하 불일치 실측: 저장된 PNG의 상/하단 50행 평균 밝기 67.0 / 32.6 — 검은 바 없음 = squash 확정. 종횡비 1.77을 1.0으로 찌그러뜨려 학습하고, eval에서는 letterbox 이미지를 넣게 된다.
PNG 저장 폭증: 875 ep = 45 GB / 48만 파일 (소스는 5.2 GB). 본 규모(2.5만 ep) 스케일 시 ~1.3 TB / 1,370만 파일 → 공유 GPFS 메타데이터 부담. video 인코딩 시 추정 15 GB.

3.4 norm stats — DROID state만 재사용

DROID state.mean [ 0.0162 0.2672 -0.0167 -2.0256 -0.0327 2.3445 0.0827 | 0.3998] ← 절대 joint pos DROID actions.mean[-0.0047 0.0114 0.0018 0.0285 0.0013 -0.0030 0.0029 | 0.4514] ← joint VELOCITY molmobot joint_pos.mean [0.0059 -0.0433 0.0281 -2.0986 0.0048 2.0633 0.0558] ← 절대 joint pos

교차 확인: examples/droid/convert_droid_data_to_lerobot.py:143step["action"]["joint_velocity"]를 액션으로 저장한다.

함정: 우리 액션(절대 position)에 DROID actions stats를 쓰면 정규화 값이 (−2.03 − 0.0285)/0.3012 ≈ −6.8로 [−1,1]을 크게 이탈해 flow-matching 타깃이 붕괴한다.

결정: DROID state stats를 state·action 양쪽에 사용

pi0.5는 discrete_state_input=True가 기본이라 state를 정규화 후 토큰으로 이산화한다. 다른 stats를 쓰면 state 토큰 분포가 어긋나 pi05_droid 초기화의 이점이 날아간다.
state와 action을 같은 정규화 프레임에 두면 action ≈ state + 작은 delta가 정규화 공간에서 성립한다.
고정 stats라 다운로드 규모를 늘려도 정규화가 안 변해 체크포인트 간 비교가 유지된다.
안전성 근거: molmobot 실분포와 std가 사실상 일치, mean 차이도 0.5σ 이내.

3.5 액션 스키마 — 절대관절위치 확정 확정

소스 h5에 actions/joint_pos(절대)와 actions/joint_pos_rel(delta)가 둘 다 존재. 실측 joint_pos_rel[1] = [0]*7, commanded_action == joint_pos.

FrankaRobotConfig.command_mode = {"arm": "joint_position"}(robot_configs.py:160)이고 PI_Policymodel_output[:7]을 그대로 action["arm"]에 넣는다 → 절대값이어야 정합. MolmoBot 논문도 "absolute joint positions" 명시.

3.6 MolmoBot 논문에서 가져올 것

항목내용우리 적용
π0.5 finetune15K step → 평균 36.0% (zero-shot 10.0%)타깃 밴드 / 스텝 수 레퍼런스
SigLIP freezevision encoder 전체 동결 — "to prevent overfitting to simulation rendering artifacts"1차 기본안으로 채택. PathRegex(".*img.*")
warmup1k step구 계획 값 유지
from-scratch200k step / batch 1024 / lr 5e-5 → 93.5%lr은 그보다 낮게(2.5e-5). 따라잡는 게 목표가 아님

3.7 학습 시간 추정 계산 기반, 실측 아님

샘플당 ≈ 6 × 2.3e9 × 712 tokens ≈ 1.1e13 FLOPs. B200 bf16 peak ~2.25 PFLOPS, JAX/XLA 실효 MFU 30–40% 가정 → 장당 0.7–0.9 PFLOPS.

구성추정 step time15k step
1× B200, batch 32~0.5 s~2 h
8× B200, batch 256~2–5 s~8–21 h
8× B200, batch 64~0.6–1.3 s~3–5 h
진짜 병목은 dataloader일 가능성: batch 256 / 3 s/step = 85 sample/s = 170 프레임 디코드/s. video dtype이면 랜덤 접근 seek 비용이 붙는다. 파일럿에서 sjob -v로 GPU util을 확인해 데이터 바운드 여부를 판정한다.

4 Takeaway

1. GPU 제약 소멸로 계획이 정공법이 됐다. FSDP 샤딩·LoRA 우회·외부 80GB GPU 확보라는 §3.3의 분기 3개가 소멸. full FT + EMA + batch 256으로 직행.
2. 2캠으로 맞추면 eval 어댑터가 공짜다. 입력 스키마가 pi05_droid와 동일해지므로 molmospaces 내장 PI_Policy를 무수정 사용. zero-shot 베이스라인과 finetune이 같은 코드 경로를 타 비교 신뢰도도 상승. 구 계획의 MlspacesPiPolicy 신규 어댑터는 폐기.
3. "LeRobot 변환 완료"는 파일럿 수준이었다. 로직은 맞지만 카메라·기하·저장형식 4건이 틀려 재변환 필요. 더 중요하게는 받은 데이터가 val split뿐이고 bench-v2도 전부 val이라 이대로 학습하면 train/test 오염.
4. norm stats는 "재사용"이 맞되 절반만 맞다. state는 반드시 재사용, actions는 절대 불가(속도 vs 위치). 이 구분을 놓쳤으면 학습이 조용히 실패했을 지점.

5 Next — Phase 로드맵

Phase내용기간게이트
0 블로커JAX env 신규 구축 + B200 sm_100 forward 검증0.5–1일jax.devices() → B200 8장, pi0.5 forward 통과
1변환기 수정 5건 + openpi 수정 3건 → val 재변환0.5일letterbox 확인, eval 프레임과 픽셀 diff ≈ 0, 45GB → 2GB급
2norm stats 합성 (DROID state → state·actions)0.5일정규화 후 99% 분위가 [−3, 3] 이내
3학습 스모크 — 로그인 10 step + sbatch 500 step0.5일loss 단조 감소, step/s 실측으로 --time 확정
4train split 다운로드 2~3만 궤적 (~350–530 GB) + 변환1–2일house ID ∩ bench-v2 = ∅ 확증
5본 학습 15k step / batch 256 / 8× B200 / SigLIP freeze1–2일loss 수렴
6평가 — PI_Policy 무수정, 5k/10k/15k 체크포인트 비교zero-shot 대비 향상

본 학습 설정 (Phase 5)

init : pi05_droid params steps : 15,000 (MolmoBot 레퍼런스), 15k 체크포인트 필수 보존 batch : 256 (8× B200) lr : cosine, warmup 1000, peak 2.5e-5, decay 2.5e-6 ema_decay : 0.99 freeze : SigLIP (PathRegex ".*img.*") horizon : 15 ← 현재 config는 기본값 50, pi05_droid와 어긋남 save_every: 1000 (preempt 대비)

남은 미해결 항목

A. molmospaces-dataset 경로 불명 — EVAL_PLAN이 참조한 /home/nas2/junhahyung/scene-mem/molmospaces-dataset이 없다. bench-v2 JSON·씬 팩 위치가 있어야 Phase 6 가능. NAS 광역 스캔은 운영 규칙 §15상 금지 → 사용자 확인 필요.
B. EVAL_PLAN Phase 3(robocasa365 zero-shot floor) 미실행으로 보임 (results/ 부재).
C. Franka용 Open/Close 학습 데이터 부재 가능성 — DoorOpeningDataGenConfig의 robot 미확인. val 1샤드로 확인 필요.