Index
2026-08-08 — Analysis

M3의 44%는 무엇이었나 — Phase 1 종합

MetaView → WAM Backbone | 4 arm + 반사실 + 캘리브레이션 + 프로브, 전부 완결

TL;DR

0.1438
M3 wrist LPIPS
0.2560
M4 (M1=0.2579)
필요 pose 정확도
13.4×
pose : 3D내용 민감도
8%
3D 내용의 기여

1 무엇을 하려 했나

MetaView의 3D 메커니즘(카메라 조건부 attention + 멀티뷰 3D 토큰)을 비디오 생성 모델로 확장해 WAM의 backbone으로 쓰는 것. 3D consistency를 확보하고 wrist view를 더 잘 생성해서 action 예측 정확도를 높이는 것이 최종 목표다.

구조는 Wan2.2 5B(X-WAM SFT) 위에 geometry branch를 30개 블록 전부에 병렬로 붙인 것. wrist 비디오 토큰이 query가 되고, key/value는 두 그룹이다 — exo 첫 프레임의 DA3 3D 토큰(기하)과 exo t=0 VAE latent(외형). 카메라는 PRoPE로 들어간다.

DROID · 9,999 clips · 192×320 · 9 frames → latent 3 · 4 GPU · effective batch 32 · 8,000 step (25.8 epoch) query: wrist 비디오 토큰 (6×10 patch grid = 60/frame) key/value: DA3 3D 토큰 (19×32 grid, dim 6144) + exo t=0 latent LoRA r=64 · geometry branch 전체 학습 · 나머지 5B 동결
PRoPE상대 인코딩이다. q에 P_qᵀ, k에 P_k⁻¹를 걸어야 qᵀkP_q P_k⁻¹ — 즉 query 카메라 대비 key 카메라 — 를 담는다. 한쪽에만 걸면 상대항이 아예 없다. 이 한 문장이 아래 모든 결과를 관통한다.

2 네 arm — 절반짜리 메커니즘은 값어치가 0이다

네 arm 모두 8,000스텝 완주, 정합 검증점 129개. 아래는 step ≥ 1000의 112개 평균.

arm카메라가 들어가는 방식wrist LPIPSexo1exo2wrist PSNRvs M1
M1branch 없음0.25790.05990.054218.52
M2PRoPE key만0.26220.06210.056718.4548/112
M4additive camera token0.25600.05730.051618.5456/112
M3PRoPE q+k, GT wrist pose0.14380.05320.049221.74112/112

후반부(step ≥ 6000, n=32)에서 격차는 더 벌어진다: M1 0.2568 / M2 0.2546 / M4 0.2524 / M3 0.1150. 수렴 속도가 아니라 도달점의 차이다.

M2·M3·M4는 branch가 동일하다. 450개 텐서를 똑같이 학습시킨다. 다른 건 오직 카메라가 어떻게 들어가느냐이고, 그 하나로 0.1438과 0.26 사이가 갈린다. 그리고 M2는 M1보다 나쁘다 — 스텝당 1.72배 느리게 돌린 대가가 마이너스다. 카메라 없는 branch는 0이 아니라 순비용이다.

이 결함은 리뷰로 잡히지 않았다

테스트 65개, 비트 정확한 check_noop, 양성 대조까지 전부 초록인 상태였다. 드러난 건 "카메라 파라미터가 어떻게 들어가고 있냐"는 질문을 받았을 때다. 구현이 명세와 맞는지는 검증했지만, 명세가 논문의 메커니즘과 맞는지는 아무도 안 봤다.

3 M4는 왜 null이었나 — 두 결함이 서로를 가렸다

M3는 GT wrist pose를 먹으므로 배포 불가다(미래 wrist pose = 예측 대상 = action). M4는 VGGT 방식으로 camera token이 뷰 내용에서 카메라를 인지하게 하고 PRoPE를 완전히 걷어냈다.

결함 A — branch가 3D 토큰의 순서를 구분하지 못한다

