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.
이 repo (examples/robomme/subgoal_prediction/qwenvl/api_memer.py) 의
prompt 와 sister repo (memer_eval/contract.py) 의 prompt 가 4축에서
다름 (상세):
front_rgb ≡ LeRobot
exterior_image_1_left). Sister repo = wrist + ext1 vertical stack.<image> 레이아웃: 이 repo = inline [<image>, <image>, ...],
sister = 한 줄에 한 개.<|box_start|>(x,y)<|box_end|> 토큰, sister = plain text.PtEngine, sister = HF transformers
직접.LoRA 는 ms-swift 템플릿으로 학습됐으므로 sister repo 의
MemERDeploymentPolicy 를 재사용하면 학습 분포 밖. → Path B (이 repo
의 Qwen3VLModelMemER 그대로 사용, 출력만 sister renderer schema 로
변환).
기존 /home/nas_main/taewoongkang/conda_envs/envs/memer/ (sister repo
의 dusting 동작 환경) 에 추가 설치:
ms-swift==3.12.6 (4.1.3 은 swift.llm 모듈 제거 + PtEngine →
TransformersEngine rename. LoRA 학습 시점 (2026-01-02) 와 가까운 3.x
마지막 메이저 버전 선택). 동반 다운그레이드: transformers 5.6.2 →
4.57.6, peft → 0.18.1.imageio, opencv-python-headless (opencv-python 은 서버에
libGL.so.1 없어서 import 실패).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 만 보게).
scripts/eval_robomme_memer_lora.py 신설. 핵심 로직:
videos/chunk-000/observation.images.exterior_image_1_left
/episode_000000.mp4 디코드 → cv2.VideoCapture 프레임 리스트.api.add_execution_frame(frame).t >= 15 (api 의 8-at-stride-2 sampling 이 IndexError 안 내려면
최소 15 frame 필요) AND (t+1) % stride == 0 (또는 마지막 프레임)
일 때 api.call() 트리거.call_and_capture 가 raw response, predicted_keyframe_positions
(1-indexed in current call's context), mapped_keyframe_indices (raw
frame index after path → step_N 추출), memory_indices_after (post-merge
key_frame_paths.keys()) 모두 추출 → sister renderer 스키마와 호환
되는 JSONL row 작성.api_memer.py 보조 수정 3곳from h5py._hl.dataset import sel 제거 (sel 미사용 dead
import, h5py 미설치 환경에서 import 실패).attn_impl='flash_attention_2' → 'sdpa' (학습 스크립트
scripts/finetune_vlm_subgoal_predictor.sh 와 일치, flash_attn 미설치
회피).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 만 사용).
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 자동 캐시.
| 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_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 을
출력했던 것을 보면 비슷한 수준.
scripts/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
생성.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 별로 처리 분기 필요.api_memer.py 의 3개 fix 에 대해 upstream 에 PR 보내거나, 이
repo fork 에서만 유지할지 결정. 사용자가 RoboMME challenge 참여 시
sim eval 에도 영향 미침.claude/260427/analysis-dusting_vs_robomme_memer.md 에 task 별
metric (parse rate, unique subtask count, vocabulary overlap with
ground truth instruction) 정리.