MoNa-π — 전체 연구 여정

SigLIP + PaliGemma + Flow Matching 기반 연속 제어 모바일 VLA — 초기 설계부터 오늘 ablation까지

244
H5 에피소드
100%
CL SR (regular 9-path)
75.5%
CL SR (full, 최고=Gemma Expert)
0.0414
best val_loss
CHAPTER 1

초기 설계 — π0를 모바일 로봇으로

2026-03-17, "MoNa-pi"(Mobile Navigation Pi-zero)로 명명. Physical Intelligence의 π0 아키텍처를 3-DOF 옴니휠 홀로노믹 모바일 로봇에 이식하는 것이 핵심 목표. MoNaVLA(이산 분류 기반)의 한계를 출발점으로 명시했다.

MoNaVLA의 한계로 진단된 3가지 (설계안 원문)
1. 데이터 암기(Timing Memorization) — offline PM 100%인데 실주행에서 시각 정보보다 "진행 시간(step)"을 암기해 행동하는 경향
2. 제어 빈도(2~5Hz) — VLM 연산량 때문에 반응이 느리고 Bang-bang 제어로 움직임이 부자연스러움
3. 이산적 행동 공간(8~9-class) — 매끄러운 곡선 주행·미세조정 불가능
설계 목표 3가지
1. Flow Matching (연속 제어) — 이산 분류 대신 연속 속도 벡터 (v_x, v_y, ω_z) 생성
2. Action Chunking (50Hz+) — 한 번의 추론으로 미래 N스텝(10~50) 액션을 한꺼번에 예측
3. Causal Visual Understanding — 시각적 변조 데이터로 타이밍 암기 방지

근거: docs/MoNa-AI_Project_Proposal.md, docs/MoNa-pi_Design_Proposal_v1.md, docs/domain_analysis_pi0_vs_mona.md


CHAPTER 2

End-to-End 파이프라인 구현

2026-04-15, 첫 전체 파이프라인 구현(shosae 커밋). 데이터 전처리부터 학습 config까지 한 번에 갖춰졌고, 같은 날 GitHub Pages 첫 배포도 진행됐다.

data/preprocessing.py·dataset.py — MoNaVLA 패턴 계승 + 신규
ActionSmoother (Savitzky-Golay 필터) — 키보드 수집의 bang-bang 스파이크 제거
IntentPrefixInjector — 9-class + 한/영 다국어 + 20% 노이즈 instruction
CounterfactualInjector — stop/steer 오버라이드 (MoNaVLA 계승)
HFlip 증강 — angular_z 부호 반전 + instruction 좌/우 교체 동시 처리
v5/v6 HDF5 포맷 자동 감지, CLIP 정규화 상수 적용

CHAPTER 3

PaliGemma + Action Expert 전환 — "진짜 π0"

같은 날(2026-04-15) 저녁, 백본을 PaliGemma + Action Expert로 전면 교체. 임시 구조에서 π0 원논문에 가까운 구조로 갈아탄 분기점.

PaliGemmaBackbone 구조
SigLIP → Linear projector(1152→2048) → Gemma encoder. PerceiverResampler 완전 제거(PaliGemma 자체 방식 채택).
load_pretrained_paligemma=False: 로컬 SigLIP+Gemma 캐시 사용 / True: google/paligemma-3b-pt-224 다운로드.
출력: (B, T_vis+L, 2048) — VLM 전체 토큰 시퀀스를 Action Expert에 cross-attention 조건으로 전달.
이 시점부터 약 6주간(4/15~5/27) 공백 — 다른 서버에서 아키텍처 작업이 이어진 기간으로 추정(monapi-train 분기 운영 체계 확립).

CHAPTER 4

AdaLN-Zero — 진짜 π0 modulation

2026-05-27, Flow Head를 π0 원논문의 AdaLN-Zero 방식으로 전면 재작성. 13개 파일, +501/-113줄 변경.

