← Back
2026-04-24 · progress

MemER 공개 체크포인트로 RoboMME BinFill ep0 inference 돌리기

TL;DR. - MemER 공식 레포는 "high-level VLM" 만 공개됨. VLA/시뮬레이터 없이도 VLM이 VLA에 넘길 subtask 문자열과 선택된 keyframe 인덱스만 뽑아보는 건 가능.

배경/목적 (왜)

작업 내용 (어떻게)

1. 전용 conda env 구축

/home/nas_main/taewoongkang/conda_envs/envs/memer/ (python 3.11). 설치 실측 버전 (2026-04-24):

torch              2.11.0+cu130     # accelerate가 transitive로 pull
torchvision        0.26.0+cu130     # qwen_vl_utils가 import
transformers       5.6.2            # pyproject 요구 >=4.57
qwen-vl-utils      0.0.14
pyarrow            24.0.0
opencv-python-headless 4.13.0
numpy              2.2.6 / Pillow 12.2 / tqdm 4.67.3
huggingface_hub    1.12.0           # `huggingface-cli` deprecated, `hf` 쓰세요

lerobot 일부러 설치 안 함: v2.0 포맷이라 LeRobotDatasetMetadata 쓸 수 없고, 어차피 MemERDeploymentPolicy는 lerobot을 top-level로 import 안 함. pip install -e memer/ --no-depsmemer_eval 패키지 import 경로 확보.

2. 어댑터 스크립트 작성

scripts/eval_robomme_subtask.py: - meta/episodes.jsonl에서 에피소드 task 문자열 추출 - 각 카메라의 videos/chunk-XXX/<key>/episode_YYYYYY.mp4를 cv2로 디코드 (RGB uint8) - MemERDeploymentPolicy(camera_keys=[wrist_image_left, exterior_image_1_left]) 생성 - 매 stride 프레임마다 policy.step(*chunk) 호출 (buffer는 다 쌓이고 inference는 chunk의 마지막 프레임에서만) - predictions.jsonl + run_info.json 덤프

카메라 키 이름 주의: RoboMME는 observation.images.wrist_image_left / exterior_image_1_left (MemER 기본의 wrist_left / exterior_1_left 가 아님).

3. 체크포인트 다운로드

HF: ajaysri/memer-dusting-qwen3vl-4b-step-1500
Local: /home/nas_main/taewoongkang/ckpts/memer-dusting-qwen3vl-4b-step-1500/
Total: 24G (model safetensors ~8.5G + optimizer/scheduler/rng_state 나머지)
Arch: Qwen3VLForConditionalGeneration, bfloat16, 36 layers, 2560 hidden

4. 실행

B200 login node, GPU 1 (비어있음). Attention은 sdpa override — flash-attn 미설치 상태라 auto-select는 flash_attention_2 찍어서 ImportError. sdpa가 기본 fallback으로 적합.

CUDA_VISIBLE_DEVICES=1 timeout 1800 \
/home/nas_main/taewoongkang/conda_envs/envs/memer/bin/python \
scripts/eval_robomme_subtask.py \
    --lerobot-path ~/Dataset/robomme/BinFill_lerobot \
    --episode-index 0 \
    --model-path ~/ckpts/memer-dusting-qwen3vl-4b-step-1500 \
    --output-dir eval_outputs/robomme_binfill_ep0 \
    --stride 5 --attn-implementation sdpa --save-raw-text

결과 (수치)

BinFill ep0 (550 frames, 10fps, stride=5 → 110 inference calls)

Subtask 분포 (n=110)

Count Predicted subtask
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
2 pick up red block
2 reset gripper
1 press the top-right button

Timestep 진행 vs 예측 subtask (에피소드 phase별)

프레임 구간 실제 로봇 행동 (mp4 기준) 모델 예측 주요 primitive
0 – 15 초기 배치/접근 take red block off bottom shelf (x3)
19 – 254 첫 빨간 큐브 bin으로 이동 + 두 번째 큐브 place the red block on the bottom shelf (x18), pick up red block (x2)
69 – 549 (도배) (전반적 ambient fallback) place the red block on the top shelf (x60)
219 – 534 버튼 press 구간 press the top button (x14), press the button (x10), reset gripper (x2)

Keyframe 선택 이벤트 (n=12)

