‹ Index
Scene-Mem Benchmark · API Track · Full Recap

측정을 고치니 결론이 두 번 뒤집혔다

RoboCasa 기반 scene-memory 벤치 · 2026-05-19 → 07-27 전체 정리
"M2(시각 keyframe)가 최고" → 측정 버그 4개 수정 → 텍스트 로그(M3v2) 0.564 최고, ours(tagged-keyframe) 0.462로 시각계 1위

TL;DR — 5줄

  1. 초기 결론 "M2 최고"는 측정 버그의 착시였다. control(메모리 0)과 row-join해보니 메모리 없는 control도 똑같이 틀렸다 → 범인은 메모리가 아니라 채점 STATE 윈도 off-by-one(행동이 이미 끝난 장면을 보여주고 "다음 행동은?"이라 물음).
  2. next-subtask 모드는 애초에 메모리를 재지 않는다. GOAL 텍스트에 정답이 verbatim 들어있어(recipe leak) 현재 화면만 봐도 답이 나온다 = perception task. 윈도를 고쳐도 control = M3, M2 < control(시각 메모리는 오히려 방해).
  3. 유효한 메모리 측정은 recall 모드뿐 — 단 (a) 회상 전용 프롬프트, (b) fine-grained 누적 walk 둘 다 고쳐야 비로소 작동했다. 각각 action_acc 0→0.67, place_acc 0.00→0.667.
  4. 최종 순위(diverse12, n=39): control 0.154 < M1 0.179 < M2 0.308 < ours 0.462 < M3v2 0.564. 우리 메서드 ours는 시각 조상 M2를 +50% 능가했고, M3v2와는 부분 상보적(ours만 맞힌 3건 존재).
  5. 현재 블로커: 벤치가 879-시나리오로 재릴리즈되며 같은 ID도 전부 다른 영상이 됐다 → 과거 수치·annotation 이식 불가. fine_subtasks.json 재생성이 선행 과제.
0.564
최고 place_acc
(M3v2 텍스트로그)
0.462
ours
(시각계 1위)
4
발견·수정한
측정 버그
2
결론이
뒤집힌 횟수
879
신 릴리즈
시나리오

1 여정 한눈에

붉은 점 = 이전 결론이 뒤집힌 지점.