Before → After
이전(단순 덧셈): h = action_proj(x_t) + t_emb — 시간 임베딩을 그냥 더함, self-attn은 query만 norm
이후(AdaLN-Zero):
(α1,β1,γ1,α2,β2,γ2) = AdaLNModulation(cond_emb)
h = h + γ1 * self_attn(norm1(h)*(1+α1)+β1)
h = h + cross_attn(norm2(h), vlm_cond)
h = h + γ2 * mlp(norm3(h)*(1+α2)+β2)
Zero-init로 학습 초기 모든 modulation이 identity → 안정적 수렴 시작.

신규 모듈: models/heads/mona_action_expert.py (정규화 래퍼), scripts/verify_mona_expert.py (수치 정합성 검증).


CHAPTER 5

BF16/dtype 버그수정 시리즈

2026-05-28 하루 동안, AdaLN-Zero 전환 이후 드러난 dtype/API 불일치를 연속으로 수정. transformers 버전업과 BF16 전환이 맞물린 전형적인 인프라 정리 구간.

수정된 버그 목록
1. FP16 → BF16 — PaliGemma 백본에서 FP16 사용 시 NaN loss 발생, BF16으로 해결
2. dtype 통일 — Pi0VLA와 PaliGemmaBackbone 간 dtype 불일치 제거
3. pg.model.vision_tower — transformers 5.x에서 PaliGemma attribute 경로가 바뀐 것 대응
4. language_model 정규화 — 두 로드 경로(bin/safetensors) 모두 내부 GemmaModel로 통일
같은 날 INT8 양자화, 평가 스크립트 재작성, 카메라/ROS2 노드 추가, Serbot2 INT8 추론까지 한 번에 진행 — 실로봇 배포를 향한 집중 작업일.

CHAPTER 6

INT8 양자화 — 온보드 추론

Serbot2(로봇 온보드) 메모리 제약 때문에 PaliGemma 3B를 BF16(~6GB)에서 INT8(~3GB)로 양자화. MoNaVLA의 monavla-driving 접근법을 그대로 참고.

구현
BitsAndBytes BitsAndBytesConfig 적용, 로드 후 explicit gc.collect() 추가.
Serbot2 온보드에서 INT8 추론을 안정적으로 돌리려면 8GB 스왑 파일이 필수(DEPLOY.md 1절).

CHAPTER 7

add_free 244ep 통합 — MoNaVLA 병목을 직접 해소

2026-06-01, V5(150ep) + add_free(221ep)를 병합해 244개 고유 에피소드로 from-scratch 재학습. 이 커밋이 특히 중요한 이유는 MoNaVLA에서 보고된 "center_straight 0% 병목"을 직접 타겟으로 해소하려 한 흔적이 커밋 메시지에 명시돼 있다는 점이다.

변경 사항
mobile_vla_dataset_merged = v5(150) + add_free(221) = 244 unique ep
center_straight: 21 episodes — 커밋 메시지: "MoNaVLA 병목 해소"
lr: 5e-5 → 1e-4, epochs: 20 → 30, pretrain.ckpt_path: "" (처음부터 재학습 — 266 샘플에 70-epoch 학습으로 과적합되는 것을 방지)
이 시점에 두 프로젝트가 데이터 레벨에서 이미 한 번 교차했다 — MoNaVLA의 진단(center_straight 병목)이 MoNa-pi의 데이터 수집 우선순위에 반영된 사례.

CHAPTER 8

Gemma Expert · LoRA · lerobot 백본 — 최종 비교실험

2026-06-01~02, 4가지 변형(base PaliGemma / lerobot 사전학습 백본 / Gemma Action Expert / LoRA)을 동일 조건(val 38 regular / 53 full)으로 비교. 이 시점까지의 아키텍처 탐색을 마무리하는 종합 실험.

closed-loop 비교 (FPE<0.3m 기준)
구성regular(38)full(53)FPE
base PaliGemma100%73.6%0.386m
lerobot 사전학습 백본100%73.6%0.391m
Gemma Action Expert100%75.5%0.394m ← 전체 최고
LoRA97.4%75.5%0.373m
핵심 발견
1. regular 9-path는 전부 100% — 쉬운 경로는 어떤 백본을 써도 차이가 안 남
2. free/irregular 시나리오가 진짜 병목(13% 수준 — full 53에 포함된 어려운 케이스)
3. lerobot의 매니퓰레이션 사전학습은 모바일 내비게이션에 이득이 없음 — base와 거의 동일
4. "백본 선택"보다 "action expert 품질"이 더 큰 factor — Gemma Expert가 백본 교체 없이도 가장 큰 개선