exo_camera_broadcast는 exo 뷰 하나당 벡터 하나를 그 뷰의 모든 3D 토큰에 똑같이 더한다. 그런데 PRoPE가 3D 토큰에 위치를 부여하던 유일한 장치였고, M4는 그걸 껐다. 남은 건 위치 정보 없는 선형 투영 + 뷰당 상수이고, attention은 순열 불변이다.

within-view permutation : max|diff| = 1.49e-08 ← 부동소수점 노이즈 across-view swap : max|diff| = 1.07e-02 ← 실제 변화

같은 exo 뷰의 3D 토큰을 아무렇게나 섞어도 출력이 동일하다. camera token이 구분하는 건 exo1인가 exo2인가, 딱 1비트다.

arm키가 나르는 것쿼리가 나르는 것
M2PRoPE — 토큰별 위치×카메라없음
M3PRoPE — 토큰별PRoPE — GT wrist pose
M4뷰당 상수 1개camera token
M4는 M2보다도 기하적으로 약하다. M2는 최소한 키에 토큰별 기하가 있었고 그걸 받아줄 쿼리가 없었을 뿐이다. M4는 양쪽 다 없다. 설계 문서에 "PRoPE는 완전히 제거된다 — 둘은 대안이지 층이 아니다"라고 쓰고 그 위에 arm 전체를 세운 것이 오류였다. 대체해야 할 건 GT wrist pose(oracle)였지, exo 키 쪽 기하가 아니었다. exo extrinsics는 정적 카메라라 배포 시점에도 알 수 있다.

결함 B — 인지한 pose가 M1보다도 나빴다

arm보고된 rot_degwrist만exo1exo2
M14.714.71
M34.084.08
M43.665.172.033.78

M4의 전체 3.66°가 최고로 보이지만, 뷰별로 쪼개면 wrist는 5.17°로 M1의 4.71°보다 나쁘다. 좋아 보인 건 쉬운 exo 두 개(그중 exo1은 타깃이 정확히 상수 0) 덕분이다. 설계 §4.3이 정확히 이 실패 모드를 예측하고 지표를 만들어뒀고, 지표가 잡았다.

두 결함이 서로를 가렸다. M4가 7.8°(아래 §6 기준) pose를 제대로 조건화에 썼다면 M1보다 46% 나빠졌을 것이다. M1과 동률로 끝난 건 additive 형태가 순열 불변일 만큼 약해서 해를 끼칠 만큼도 카메라를 반영하지 못했기 때문이다. 형태만 고쳤다면 더 나빠졌다.

4 M3는 카메라를 따르는가 — 반사실 궤적

M3의 44%는 "카메라대로 재투영한다"와 "pose를 클립 지문으로 써서 암기했다" 양쪽과 양립한다. 가르려면 학습 때 준 적 없는 카메라를 주고 생성해야 한다. 같은 클립을 같은 노이즈로 궤적만 바꿔 재생성했다(168개 영상, held-out 6 + 한 번도 안 본 6클립).

궤적앵커 보존M1 heldM3 heldM1 unseenM3 unseen
offset (강체 0.15 m)0.00010.00960.00020.0111
random_dir (거리 동일, 방향 무작위)0.00000.06300.00000.0982
static (정지)0.00000.11180.00000.2035
jitter (σ=2 cm)0.00000.12340.00000.1061
swap (다른 클립)0.00120.15190.00080.2189
reverse (되감기)0.00010.22010.00050.2746

수치는 vs_gt_traj LPIPS — 같은 클립·같은 노이즈·진짜 카메라로 만든 생성물 대비 변화량. 0이면 카메라 치환이 아무것도 안 바꿨다는 뜻이다.

M3는 따른다. 궤적을 바꾸면 출력이 움직이고 실제 영상과의 거리도 함께 나빠진다. M1은 앵커 보존 궤적 네 개에서 정확히 0.0000(중앙값·최댓값 모두, 비트 동일) — M1은 wrist_viewmats를 안 읽고 proprio 토큰 1·2는 예측 타깃이라 노이즈로 대체되므로, 실제 입력은 토큰 0 하나뿐이고 앵커 보존 궤적은 그걸 건드리지 않는다. 반응은 M3의 메커니즘에 귀속된다.
단, 쓰는 건 배치가 아니라 흐름이다. 앵커 고정 궤적의 순서가 held-out과 unseen에서 동일하다: offset(어디에 있나) ≪ random_dir(어디로 가나) < static/jitter(움직이나 / 매끄럽나).