260519 — 260528
v2 설계 + 첫 대규모 측정 next-subtask
통합 프롬프트 hierarchical next-subtask 정식화, 메모리 4종(control / M1 균일샘플 / M2 MemER keyframe / M3 MEM 텍스트) 구현. gemini 3세대 × S=100(n=300/cell) 측정.
결론: M2 최고 (.34/.73/.41) · M3는 강모델서 0으로 붕괴 "over-advance"
260616 — 260619
recall 모드 + m3v2 도입 → 둘 다 실패
데이터셋의 eval_spec.lang("Show me where the steak is placed.")이 진짜 최종 goal임을 발견 → recall 모드 구현. M3 over-advance 대책으로 m3v2(자기서술 대신 emit한 subtask 로그) 추가.
recall 전 셀 slot ≈ 0 · m3v2도 강모델서 .003 → 원인 미상
260623
반전 ① — 범인은 메모리가 아니라 STATE 윈도였다
control(메모리 0) vs m3를 같은 row로 join → 메모리 없는 control도 182건 똑같이 over-advance, 예측 문자열까지 80% 동일. 메모리 순기여는 +2.3%에 불과. 진짜 원인 = 채점 chunk의 마지막 프레임이 행동 완료 장면. --state-align leadup으로 윈도를 한 칸 당김.
control slot 0.000 → 0.800 (라이브 A/B, n=15) · "M2 최고"는 착시로 판명
260623
반전 ② — next-subtask 모드는 메모리를 재지 않는다
leadup 보정 후 다양12 셋 재측정: M3 = control(0.333 동일), M2 < control(0.128 ≪ 0.333, 순 −8행). 약·강모델 공통. GOAL 텍스트에 정답이 verbatim 포함(recipe leak)돼 메모리 없이 풀리는 perception task였음.
이 모드로는 결론 불가 → recall 모드 재설계가 유일한 길
260623
recall 전용 프롬프트 — 메모리 유효성 첫 직접 증거
RECALL_SYSTEM(과거 배치 보고, 다음 행동 금지)으로 교체. "Navigate to the fridge" 병리 소멸. 보존형(M1/M2/M3v2) > 무보존(control/M3=0) 확인. M3(덮어쓰기)의 장기 망각도 데이터로 확정 → M3 deprecated.
action_acc 0.00 → 0.667 · M2 place 0.444로 1위(coarse 기준)
260624
반전 ③ — 메모리 내용물이 깨져 있었다 (coarse walk)
M3v2 로그 1번 줄이 "Close the freezer door" — 배치 사건 자체가 기록 안 됨. 누적 walk가 세그먼트당 1콜(coarse) + 세그먼트 끝 윈도라 over-advance가 메모리 적재 단계에도 있었던 것. --recall-walk fine으로 fine-subtask 단위 + leadup 누적 전환.
로그 1번 줄 → "Place the bottled water in the freezer" · m3v2 place 0.00 → 0.667
260624
반전 ④ — 순위 역전: 시각 < 텍스트
fine walk로 diverse12 풀 스윕(n=39). coarse에서 1등이던 M2는 0.179에 정체, M3v2가 0.564로 압도. 이유: over-advance 오염은 텍스트 로그를 가장 크게 파괴(한 줄 틀리면 회상할 사실이 틀림)했고, 이미지는 한 칸 어긋나도 장면이 남아 덜 망가졌다 → 수정의 수혜가 텍스트에 집중.
M3v2 0.564 >> M2 0.179 · "M2 최고"는 최종적으로 폐기
260625
ours(tagged-keyframe) 설계·구현 + 파서 버그 수정
M2의 시각 grounding + M3v2의 질의가능성을 합침: walk 한 콜에서 next-subtask와 keyframe 태그를 동시 emit(fused), 회상 때 질문 object로 태그 검색. 추가로 모든 메서드에 영향을 준 파서 버그(모델이 템플릿의 in/on을 그대로 출력 → action=other로 깎임) 수정.
ours 0.462 (M2 0.308의 +50%) · 파서 수정으로 이미지 메서드 전반 상향
260727 — 현재
벤치마크 879 릴리즈 재동기화 블로커
HF 원본이 07-22에 879 시나리오 / 7,680 test로 재릴리즈. 전체 재다운로드 + 8,803/8,803 검증 완료. 그러나 ID가 공통인 623개조차 전부 다른 영상(프레임 수 623/623 불일치) → 과거 수치·annotation·시나리오 리스트 전부 이식 불가.
신 릴리즈엔 fine_subtasks.json 0개 → 재생성이 선행 블로커

2 최종 결과 — 유효 측정(recall) 기준

설정: gemini-3.5-flash · diverse12(서로 다른 12 태스크 패밀리) · n=39 · --target recall --recall-style report --recall-walk fine · 파서 수정 반영. 주 지표 = place_acc(물건이 최종적으로 어디 놓였는지 회상).

method메모리 형태place_accslot_accexact비용/스윕
control없음 (floor)0.1540.0770$6.20
M1전체 균일 8샘플 이미지0.1790.1280$11.92
M2 MemER모델 지목 keyframe 이미지 ≤80.3080.1540$10.42
ours 우리keyframe + inline 구조화 태그 + 검색0.4620.2560.077~$10
M3v2emit한 subtask append 로그 (~23줄)0.5640.2820.128$6.20
메모리는 유효하다 — 보존형(M2/ours/M3v2)이 무보존 control(0.154)을 뚜렷이 상회. 260623 이전의 "메모리 무용" 인상은 깨진 측정의 산물이었다.
ours는 시각계 1위, 텍스트엔 아직 못 미침. head-to-head: 둘 다 맞힘 15 / ours만 3 / M3v2만 7. 격차의 정체 = 커버리지(M3v2 ~23 이벤트 vs ours ≤8 keyframe). ours만 맞힌 3건(cup, bowl→microwave, bagel→toaster oven)은 M3v2 텍스트가 틀린 걸 시각으로 교정한 것 → 부분 상보적, 하이브리드 여지.