ts= 19  → picked frame 4
ts= 34  → picked frame 4
ts= 69  → picked frame 34
ts=114  → picked frame 79
ts=139  → picked frame 124
ts=149  → picked frame 149
ts=154  → picked frame 144
ts=159  → picked frame 144
ts=169  → picked frame 144
ts=174  → picked frame 139
ts=179  → picked frame 164
ts=299  → picked frame 264

메모리 최종 상태 (1D clustering merge_distance=5 subsampled = 25 raw frames 적용 후):

Final mem: [4, 34, 79, 144, 264]
All candidates seen: [4, 4, 34, 79, 124, 139, 144, 144, 144, 149, 164, 264]

→ 인접 keyframe들 {139, 144, 149}가 144로 클러스터 병합, {4, 4}가 4로 중복 제거 → 최종 5개.

의미 (Takeaway)

  1. Pipeline 전체 검증 완료: env → parquet/mp4 decoding → MemER Deployment Policy 스텝 호출 → JSON parse → memory FIFO 전부 이슈 없이 동작. 110 call 중 parse fail 0건.
  2. VLM→VLA 브리지 단일 string 채널 재확인: predicted_subtask가 VLA에 넘어갈 전부. embedding/hidden state 공유 없음. 스크립트의 predictions.jsonl 한 줄이 곧 VLA 한 스텝의 입력 명세.
  3. OOD 관찰 — dusting 체크포인트의 semantic transfer: BinFill은 학습 도메인 밖 (oxe_droid single_arm, bin/button task) 인데도 모델이 - episode의 시간적 phase를 구분함: pick → place → press → 말미에 다시 place fallback - press the top button 류가 버튼 press 구간(ts=219-534) 에만 집중적으로 나타남 — 실제 BinFill 스크립트 후반과 일치 - subtask vocabulary는 학습 분포에 강하게 박혀있음: "shelf" 라는 단어가 BinFill에 없는데도 "top shelf / bottom shelf"가 80% 이상의 step에서 나옴 → dusting의 공간 primitive 재사용
  4. Keyframe selection 작동: 초반 pickup 단계에서 frame 4가 반복적으로 선택되고, mid-episode에서 frame 79/144/264가 선택됨. 클러스터 병합으로 인접 후보가 compact하게 5개 유지됨. Experience Retrieval 메커니즘 정상.

보완점/다음 (Next)

  1. Fine-tuning 없이 RoboMME 정량 평가 불가: subtask GT 자체가 없고 vocabulary도 dusting vs BinFill 사이 공집합 → accuracy 계산하려면 - RoboMME에 subtask 라벨을 직접 달아야 하거나 - Qwen3-VL-4B-Instruct base 모델로 zero-shot (dusting-tuned 대신) - 또는 RoboMME에서 직접 finetune (scripts/generate_sft_data.py로 SFT data 만들고 QwenLM/Qwen3-VL repo로 학습, README.md §9 참조)

  2. 다른 RoboMME task 실험: BinFill은 pick+place+press 세 phase 혼재. 더 clean한 single-primitive task (PickXtimes_lerobot, StopCube_lerobot)에서는 subtask sequence가 더 간결할 가능성. 어댑터 이미 준비돼있으니 --lerobot-path만 바꿔 바로 비교 가능.

  3. Keyframe prompt 영향도 실험: --memory-length, --merge-distance, --recent-frames-length 변화시 VLM 출력 일관성이 어떻게 바뀌는지. 지금 default (8/5/8) 고정 값.

  4. VLA 붙이기: openpi(README §5 추천)로 Franka/DROID VLA 로드 → predicted_subtask 을 language instruction으로 주입 → closed-loop. 이건 별도 프로젝트 규모.

  5. Flash-attn 설치 여부: 현재 sdpa 0.78s/call (B200 1 GPU). flash_attention_2 설치하면 ~2-3x 가속 예상. /etc/claude-code/CLAUDE.md §11 Path A로 /usr/local/cuda-13.0 export 후 flash-attn 빌드. 현재는 불필요 수준의 속도.

산출물

경로 내용
scripts/eval_robomme_subtask.py RoboMME v2 → MemER deploy 어댑터 CLI
claude/RUNNING_ON_ROBOMME.md 경로/명령어 캐노니컬 레퍼런스 (다른 세션/사람이 바로 따라갈 수 있음)
eval_outputs/robomme_binfill_ep0/predictions.jsonl 110 스텝 prediction 원본
eval_outputs/robomme_binfill_ep0/run_info.json config/duration/camera/platform 메타
eval_outputs/robomme_binfill_ep0_smoke/ stride=100 smoke test 결과 (ablation용 보관)