손목 뷰의 optical flow는 (카메라 변위) × (장면 깊이)로 결정된다. offset은 flow를 전혀 안 바꾸고, random_dir은 방향만, static/jitter는 크기 자체를 바꾼다 — 반응 크기가 정확히 그 순서다. M3는 카메라를 "장면 안에 나를 배치하는 정보"가 아니라 "프레임 사이 화면이 어떻게 흐를지 예측하는 정보"로 쓴다.

절대 위치가 무시되는 이유도 코드에 있다: latent_mask[:, :, 0] = 1모든 뷰의 첫 프레임을 clean하게 준다(wrist 포함). 절대 위치는 이미 그 이미지 안에 있어 중복이고, 첫 프레임이 말해주지 않는 건 "카메라가 다음에 어디로 가는가"뿐 — M3는 정확히 그 부분만 골라 썼다.

생성 품질: 한 번도 안 본 에피소드에서 M3 gt의 wrist LPIPS는 0.1308 (M1 0.2422). unseen은 에피소드 단위로 제외했다 — 같은 에피소드의 다른 창은 같은 장면·로봇· 카메라 리그라 unseen이 아니다. 641,022개 중 subset의 8,381개 에피소드를 통째로 빼고 남은 441,671개에서 64개를 뽑아 전부 디코딩 검증(64/64)했다.

5 기하는 정확한가 — 캘리브레이션 검증

droid_dataset.py:167은 Task 2 이래로 이렇게 기록해뒀다 — wrist↔exo1은 droid_raw의 원시 캘리브레이션, exo1↔exo2는 PointWorld의 bundle-adjusted 캘리브레이션. "알려진, 수용된 불일치이며 일치한다고 증명되지 않았다." 그런데 branch는 매 logit에서 이 둘을 곱한다.

exo1 첫 프레임을 metric depth와 원본 픽셀 intrinsics로 unproject → PointWorld 상대 pose로 exo2에 재투영 → 실제 exo2와 비교. 40 clips.

posePSNRNCCcoverage
참 pose13.140.60580.236
+1°12.920.58200.236
+2°12.580.54720.236
+5°11.770.45520.234
+10°10.980.36030.229
identity9.420.13690.846
참 pose가 최고이고 주입 오차에 단조 감소하며 지표가 1°에서 갈린다. exo 캘리브레이션은 1~2°보다 정확하다 — 3D 토큰은 우리가 말한 좌표에 실제로 있다. M4 실패 원인 후보에서 "3D 토큰이 엉뚱한 좌표에 있었다"는 빠진다. DA3 캐시 140 GB도 PRoPE 수식도 무죄다.
같은 실행에서 더 중요한 수치가 나왔다: query 패치 하나가 exo 시야의 10.14°를 차지한다 (6×10 격자). 캘리브레이션 오차(1~2°)는 패치의 1/5. 두 가지가 따라온다 — 더 좋은 캘리브레이션은 얻을 게 없고, 정밀한 ray correspondence는 애초에 가능하지 않았다. wrist query 패치 하나의 "ray"는 exo 뷰에서 10° 원뿔이다.

Phase 0의 워프는 이 질문에 답한 적 없다 — exo_to_wrist(exo_pose, wrist_pose)로 두 pose 모두 droid_raw에서 읽어 한 캘리브레이션 안의 컨벤션(w2c vs c2w)만 확인했다. 그리고 이 검증은 qᵀkkey 쪽만 본다 — wrist 뷰로 워프하려면 wrist intrinsics가 필요한데 파이프라인에 없다.

6 두 프로브 — 결정적 수치