참고: 뒤집히기 전 수치들 (모두 폐기)

시점 / 설정당시 1등수치왜 폐기됐나
260528 next-subtask (chunk)M2slot .34STATE 윈도 off-by-one 착시
260623 next-subtask (leadup)control (메모리 무효)slot .333recipe leak — 모드 자체가 메모리를 안 잼
260623 recall (coarse walk)M2place .444 (n=9)누적 walk가 coarse → 메모리 내용물 파손
260624 recall (fine, 파서수정 전)M3v2place .564 (M2 3.1×)파서 버그로 이미지 메서드 과소평가 → 실제 1.8×

3 결론을 뒤집은 측정 버그 4개

이 프로젝트의 실질적 산출물은 메서드 순위보다 "무엇이 측정을 망가뜨리는가"에 가깝다.

Bug 1 · 채점 STATE 윈도
행동이 이미 끝난 장면을 보여주고 "다음 행동은?"이라 물었다

타깃 세그먼트를 fine 개수 K로 균등 분할하고 k=tfi chunk를 STATE로 제시했는데, 그 chunk의 마지막 프레임은 타깃 행동이 완료된 뒤였다. 모델은 정상적으로 그 다음 행동(tfi+1)을 답했고, 이것이 "over-advance"로 관측됐다.

지표 지문: fixture는 유지되니 place_ok는 0.59로 살아남고 action_ok만 0.00 → 정확히 "한 칸 앞서감".

진단법(핵심): 메모리가 원인이라는 가설을, 메모리가 0인 control과 row-join해서 반증했다. control도 182건 동일하게 틀렸고 예측 문자열이 80% 일치 → 메모리는 원인이 아니라 같은 STATE에서 파생된 쌍둥이 증상.

chunk (기존)
slot 0.000
action 0.000 / place 0.600
leadup (보정)
slot 0.800
action 0.800 / place 1.000
chunk vs leadup 윈도 비교
윈도 위치 비교 — span은 동일하게 유지하고 위치만 한 칸 당겨(chunk[tfi-1]) 마지막 프레임이 배치 직전이 되게 했다. 변수 격리를 위해 윈도 모양은 건드리지 않음.
Bug 2 · 모드 자체의 무효성 (recipe leak)
next-subtask 모드는 GOAL에 정답이 적혀 있었다

윈도를 고친 뒤에도 M3 = control(0.333 완전 동일), M2 < control(0.128)이 약·강모델 공통으로 나왔다. 원인은 메서드가 아니라 태스크 구조: GOAL 텍스트에 타깃 fine-subtask가 verbatim 포함돼 현재 화면만으로 답이 나온다.

즉 이 모드는 perception / instruction-following 과제였고, 메모리는 필요 없거나(control과 동률) 오히려 방해(M2는 주입된 keyframe이 시간축을 흩뜨려 net-negative)였다.

중요한 함의: 메모리 메서드를 아무리 잘 만들어도 이 모드에서는 차이가 나올 수 없다. 260528~260619의 모든 비교가 여기 해당.
Bug 3 · 회상 프롬프트
회상 질문에 "다음 행동"으로 답하고 있었다

recall 모드로 바꿔도 전 메서드 slot=0이었다. STEP_SYSTEM이 "다음 행동을 실행하라"를 강제하기 때문에, "Show me where the steak is placed."에 "Navigate to the fridge"로 답한 것.

LANG : "Show me where the steak is placed." GT : Place the steak on a shelf in the freezer OLD (STEP_SYSTEM) : "Navigate to the fridge" ← 다음 행동 NEW (RECALL_SYSTEM) : "Place the steak on the counter" ← 과거 배치 보고(형식 교정)