CHAPTER 9

6/23 Ablation — ODE steps · 증강 효과 · 데이터량

2026-06-23, 6/2에 작성됐지만 한 번도 실행되지 않았던 scripts/run_ablation.sh(M6→M5→M7)를 처음 실행. 중간에 브랜치 전환 실수로 M5 eval이 한 번 끊겼다가(eval_closedloop.pymonapi-driving엔 없는 파일이라 working tree에서 사라짐) monapi-train에 고정한 채 재실행해 완료했다.

M6 — ODE steps 3/5/10 (재학습 없음)
3/5/10 전부 100% SR, FPE 0.0697~0.0707m로 거의 동일 → ODE steps를 3으로 줄여도 품질 손해 없음, 추론 속도 단축 여지 확보.
M5 — 증강 없이 244ep 재학습 vs baseline
구성full(53)FPE
baseline(증강 있음)73.6%0.3906m
M5(증강 없음)73.6%0.3868m
증강 유무가 측정 가능한 차이를 만들지 않음 — 두 구성이 거의 동일.
9-2. 14건 실패 정체 추적(정정) — "center/straight 약점"이 아니라 free_* OOD 프로브 전체
episode_idx를 실제 h5 파일명으로 역매핑한 결과, 처음 추정(center/straight 카테고리)과 전혀 다른 그림이었다.
카테고리구성실패
정형 9종(center/left/right×straight/left/right)38개0개(0%)
free_center_*(극단오프셋/대각선/원거리/조명)5개4개(robot_far만 성공)
free_left_*5개5개(100%)
free_right_*5개5개(100%)
baseline·M5(no-aug) 둘 다 동일하게 free 14/15 실패, 정형 0/38 실패. CH7의 add_free 244ep 통합(center_straight 21ep 추가)이 정형 경로 문제는 실제로 해소한 것으로 보이고, 남은 문제는 augmentation이 아니라 학습 분포 밖(OOD) 일반화 한계.
9-3. M8 — OOD 흉내 증강 시도 → 완전한 null 결과
data/ood_augment.py 신규 구현(robot_close/far zoom, basket_extreme 좌우 shift, 윈도우 8프레임 동일 적용)으로 재학습 후 동일 full(53) eval.
구성SRFPE
baseline73.6%0.3906m
M5(no-aug)73.6%0.3868m
M8(ood-aug)73.6%0.3944m
실패한 14개 에피소드가 baseline/M5와 정확히 1:1 동일(성공한 것도 free_center_robot_far 단 1개로 동일) — 단 1건도 안 바뀜. 원인 추정: 이 증강은 이미지를 변형하지만 액션 레이블은 원본 그대로라, 실제 OOD 시나리오의 image→action 대응 관계를 못 가르침 — 시각적 invariance만 학습되고 새로운 매핑은 못 배움. 이미지 레벨 합성 증강으로는 이 문제를 못 풀고, 실제 free_* 데이터 추가 수집이 유일하게 남은 검증된 다음 단계.
M7 — 116ep 원본 v5만 재학습
regular(24)=full(24)=100% — 그러나 이 데이터셋 자체에 hard 카테고리 평가 에피소드가 거의 없어서, "작은 데이터셋이 더 낫다"는 결론으로 이어질 수 없음. M5(n=53)와 직접 비교는 apples-to-apples가 아님 — 후속 작업으로 남김.
9-4. M7 진짜 apples-to-apples 비교 (6/24 추가) — 116ep와 244ep는 거의 동일
M7 체크포인트(checkpoints/ep116/best)를 M5/M8과 동일한 eval 모집단(244ep val split, full n=53)에 직접 통과시켜 진짜 비교를 완성했다.
구성SR(동일 n=53)FPE
baseline(244ep)73.6%0.3906m
M5(244ep, no-aug)73.6%0.3868m
M8(244ep, ood-aug)73.6%0.3944m
M7(116ep)71.7%0.3790m
71.7% vs 73.6% — 53개 중 1개 에피소드 차이, 노이즈 수준. add_free로 데이터를 116→244ep(2배)로 늘려도 전체 SR은 거의 안 바뀜. CH9-2(정형 9종 0% 실패)와 CH9-3(M8 합성 증강 null)을 함께 보면, add_free 통합이 실제로 고친 건 정형 경로뿐이고 free_* OOD 일반화는 데이터 양을 늘리는 것만으로는 개선되지 않는다는 그림으로 수렴한다. 다음 단계는 양이 아니라 free_*류를 직접 타겟한 데이터 수집.
9-5. M9 — train/val 누출 버그 발견 + 수정 (6/24 추가)
"알고리즘적 미스 audit" 중 data/dataset.pybuild_train_val_split()을 점검하다 발견. 기존 구현은 윈도우(샘플) 생성 후 random_split()으로 train/val을 나눴는데, window_size=8 슬라이딩 윈도우는 인접 윈도우끼리 7/8프레임이 겹친다 — 같은 에피소드의 윈도우가 train/val 양쪽으로 갈리는 누출이 발생.

