← Back
2026-04-27 · experiment

Exp — RoboMME-trained MemER LoRA on BinFill ep0 (VLM-only inference)

TL;DR. Sister repo (~/repos/Robotics/memer/) 에서 공식 Anthropic MemER dusting

배경/목적 (왜)

Sister repo (~/repos/Robotics/memer/) 에서 공식 Anthropic MemER dusting 체크포인트를 RoboMME 16개 task ep0 에 돌렸을 때 결과가 전부 OOD — "place the red block on the top shelf" 처럼 dusting kitchen vocabulary 를 RoboMME 시각 입력에 강제 매핑함. 즉 dusting LoRA 의 학습 분포가 RoboMME 와 무관하기 때문에 해석 불가능한 출력만 나옴.

이 실험은 RoboMME 자체에 학습된 MemER LoRA (Yinpei/vlm_subgoal_predictor / memer/grounded_subgoal/checkpoint-1300) 를 RoboMME LeRobot v2 에 inference-only 로 돌려서 도메인-매칭된 출력을 얻는 게 목표. VLA 는 안 붙임 — VLM 이 어떤 subtask 와 keyframe 을 출력하는지만 본다. 첫 단계로 BinFill ep0 (550 frames, "put two red cubes into the bin, then press the button to stop") 한 episode 에서 검증.

작업 내용 (어떻게)

Plan: plan-evaluate_memer_lora_on_robomme.md.

Path B 결정 (Phase 0)

이 repo (examples/robomme/subgoal_prediction/qwenvl/api_memer.py) 의 prompt 와 sister repo (memer_eval/contract.py) 의 prompt 가 4축에서 다름 (상세):

  1. 카메라: 이 repo = 단일 front-view (front_rgb ≡ LeRobot exterior_image_1_left). Sister repo = wrist + ext1 vertical stack.
  2. <image> 레이아웃: 이 repo = inline [<image>, <image>, ...], sister = 한 줄에 한 개.
  3. Output schema: 이 repo = grounded JSON with <|box_start|>(x,y)<|box_end|> 토큰, sister = plain text.
  4. Engine: 이 repo = ms-swift PtEngine, sister = HF transformers 직접.

LoRA 는 ms-swift 템플릿으로 학습됐으므로 sister repo 의 MemERDeploymentPolicy 를 재사용하면 학습 분포 밖. → Path B (이 repo 의 Qwen3VLModelMemER 그대로 사용, 출력만 sister renderer schema 로 변환).

환경 셋업 (Phase 1)

기존 /home/nas_main/taewoongkang/conda_envs/envs/memer/ (sister repo 의 dusting 동작 환경) 에 추가 설치:

LoRA 다운로드: hf download Yinpei/vlm_subgoal_predictor --include "memer/grounded_subgoal/*" → 1.08 GB zip → unzip → 경로 .../robomme-memer-lora-checkpoint-1300/checkpoint-1300/ (zip 안에 한 단계 더 들어감). adapter_config 확인: base = Qwen/Qwen3-VL-4B-Instruct, r=16 / α=32, language-model 선형 레이어만 LoRA, attn_impl=sdpa, max_length=3200, bf16.

Base model: hf download Qwen/Qwen3-VL-4B-Instruct (8.3 GB) 로 미리 받아둠. 안 그러면 ms-swift 가 ModelScope 로 fallback 해서 ~230 kB/s 로 받느라 90 분 timeout 도달. USE_HF=1 환경변수도 필요 (ms-swift 가 HF cache 만 보게).

Driver 작성 (Phase 2)

scripts/eval_robomme_memer_lora.py 신설. 핵심 로직:

api_memer.py 보조 수정 3곳

  1. 라인 3 from h5py._hl.dataset import sel 제거 (sel 미사용 dead import, h5py 미설치 환경에서 import 실패).
  2. 라인 30 attn_impl='flash_attention_2''sdpa' (학습 스크립트 scripts/finetune_vlm_subgoal_predictor.sh 와 일치, flash_attn 미설치 회피).
  3. merge_key_frame_paths() 시작에 if not nums: return 가드 추가. 첫 호출에서 모델이 keyframe_positions=[] 출력 → key_frame_paths 비어있는 채로 merge 들어가서 nums[0] IndexError 나던 latent bug. sim eval 사이클에서 안 터졌던 이유는 sim 이 더 큰 buffer 로 첫 call 부터 keyframe 후보를 출력하게 유도해서 그랬을 듯.

실행

USE_HF=1 CUDA_VISIBLE_DEVICES=1 timeout 5400 \
    /home/nas_main/taewoongkang/conda_envs/envs/memer/bin/python \
    scripts/eval_robomme_memer_lora.py \
    --lerobot-path /home/nas_main/taewoongkang/Dataset/robomme/BinFill_lerobot \
    --episode-index 0 \
    --adapter-path /home/nas_main/taewoongkang/ckpts/robomme-memer-lora-checkpoint-1300/checkpoint-1300 \
    --output-dir eval_outputs/robomme_BinFill_ep0

GPU 0 은 다른 사용자가 점유 중이라 GPU 1 선택. timeout 5400 (90 min login GPU 캡, 실제로는 178 s 만 사용).

Visualization (Phase 3)

