← Index
2026-08-21 — Analysis

Open/Close Zero-shot — 벤치마크가 측정한 것은 무엇인가

MolmoBot | 학습 데이터 0인 스킬의 rollout, 그리고 58%의 정체

TL;DR

4.0%
Open, mix@10k (2/50)
6.5%
Open, part1@15k (13/200)
58.0%
Close, mix@10k (29/50)
z=7.2
Close−Open 비대칭 유의도

1결과

벤치모델에피소드성공률비고
Openmix@10k504.00% (2/50)공식 벤치 stride-4 서브샘플
Openpart1@15k2006.50% (13/200)공식 벤치 전량
Closemix@10k5058.00% (29/50)수정본 벤치 서브샘플
Closepart1@15k5048.00% (24/50)수정본 벤치 서브샘플
비교Δz판정
mix: Close vs Open+54.0%p+7.19압도적 비대칭
part1: Close vs Open+41.5%p+5.70압도적 비대칭
Close에서 mix vs part1+10.0%p+1.01차이 없음

두 모델 모두 open/close 학습 데이터가 0이다. 그런데 두 모델이 독립적으로 같은 비대칭을 재현했고, Close에서는 서로 구분되지 않는다(z=1.01). 후자가 특히 중요하다 — 이 벤치가 모델의 무엇도 변별하지 못한다는 직접 증거다.

258%의 정체 — 에피소드 h5 직접 검증

학습 데이터가 없는 태스크에서 58%가 나오면 먼저 의심해야 한다. 50개 에피소드의 리워드 궤적을 직접 열었다.

가짜 성공은 아니다. 전 에피소드가 정확히 리워드 0.500(=50% 열림)에서 시작하고 시작부터 성공 상태인 에피소드는 0/50이다. 성공하려면 50% → 15% 이하까지 실제로 닫아야 한다. 관절이 처음 움직이는 시점은 평균 97 step(전체 607의 16% 지점), 성공 도달은 평균 194 step — 초기화 직후 자연 낙하 같은 아티팩트가 아니다(5 step 이내 움직임은 32개 중 1개).
그러나 의도적 닫기로 보기도 어렵다. 관절 이동량 중 여는 방향 비율이 27.5%(중앙값 26%)로, 한 방향으로 미는 의도적 동작(0%)과 무작위 흔들림(50%)의 중간이다. 19/50 에피소드는 시작보다 더 열린 순간도 있었고, 실패한 18개 중 10개도 관절을 조금씩 움직였다.
결정적 논거는 Open 4%다. 모델이 지시문("Close the ...")을 이해해서 닫는 것이라면 Open도 어느 정도는 나왔어야 한다. 4%에 그쳤다는 것은 언어 이해가 아니라 동작 구조가 원인임을 말한다 — 닫기는 밀기만으로 성립하고, pick 정책이 물체를 향해 팔을 뻗는 동작 자체가 밀기를 만든다. 반면 열기는 손잡이를 잡고 당겨야 해서 pick/place 데이터로는 전이되지 않는다.

3그래서 이 수치는 무엇인가

FrankaCloseHardBench 계열은 정책 비교용 변별력이 약하다. 접근만 해도 절반 이상 성공하므로, 향후 close 성능을 논할 때 이 벤치 수치를 그대로 인용하면 과대평가가 된다.
FrankaOpenHardBench는 반대로 학습 없이는 바닥(4~6.5%)이다. open 데이터 확보의 가치를 정량적으로 뒷받침하는 숫자이자, 앞으로 open 학습의 baseline이 된다.

미완의 대조군: no-op / random 정책 베이스라인. 현재 eval 코드에 무동작 정책이 없어 (policies/에 pi/stateful/websocket뿐) 즉시 실행이 불가능했다. Open(4%)이 사실상 대리 대조군 역할을 했지만 정식 베이스라인은 아니며, 이걸 돌리면 "접근=밀기" 가설이 확정된다.

4부수 발견 — 이 벤치는 원래 돌아간 적이 없었다

측정을 시작하자마자 전 에피소드가 task sampling에서 실패했다.

ValidationError: 1 validation error for OpenCloseTaskSpec joint_start_position Input should be a valid number [type=float_type, input_value=[0.0], input_type=list]

benchmark_schema.py는 이 필드를 float으로 선언하는데 배포 벤치는 200/200 전부 1-원소 list로 저장하고, 정작 소비처 json_eval_task_sampler.py:390은 list를 기대해 [0]로 인덱싱한다. 검증 spec이 task_cls 기준으로 정해져 Open/Close 양쪽이 동일하게 막혔다. 즉 이 릴리스에서 Franka Open/Close 벤치는 한 에피소드도 실행된 적이 없다.

복구를 위해 venv 패치 4건을 적용했다: task_cls 정규화, displacement assert 완화, opening_tasks.py의 task_type="open" 무조건 대입 제거(close 분기 되살림), OpenCloseTaskSpec에 1-원소 list 언랩 validator 추가. 추가로 Close 벤치는 지시문 200개가 전부 "Open the ..."이어서 task_type="close" 주입 + 지시문 교정한 파생본을 만들었다 — 따라서 close 수치는 공식 벤치와 직접 비교 불가하다.

5인프라 교훈 — preempt의 3종 위장

클러스터 포화로 4시간 잡이 반복 preempt되며, preempt가 세 가지 다른 얼굴로 나타남을 확인했다.

위장겉보기실제
① 조용한 소멸로그 파일 없음setup 중 evict
② 정상 요약Success count: 0, Total count: 0 / Success rate: 0.00%SIGTERM 종료 핸들러 출력
③ 진짜 버그처럼 보이는 트레이스백AssertionError: No episodes found from eval0 에피소드 상태로 collect_info 진입
②를 완주로 오인하면 close zero-shot이 0.00%라는 가짜 수치가 리포트에 실릴 뻔했다. 자동화 판정은 ① sacct State를 먼저 확인하고 ② Total count >= 목표 에피소드 수일 때만 완주 인정해야 한다.

또한 eval에는 중간 저장이 없어 preempt 시 전량 손실이다(실제로 82 house / 2시간 12분 손실). 포화 시에는 200ep 대신 stride-4 서브샘플 50ep로 잡을 1시간대로 줄이는 것이 실제 수치를 얻는 길이었다. QOS는 share보다 extra가 안정적이다 — share는 own·extra 양쪽에 밀린다.