실측(244ep, seed=42): val로 분류된 53개 에피소드 중 49개(92.5%)가 train에도 자기 자신의 다른 윈도우를 갖고 있었음. MoNaVLA의 nav_h5_dataset_impl.py는 처음부터 에피소드(파일) 단위로 나눈 뒤 윈도우를 생성하는 좋은 패턴을 갖고 있었는데 MoNa-pi엔 포팅이 안 돼 있었다.

수정: 파일 단위 분할로 재작성(교집합 0개 확인) + free_* 비율이 전체의 ~8%뿐이라 단순 shuffle로는 val에 0개 뽑히는 경우가 실제 발생(stratify 끄고 확인) — stratify_free=True 옵션으로 free_*/정형을 따로 비율 분할 후 합치도록 보강.
카테고리(수정 후, n=17)SR
정형 9종(15개)100%
free_*(2개)0%
누출을 제거한 진짜 held-out 평가에서도 같은 패턴(정형 100% / free_* 0%)이 재현됨 — CH9-2/9-4의 결론이 누출과 무관하게 견고함을 재확인. 다만 이 수정으로 위 9-1~9-4의 절대 SR 수치는 더 이상 재현되지 않는다(내부 split이 바뀌어 val 모집단 자체가 달라짐) — 그 비교들은 "당시 비교 사이의 상대적 결론"으로만 읽을 것.
9-6. M10 — free_* 고정 holdout 분리 (장기 로드맵 Phase 1, 6/24 추가)
9-5에서 남긴 후속 검토(stratified val의 free_* 표본이 n=2뿐이라 통계적으로 약함)를 바로 처리. build_train_val_split()stratify_freeexclude_free_holdout(기본 True)으로 교체해 free_* 21개를 train/val 분할에서 완전히 제외하고, 신규 build_free_holdout()으로 매 실험마다 동일한 21개 전체를 평가하도록 고정했다. eval_closedloop.py--free-only 플래그 추가.

근거: 9-4(M7, 데이터 2배)와 9-3(M8, 합성 OOD 증강) 둘 다 free_* SR을 못 바꿨다 — free_*를 학습에 일부 끼워 넣는 게 이미 효과 없다고 확인된 상태에서, stratify로 val에 어떤 1~2개가 뽑히는지가 흔들리면 앞으로의 어떤 개선(실데이터 수집, grounding 신호 주입)도 "효과가 있었는지" 판단할 기준이 안정적이지 않다. 학습 신호로서는 버려도 되는 손실 대비, 고정 모집단으로 얻는 측정 안정성이 이후 모든 비교의 전제조건.