형식이 교정되자 action_acc 0.00 → 0.667. 그리고 이때 남은 place_acc = 0비로소 진짜 메모리 신호였다 — M3의 running-notes에 스테이크가 아예 없었다(덮어쓰기 설계의 장기 망각). next-subtask 모드에서는 결코 볼 수 없었을 신호.

Bug 4 · 메모리 적재 walk의 입도
회상할 사실 자체가 메모리에 안 들어가 있었다

Bug 1의 over-advance가 채점뿐 아니라 메모리 적재 단계에도 있었다. recall의 누적 walk가 세그먼트당 1콜(coarse) + 세그먼트 끝 윈도라, 한 세그먼트의 navigate→pick→place→close가 한 줄로 뭉개지고 그 한 줄이 over-advance했다.

M3v2 로그 1번 줄 (동일 에피소드 combo_042) coarse : "Close the freezer door" ← 배치 사건 누락 fine : "Place the bottled water in the freezer" ← 배치 기록됨
coarse walk
place 0.00
walk 11콜
fine walk
place 0.667
walk 19콜 · $0.44/ep

왜 이게 순위를 뒤집었나: over-advance 오염은 메모리의 내용인 텍스트 로그를 가장 크게 파괴한다(한 줄이 틀리면 회상할 사실이 틀림). 이미지는 프레임이 한 칸 어긋나도 장면이 남아 덜 망가진다. 그래서 fine walk의 수혜가 M3v2에 불균형하게 집중되며 시각 < 텍스트로 역전됐다.

Bug 5 (부수) · 채점 파서
정답을 오답으로 깎고 있었다

모델이 프롬프트 템플릿의 "...in/on the <place>"에서 in/on을 그대로 출력하는 경우가 있었는데, place 정규식이 이 슬래시 연결어를 인식 못 해 action='other'로 강등됐다. 정답을 정답으로 채점하도록 정규식에 in/on|on/in 추가(치팅 아님).

method수정 전수정 후
control0.0260.154
M10.0770.179
M20.1790.308
M3v20.5640.564 (무변 — 로그는 헤지 안 함)

결과적으로 이전에 보고한 "M3v2가 M2를 3.1× 압도"는 이 버그로 과장된 것이었고, 실제 격차는 1.8×였다.

4 메모리 메서드 — 무엇을 어떻게 저장하나

모든 메서드가 동일한 프롬프트·동일한 walk·동일한 채점을 쓰고, 오직 "장기 메모리를 어떻게 만들고 회상 때 무엇을 붙이나"만 다르다.

control 메모리 없음

현재 window만. 상식·현재 장면으로 얼마나 맞히는지의 바닥선. place 0.154.

M1 균일 서브샘플

전체 영상에서 균일하게 8장. 지능적 선택 없음 = naive 하한. place 0.179 (control과 큰 차 없음).

M2 MemER keyframe (arXiv 2510.20328)

모델이 매 콜에 중요 프레임을 지목 → 인덱스 누적 → d=5 단일연결 클러스터링 → 대표들 중 최근 8개 이미지를 회상 때 제시. place 0.308.

M3 MEM 텍스트 (deprecated)

매 스텝 running-notes를 통째로 다시 씀 → 초반 사건이 소실(장기 망각). place 0.000 = control과 동일. 덮어쓰기는 메모리가 아니다는 걸 데이터로 확정하고 폐기.

M3v2 subtask append 로그

모델이 실제로 emit한 sub-task 문장을 덧붙이기만 함(~23줄/에피소드). 회상 때 로그 전체를 제시. 최고 place 0.564, 최저 비용(이미지 미첨부).

ours tagged-keyframe + 검색

walk 한 콜에서 next-subtask와 keyframe 태그(objects·place·action)를 동시 emit(fused). 회상 때 질문의 object로 태그를 검색해 매칭된 태그 + 그 이미지만 제시. place 0.462.