학습 없이 샘플링만. unseen 6클립. 기준선 M3 gt vs_real LPIPS = 0.1308, M1 = 0.2422 → 기하 메커니즘 전체의 값어치 = 0.1114. 아래 비율은 전부 이 값 대비다.

(A) pose 정확도 — 절벽이 2°와 5° 사이

pose 오차vs_realΔ이득 유지율
0° (GT)0.1308100%
0.1564+0.025677%
0.2448+0.1140−2%
(M4 달성치)0.2937+0.1629−46%
15°0.3638+0.2330−109%
5°를 넘으면 무익이 아니라 유해하다. 8°에서 M3는 M1보다 46% 나쁘다. 틀린 카메라는 안 주는 것만 못하다.

달성치와의 격차 — 보고된 rot_deg_wrist는 토큰 0이 GT로 고정된 채 평균에 들어가므로 예측이 필요한 프레임의 오차는 보고값의 1.5배다:

필요
≤ 2°
M3 pose head
~6.1°
M1
~7.1°
M4 camera token
~7.8°

어느 arm도 근처에 못 갔고 격차가 3~4배다. 마진 문제가 아니다.

(B) 3D 토큰 — 있어야 하지만 맞을 필요는 없다

카메라와 PRoPE는 건드리지 않고 내용만 바꿨다. 뷰를 넘나들지 않고 뷰 안에서 섞는다 — 넘나들면 어느 camera token과 짝지어지는지도 바뀌어 "내용"과 "카메라"가 뒤섞인다.

tokens_3dvs_realΔ메커니즘 대비
gt0.1308
shuffle (뷰 안 순열)0.1387+0.00797%
other (다른 클립 내용)0.1394+0.00858%
zero (상수)0.2251+0.094385%
⚠️ 정정 — 이 실험은 tokens_3d(DA3)만 바꿨다. branch의 key/value는 두 그룹이고(k = cat([k_3d, k_ref]), 1216개 + 120개), 두 번째 refx[:, exo_ref_slice(...)]비디오 시퀀스에서 직접 오는 exo t=0 VAE latent안 바꿨다. 게다가 exo 뷰는 메인 DiT 시퀀스에도 있고 t=0가 clean하게 주어지므로 global attention으로도 접근된다.

따라서 보인 것은 "장면 내용은 8%"가 아니라 "DA3가 나르는 장면 정보는 exo latent와 중복이라 8%"다. 두 해석이 남고 이 실험은 못 가른다 — (a) branch가 장면 정보를 주로 120개 exo latent에서 읽고 DA3 1216개는 중복이다, (b) branch가 장면 정보를 거의 안 읽는다. 오히려 zero가 85%인 것이 (a) 쪽 정황이다 — 내용이 중복이라면 DA3를 상수로 채워도 ref에서 읽으면 되는데 무너졌으니, DA3 그룹은 내용이 아니라 키의 90%를 차지하는 attention 기질로서 필요했던 셈이다. 깨끗한 실험은 ref도 같이 바꾸는 것이다.

그래도 왜 이렇게 적은가

정정과 별개로 이유는 셋이 남는다. (1) 정보가 중복이다 — DA3는 exo 첫 프레임의 특징이고 exo latent는 같은 프레임의 VAE 인코딩, 같은 이미지의 두 표현이다. (2) query 해상도가 10.14°/patch다 — 내용의 정밀도가 애초에 활용될 수 없다. (3) "다른 클립"이 무의미한 게 아니다 — 같은 DROID 테이블탑이라 "책상이 있고 팔이 있다" 수준의 통계는 전이된다. 무작위 노이즈가 아니다.

그리고 zero에서 깨지는 건 PRoPE가 키와 값을 둘 다 회전시키는데 (_apply_to_kv(k_3d), (v_3d)) 모든 키가 같은 벡터가 되어 attention이 선택할 대상이 없어지는 것이다.

pose 5° 오차 +0.1140 잘못된 3D 내용 +0.0085 ─────── 13.4배

메커니즘은 3D 내용보다 카메라 pose에 13배 민감하다.

7 종합 — M3의 44%의 정체

네 실험이 각각 다른 경로로 같은 곳을 가리킨다.