검증: train 202 / val 22 (둘 다 free_* 0개), free holdout 21개·368샘플(train+val과 파일 교집합 0), train.py DataLoader sanity 정상. 장기 로드맵(Phase 2: 실데이터 수집, Phase 3: grounding 신호 파일럿)은 plan.md 참고.
9-7. Phase 3 파일럿 — gray basket bbox grounding 3-way 비교 (6/25 추가)
MoNaVLA 교훈(end-to-end는 closed-loop 0%, bbox+image decomposition은 66.7%) 이식 파일럿. MoNa-pi엔 bbox 라벨이 없어 PaliGemma 자체 generate() detect로 "gray basket" 오프라인 추출(244 에피소드, 4609프레임 중 valid 43.9%) 후 3조건(image_only/bbox_only/bbox+image concat) 동일 조건(15 epoch, 정형 9종 train 202/val 22)으로 학습, --free-only(n=20, 새 고정 holdout)·--regular-only(n=15)로 평가.
조건학습시간regular SRfree SR
image_only(baseline)28분100%15.0%(3/20)
bbox_only4.5분(백본 스킵)100%20.0%(4/20)
bbox+image(concat)27분0%0%
가장 중요한 발견(예상 밖): 이번이 free_* SR을 n=20(9-6 이전 M9는 n=2)으로 처음 측정한 결과다 — image_only 기준으로도 0%가 아니라 15%. M9의 "free_* 0%"는 표본 2개의 통계적 한계였을 뿐, 보편적 사실이 아니었다는 게 드러남. 9-6(Phase 1 고정 holdout)이 의도한 대로 더 신뢰 가능한 측정을 만들어냄.

bbox_only가 image_only보다 약간 우위(20% vs 15%, 1개 에피소드 차이 — 이 n에서는 노이즈 수준)면서 PaliGemma 3B 백본 호출을 생략해 6배 빠름. bbox+image는 val_loss가 세 조건 중 가장 낮았는데(0.0625) SR은 0% — FPE는 양호(0.16m, threshold 0.5m 이내)한데 TLD가 0.45~0.47로 일관되게 붕괴(방향은 맞지만 속도가 expert의 절반). 학습 안 된 bbox 토큰을 VLM cond에 단순 concat하면 cross-attn의 attention mass가 희석돼 액션 크기가 줄어드는 것으로 추정 — 그라운딩 신호 자체의 무용함이 아니라 naive concat 설계의 결함. loss만 보고 판단하면 함정(가장 낮은 loss가 가장 나쁜 SR)이라는 교훈도 남음.

전체 결과: docs/ABLATION_RESULTS_20260623.md


CHAPTER 10

MoNaVLA 연동 분석 — 같은 클래스 버그, 다른 운명

같은 날, MoNaVLA 세션에서 발견한 파이프라인 교훈들을 MoNa-pi 코드에 직접 대조 검증. 가정으로 옮기지 않고 실제 코드를 읽어 검증된 것만 적용했다.

적용함 — train/inference 리사이즈 보간 불일치
학습(data/dataset.py): resize(..., PILImage.BILINEAR) 명시
추론(inference/engine.py): resize(...) 보간법 미지정 → Pillow 기본값(BICUBIC)
monapi-driving·monapi-train 양 브랜치 모두 BILINEAR로 통일, 커밋·푸시 완료.
점검 완료, 문제 없음 — BGR/RGB · text attention
BGR/RGB: camera_node.pycollectorcontroller 전체 경로가 일관되게 RGB(rgb8)로 통일돼 있음.
text attention: 기존 측정 로그(logs/D8_attention_v2.log)에서 PaliGemma 백본 text attention이 forward/left/right 3방향 모두 72.6~74.4%로 정상 — MoNaVLA의 Google-robot post-train Kosmos-2 0% 붕괴와 대조적으로 MoNa-pi는 건강한 상태.
평행 현상 가설 — 정정됨 (center/straight 약점 → free_* OOD 문제)
DEVSTATE.md(5/28 기준) 당시엔 center_left 0%/center_right 33%/*_straight 25%였고, MoNaVLA의 center_straight 0% 병목과 평행하다는 가설을 세웠다. 그러나 CH9-2(6/23 재진단)에서 정형 9종 경로는 현재 100% 성공으로 확인됨 — CH7의 add_free 통합(center_straight 21ep 추가)이 그 문제를 실제로 해소한 것으로 보임. 현재 남은 약점은 카테고리가 아니라 free_* OOD 프로브 전체(→ CH9-2). MoNaVLA와의 평행 비교는 "약한 카테고리"가 아니라 "분포 밖 일반화" 레벨에서 다시 봐야 함.

전체 분석: docs/MONAVLA_CROSSCHECK_20260623.md