ours 설계 요지

저장 walk 콜 = next-subtask + inline keyframe_tags 동시 emit (추가 콜 0) └ 태그 = {objects[], place, action} · 모션 윈도로 판단 → 단일 정지프레임 오라벨 교정 └ 이미지는 M2대로 cap 8(예산), 태그(텍스트)는 전 nomination 보존(공짜) 회상 질문 object로 태그 필터 → t_order 최신 top-k(=3) → 태그 텍스트 + 그 keyframe 이미지만 context └ 매칭 0건이면 전체 dump로 폴백

fused가 실제로 고친 것: 초기엔 walk 후 별도 캡션 콜을 돌렸는데, 단일 정지프레임만 보고 캡션해 steak@fridge/open처럼 오라벨이 났다. 한 콜에서 모션 윈도와 함께 판단하게 하니 steak@freezer/place로 교정되고, 태그 수도 6→16으로 늘며 콜은 39→33으로 줄었다.

M2 keyframe 메모리
M2가 실제로 보관한 것 — 모델이 지목한 프레임의 클러스터 대표 이미지들. 회상 시 모델이 이 픽셀을 다시 해석해야 한다(재인식 부담).
M1 균일 히스토리
M1이 보관한 것 — 전체에서 균일 8장. 배치 순간과 무관하게 뽑히므로 회상 근거가 되기 어렵다(place 0.179).

M2의 숨은 병목 — sparse nomination

M2 메모리가 빈약한 이유는 용량(8칸)이 아니라 모델이 애초에 거의 지목하지 않는 것이었다. 11콜 중 ~2콜에서만 지목해 8칸 중 3~5칸만 찼다. 강제 지목(--m2-force)으로 keyframe 5.3→12.6장으로 늘렸으나 place는 0.179→0.231에 그쳤다 — 강제로 고른 중앙 프레임이 배치 순간이 아니면 오히려 노이즈가 되는 양날.

forced vs non-forced M2
forced vs non-forced M2 — 메모리는 2배 이상 채워지지만(5.3→12.6) 정확도 이득은 소폭. "많이 저장"보다 "무엇을 저장하나"가 지배적임을 보여준다.

5 두 측정 모드 — 왜 하나만 유효한가

next-subtask 무효recall 유효
질문"지금 할 다음 sub-task는?""그 물건이 어디 놓였나?" (eval_spec.lang)
채점타깃 step 예측 ↔ target_fine_subtask회상 답 ↔ 최종 배치 fixture (place_acc)
메모리 필요?아니오 — GOAL에 정답 verbatim(recipe leak)예 — 과거 사건은 현재 화면에 없음
관측 결과control = M3, M2 < control (net-negative)control < M1 < M2 < ours < M3v2
필요한 수정leadup 윈도 (그래도 무효)회상 프롬프트 + fine walk (둘 다 필수)
한 줄 요약: 메모리는 필요 없을 땐 방해(next-subtask에서 M2가 net-negative), 필요할 땐 증거(recall에서 M2가 control의 2배). 모순이 아니라 태스크가 무엇을 요구하느냐의 문제였다.
over-advance 실제 장면 — 채점 시점의 STATE가 이미 배치 완료 뒤라, 모델이 "다음 행동"으로 문을 닫는 걸 답한다. 이 한 칸이 260528~260619의 모든 결론을 왜곡했다.

6 비용 — 추정하지 말고 측정할 것

초기 토큰 기반 추정이 6–8× 과소였다(추정 $38 → 실비 ~$300). 이후 게이트웨이가 응답에 반환하는 estimated_cost.amount를 콜마다 누적해 runner summary에 COST $X로 출력하도록 바꿨다 — 단가표 추측 금지, 게이트웨이 값이 ground truth.