실험말하는 것
4 arm ablation카메라가 정확하고(내용) 상대 변환으로(형태) 들어가야 한다. 둘 중 하나만 깨져도 0. 직렬이다
반사실 궤적M3는 카메라를 따르지만 배치가 아니라 프레임 간 flow를 쓴다 — 조대한 정보
캘리브레이션기하는 정확하지만 query 격자가 10°/patch — ray 수준 검색은 불가능
프로브DA3의 장면 특이적 내용은 8%, pose 정확도가 13배 더 중요
M3의 44%는 압도적으로 "wrist 토큰이 자기 카메라를 알게 된 것"이다. pose 5° 오차가 메커니즘 전체를 지우는 반면 DA3 내용 교체는 8%다. 다만 "3D 토큰은 통로일 뿐"이라고까지 말하려면 §6의 정정이 요구하는 실험이 필요하다 — 지금 확실한 건 DA3의 장면 특이적 산출물이 exo latent와 중복이라는 것까지다. 그리고 그 결론은 정정의 영향을 받지 않는다: 중복이든 무용이든 DA3를 빼도 8%다.

그리고 M3의 oracle 성격도 날카로워진다. wrist 카메라의 미래 운동 = action이므로, M3가 받은 건 "카메라 정보"가 아니라 사실상 정답 action이고, M3가 한 일은 "주어진 앵커 프레임에서 알려준 운동대로 렌더링"이다.

왜 pose 예측이 2°에 못 미치는지도 이미 안다. 뷰당 clean 프레임이 한 장뿐이라 속도 단서가 없고, 정지 이미지 한 장으로 0.53초 뒤의 카메라를 2° 안에 맞추라는 건 이 프로젝트의 최종 목표를 하위 문제로 풀라는 요구다.

8 개정된 로드맵

  1. action을 camera token의 입력으로. pose 정확도가 유일한 실질 병목이고 action이 그걸 결정한다 — wrist 카메라 pose = 로봇 end-effector pose이고, WAM의 사용 시나리오는 "이 action을 하면 뭐가 보이나"라 action은 추론 대상이 아니라 입력이다. action head는 geometry의 다음 단계가 아니라 전제였다.
  2. 3D 토큰 무용성 확인 — 임의 토큰으로 arm 학습(5h). 확정되면 DA3 파이프라인을 register 토큰으로 대체하고 캐시 140 GB + clip당 14.9 MB 전처리를 없앤다.
  3. 조건화 형태 수정(additive → PRoPE)은 1번 이후. 지금 고치면 나쁜 pose가 그대로 드러나 M1보다 나쁜 arm이 나온다 — 위 표가 −46%를 말한다.
하지 말 것: 더 나은 캘리브레이션(이미 query 해상도보다 5배 정확) · 더 오래 학습(격차가 후반부에 안 벌어짐, step≥6000에서 1.7%) · 조건화 형태부터 고치기(3번).

9 아직 확신하지 못하는 것

10 방법론 교훈

통과하는 게이트는 그 게이트가 무엇을 배제하는지 확인하기 전까지 증거가 아니다. M2는 65개 테스트와 비트 정확한 check_noop을 통과한 채 메커니즘의 절반만 돌고 있었다. M4는 137개 테스트와 6개 검증 지표를 통과한 채 순열 불변이었다. 두 번 다 "구현이 명세와 맞는가"는 검증됐고 "명세가 물리와 맞는가"는 아무도 안 봤다.

메커니즘을 옮길 때는 그것이 무엇을 전제하는지 다시 유도해야 한다. 설계 §3에서 VGGT 소스를 직접 읽고 구조는 정확히 옮겼지만 "모든 프레임이 관측된다"는 전제는 명시조차 안 했다.

19시간짜리 학습 전에 30분짜리 프로브를 돌렸어야 했다. 이번에 순열 불변성은 단위 테스트 수준의 코드 10줄로 즉시 드러났고, pose 임계값과 3D 토큰 기여도는 학습 없이 30분에 나왔다. 셋 다 M4를 설계하는 시점에 가능했다.