Index
2026-05-10 — Progress

ApiMem frames_per_call — 멀티-프레임 컨텍스트 구현

memer / robomme_policy_learning | commit fdbe034

TL;DR

4
파일 수정
+92
Lines Added
1
default (legacy)

1 배경 / 목적

기존 ApiMem get_subgoal은 매 K-tick마다 현재 프레임 1장만 VLM에 넘겼다. BinFill 같이 이미 수행된 동작(arm이 무언가를 집었다, bin 가까이 이동했다)이 다음 subgoal 결정에 중요한 태스크에서, 단일 정적 프레임만으로는 "이번 tick까지 뭘 했는지"를 VLM이 볼 방법이 없었다.

기존 한계: 단일 프레임 context → VLM이 motion history 없이 subgoal 추론. 특히 연속 동작이 필요한 task (BinFill, UnmaskSwap 류)에서 과거 상태를 자연어 mt에만 의존해야 했음.

이 변경은 tool-calling 설계(260510)에서 ApiMemModel.get_subgoal(frames)frame list를 넘기는 인터페이스가 필요해진 것과도 맞물려 진행됨. 즉 frames_per_call 자체로도 유용하고, 이후 tool-calling 구조의 전제 조건이기도 함.

2 작업 내용

_subsample_frames() — 핵심 로직

subgoal_predictor.py에 추가된 함수. frame_buffer[last_tick_buf_idx + 1:] 구간에서 균등 간격(even-spaced)으로 N장을 추출한다. 마지막 프레임이 항상 current frame = frames[-1]이 되도록 보장.

_subsample_frames(frame_buffer, last_tick_buf_idx, frames_per_call=1): slice = frame_buffer[last_tick_buf_idx + 1:] if len(slice) == 0: → [latest_image] fallback if frames_per_call == 1: → [slice[-1]] (legacy path) else: → np.linspace 인덱스로 N개 균등 추출, slice[-1] 항상 포함

ApiMemModel.get_subgoal(frames) 인터페이스 변경

기존: get_subgoal(image: np.ndarray) → 현재: get_subgoal(frames: list[np.ndarray] | np.ndarray). 단일 ndarray 입력도 [image]로 자동 wrap → 하위 호환 유지. Detection은 frames[-1] (최신 프레임)에만 수행 — 비용 절감.

eval.py Args + shell script

# eval.py:Args api_mem_frames_per_call: int = 1 # default=1 → legacy behavior # scripts/run_api_mem_one_episode.sh — 6번째 인자 추가 FRAMES_PER_CALL="${6:-1}" # python 호출에 --args.api-mem-frames-per-call $FRAMES_PER_CALL 추가

HybridApiMemLoRACoordPredictor 동일 적용

subgoal_predictor.py::HybridApiMemLoRACoordPredictor.get_subgoal도 같은 multi-frame 플럼빙 추가. LoRA coord swap 로직은 frames[-1] 기반으로 그대로 작동.

3 결과

구현 단계 — SR 측정 실험은 아직 미실행. 기존 runs (8-run BinFill, hybrid 9951)은 모두 frames_per_call=1로 돌아가므로 수치 변화 없음.

변경 항목BeforeAfterNotes
get_subgoal 시그니처image: np.ndarrayframes: list | ndarray하위 호환 유지
detection 대상current frameframes[-1] only비용 불변
image attach (VLM)1장N장 (frames_per_call)N=1 = 기존 동작
HybridPredictor미지원동일 적용coord swap 무영향
인터페이스 설계: frames_per_call=1(default)에서 기존 8-run, 9951 모든 결과 재현 가능. 신규 실험만 N>1 사용.
비용 증가: frames_per_call=N이면 VLM 입력 이미지 N배. flash-lite의 경우 image token 비용이 N배 증가함. N=3~4 추천.

4 Takeaway

의미

ApiMem VLM이 드디어 "이번 tick까지 arm이 어떻게 움직였는지"를 직접 볼 수 있게 됐다. 기존 text-only mt가 담기 어려운 공간적 변화(arm이 container 쪽으로 이동 중, gripper가 열렸다 닫혔다)를 시각적으로 전달 가능.

또한 이 변경은 tool-calling 설계에서 ApiMemModel.get_subgoal(frames, full_frame_buffer=None) 시그니처가 필요한 전제 조건이기도 하다 — tool-calling P2 구현 시 이 인터페이스를 그대로 확장.

5 Next Steps

미측정 — frames_per_call=N ablation 필요

N=1(baseline) vs N=3 vs N=5 on BinFill (50 ep each) 로 실제 SR 변화 측정 필요. VideoUnmaskSwap tool-calling sweep (P4) 시 frames_per_call=3 병행 제출 권장.

tool-calling P2 인터페이스 확장

get_subgoal(frames, full_frame_buffer=None)로 확장 예정. full_frame_buffertrack_objects tool이 keyframe-to-now 슬라이싱에 사용. 현재 frames_per_call 구현이 이 확장의 기반.