단위실측
API 콜 1회≈ $0.0192 (prompt ~9.1k tok, 이미지가 대부분)
시나리오 1개 (3 test)≈ $0.23 (next-subtask) / $0.44 (recall fine walk)
diverse12 스윕 1메서드$6.2 (텍스트) ~ $11.9 (이미지)
4메서드 풀 스윕≈ $24.7
운영 교훈 2가지 — (1) 비싼 런은 순차로(게이트웨이 병렬 3동시에서 일시 오류로 크래시), (2) 키 한도 관리: 기존 Letsur: 키는 COST 한도 소진 → 현재 Letsur_qualcomm: 사용.

7 현재 상태 & 다음

🚧 블로커 — 879 릴리즈 이행: fine subtask annotation 재생성

벤치가 07-22에 879 시나리오 / 7,680 test로 재릴리즈됐고 전체 재다운로드·검증은 끝났다(8,803/8,803, 24GB). 문제는 ID가 공통인 623개조차 전부 다른 영상이라는 점(video_total_frames 623/623 불일치) — 구 fine_subtasks.json 858개는 구 영상에 index-align된 것이라 재사용이 불가능하다.

dataset.py는 이 파일을 fall back 없이 요구하고 신 릴리즈엔 0개다. 즉 핵심 결과의 근거인 --recall-walk fine이 신 데이터에서 동작하지 않는다. 재생성이 끝나야 DATA_ROOT를 신 경로로 전환할 수 있다.

신 릴리즈가 가져온 변화

항목구 (858)신 (879)
test 수~2,6007,680
tests[].task 필드없음restore 3,181 / find_target 2,878 / find_distractor 1,621
채점 함수단일memory_success + restore_success
fine_subtasks.json858개 (자체 생성)0개
반가운 소식: 오래 열려 있던 개선 항목 "상식으로 못 맞히는 질의"를 벤치가 find_distractor(1,621건, 영상 중 조작 대상이 아니었던 물체의 위치를 회상)로 먼저 제공해줬다.

블로커 해소 후 대기 중인 과제

8 재현 — 현재 정본 커맨드

# 키: Letsur: 는 한도 소진 → Letsur_qualcomm: 사용 KEY=$(grep '^Letsur_qualcomm:' ~/envs/api_keys.txt | cut -d: -f2- | tr -d ' ') OPENAI_API_KEY=$KEY OPENAI_BASE_URL=https://gw.letsur.ai/v1 \ python -m scene_mem_api.runner \ --model gemini-3.5-flash \ --method {control|m1|m2|m3v2|ours} \ --target recall --recall-style report --recall-walk fine \ --stage val --val-list tmp/diverse12.txt # 주요 플래그 --state-align {chunk,leadup} next-subtask 채점 윈도 (leadup = over-advance 보정) --recall-style {execute,report} 회상 출력 형식 (report = 과거 배치 보고) --recall-walk {coarse,fine} 메모리 적재 입도 (fine = fine-subtask 단위 + leadup) --m2-force / --ours-retrieval {r1,r2} --ours-topk

각 조합은 캐시 subdir와 결과 파일 suffix가 분리돼 있어 과거 결과를 덮어쓰지 않고 A/B가 가능하다. 프로젝트 규칙: try-except 금지(에러를 감싸지 말고 근본 원인 수정), 임시파일은 ./tmp/.

9 전체 산출물 인덱스

리포트 (claude_reports)

작업 로그 (repo claude/)

코드

scene_mem_api/runner.py 오케스트레이션 · walk · 채점 · CLI 플래그 전부 scene_mem_api/memory/base.py control / M1 / M2 / M3v2 / OursPolicy scene_mem_api/prompts.py STEP_SYSTEM · RECALL_SYSTEM · OURS_STEP_SYSTEM · CAPTION_SYSTEM scene_mem_api/dataset.py segment / fine / recall 타깃 해석 ← 879 블로커 지점 scene_mem_api/templates.py sub-task 파싱 · 슬롯 정규화 (in/on 버그 수정 지점) scene_mem_api/metrics.py slot / place / action / exact