Sister repo 의 scripts/render_robomme_subtask_video.py 에 우리 jsonl 을 그대로 입력. Renderer 의 default camera_keys (wrist + ext1 stack) 는 시각적 컨텍스트 용도일 뿐 — 모델은 ext1 만 봤지만 panel 에 wrist 도 같이 보여주는 건 사람이 보기 편하니까 default 유지. Output: eval_outputs/robomme_BinFill_ep0/visualization.mp4 (550 frames @ 10 fps, 1100×840, H.264 yuv420p, 2.3 MB). 6 memory thumbnail 자동 캐시.

결과 (수치)

실행 통계

Subtask vocabulary 비교 (ep0 110 raw frames 기준)

metric Dusting (OOD, sister repo) RoboMME-MemER LoRA (this repo)
rows 110 108
unique subtasks 8 14 (대부분 좌표 variant)
도메인 매칭 ❌ "shelf"/"block" ✅ "cube"/"bin"/"button"
grounded <x,y> 좌표 없음 있음 (모든 row)
top-1 subtask "place the red block on the top shelf" 60× "put it into the bin at \<118, 174>" 32×

Dusting 은 RoboMME 와 무관한 kitchen vocabulary 를 강제 매핑함: - 60× "place the red block on the top shelf" - 18× "place the red block on the bottom shelf" - 14× "press the top button" / 10× "press the button" - 3× "take red block off bottom shelf" / 1× "press the top-right button" → RoboMME 의 BinFill (cube → bin → button) 과 의미상 무관한 출력. 사람 이 봐도 어떻게 매핑했는지 알 수 없음.

RoboMME-MemER LoRA 는 BinFill task 의 정확한 단계를 grounded coordinate 와 함께 예측: - 12× "pick up the first red cube at \<104, 115>" - 6× "pick up the second red cube at \<58, 161>" - 32× "put it into the bin at \<118, 174>" - 46× "press the button at \<58, 92~94>" (좌표 ±1 노이즈) - 14 unique subtasks 중 의미상 다른 건 4종 (pick first / pick second / put / press), 나머지는 grounded coord ±1-2 픽셀 노이즈.

Memory FIFO 동작

최종 memory_indices_after = [87, 105, 130, 174, 349, 430] — 6 개의 key frame 이 episode 전반에 걸쳐 분산. cluster merge dist=8 가 작동해서 연속된 후보들을 median 으로 압축. dusting 은 final memory size 를 안 재봤지만 sister repo 의 renderer 가 dusting BinFill 에 6 thumbnail 을 출력했던 것을 보면 비슷한 수준.

의미 (Takeaway)

  1. Path B 결정이 옳았음. ms-swift 템플릿으로 학습된 LoRA 를 sister repo 의 HF-기반 inference path 에 얹지 않은 것이 핵심. 만약 Path A 로 갔다면 prompt 의 4축 차이 (카메라 수, image layout, output schema, engine) 때문에 또 OOD 같은 출력이 나왔을 것.
  2. RoboMME-MemER LoRA 는 dusting 대비 정량/정성적으로 압도적. subtask vocabulary, grounded coords, semantic 단계 전이 모두 BinFill task 와 일치. VLM 단독으로도 "지금 무엇을 해야 하는지" 사람이 볼 때 해석 가능한 출력.
  3. 이 repo 의 sim eval path 에 latent bug 3개 발견 (h5py dead import, hardcoded flash_attention_2 mismatch with training, empty merge IndexError). 모두 1-3 줄 수정. sim eval 을 실제로 돌리면 적어도 #2 는 무조건 터짐 (flash_attn 미설치 환경에서). 이 수정들은 sim eval path 에도 영향. 추후 PR / fork 로 upstream 검토 가치 있음.
  4. Login GPU 90 분 캡 안에서 16-task 전체 sweep 가능 (~47 분). sbatch 안 써도 됨. 근데 동시에 다른 사람이 GPU 0 점유하는 상황이 있어서 항상 nvidia-smi 후 비어있는 GPU 골라야 함.

보완점/다음 (Next)

  1. 16-task ep0 sweepscripts/eval_robomme_memer_lora_all_tasks.py 를 sister repo 의 eval_robomme_all_tasks.py 미러로 작성, 47 min 안에 완료. visualization 16 개 + eval_outputs/all_tasks_ep0_index.md 생성.
  2. VideoPlace / VideoUnmask 등 video demo prefix 가 있는 task 검증. 현재 driver 는 start_new_episode(video_query=None, ...) 로 video prefix 를 끄고 있는데, RoboMME 의 일부 task (TASK_WITH_VIDEO_DEMO in examples/robomme/utils.py) 는 demo video 가 학습 분포에 들어가 있을 수 있음. 16-task sweep 시 task 별로 처리 분기 필요.
  3. api_memer.py 의 3개 fix 에 대해 upstream 에 PR 보내거나, 이 repo fork 에서만 유지할지 결정. 사용자가 RoboMME challenge 참여 시 sim eval 에도 영향 미침.
  4. Comparison report (Phase 5) — 16-task sweep 끝나면 claude/260427/analysis-dusting_vs_robomme_memer.md 에 task 별 metric (parse rate, unique subtask count, vocabulary overlap with ground truth instruction) 정리.