Index
2026-08-07 — Analysis

M4는 왜 실패했나 — camera token이 볼 것이 없었다

MetaView → WAM Backbone | 4-arm ablation 완결 (job 72911)

TL;DR

0.2560
M4 wrist LPIPS
56/112
M4 vs M1
5.17°
M4 wrist pose (M1=4.71)
3.78°
exo2 pose (정적)
0.063
branch ratio (M2=0.186)

1 결과

8,000스텝 완주(18h51m, max_steps=8000 reached 정상 종료). 정합 129개 검증점.

armwrist LPIPS (step≥1000)exo1exo2wrist PSNRvs M1
M10.25790.05990.054218.52
M20.26220.06210.056718.4548/112
M40.25600.05730.051618.5456/112
M3 (oracle)0.14380.05320.049221.74112/112
wrist에서는 null, exo에서만 4–5% 실질 개선. 이 비대칭이 원인 분석의 출발점이다. 후반부(step≥6000)도 M4 0.2524 vs M1 0.2568로 1.7% — 격차가 벌어지지 않는다. M3는 step 1000부터 112/112였다. 더 오래 돌려서 해결될 문제가 아니다.

뷰별 pose — 지표가 만들어진 목적대로 작동했다

arm보고된 rot_degwrist만exo1exo2
M14.714.71
M34.084.08
M43.665.172.033.78
M4의 wrist pose는 M1보다 나쁘다(5.17° vs 4.71°). 전체 3.66°가 좋아 보인 건 쉬운 exo 두 개(그중 exo1은 타깃이 상수 0) 덕분이다. 설계문서 §4.3에 "정적인 exo 두 개와 평균 내면 wrist가 나빠지는데도 좋아 보인다, 이 지표가 존재하는 이유가 그걸 잡기 위해서다"라고 적어뒀는데, 그 상황이 실제로 발생했고 지표가 잡았다. (작업 중 한 번 이 수치를 쪼개지 않고 "인지에 성공했다"고 성급하게 읽은 실수가 있었다.)

branch 계측도 같은 방향이다: M4 ratio_mean 0.063 / attn_max_mean 2.05 vs M2 0.186 / 6.99M4의 branch가 M2의 1/3만 기여한다. 쓸 만한 신호를 못 받아 스스로 기여를 줄인 모양새다.

2 원인 1 — 추론해야 할 토큰이 볼 것이 없다

이게 핵심이고, 설계 단계에서 놓친 구조적 오류다.

hook은 input_seq의 video 부분을 frame-wise attention에 넘긴다. 그런데 그 video는 noised latent다 — _forward_single이 받는 xxt_latents이고, 그중 latent frame 0만 latent_mask[:, :, 0]=1로 clean하게 주어진다.

camera_attention.py의 매핑은 cam[v,t] ← view v, frame t다. 따라서:

camera token무엇을 보나결과
cam[exo1,t0], cam[exo2,t0]clean exo 첫 프레임정적 카메라라 이걸로 결정됨 → 2.03° / 3.78°
cam[wrist,t0]clean wrist 첫 프레임근데 이 pose는 이미 clean proprio 토큰으로 주어진다 → 중복
cam[wrist,t1], cam[wrist,t2]noise (생성 대상)추론 불가 → 5.17°
추론 가능한 토큰은 이미 답을 받았고, 답이 필요한 토큰은 볼 것이 없다.

VGGT의 전제를 옮기지 않았다

VGGT는 모든 프레임이 관측된 세팅이다. "자기 프레임을 보고 자기 카메라를 추론하라"가 성립한다. 우리는 생성 세팅이라 그 프레임이 곧 만들어내야 할 대상이다.

설계문서 §3에서 VGGT 소스를 직접 읽고 "camera token은 프레임당 하나, 일반 시퀀스에 concat, 전용 카메라-카메라 레이어 없음"까지는 정확히 옮겼다. 그런데 "그 프레임이 clean하다"는 전제는 명시조차 하지 않았다. 메커니즘을 옮길 때는 그것이 무엇을 전제하는지 다시 유도해야 한다.

놓친 설계: wrist 카메라는 팔에 달려 있고, 팔은 exo 뷰에 보인다. 즉 wrist 카메라의 pose는 exo 이미지에서 관측 가능하다 — 사람이 추론하는 방식이 그것이다. 그런데 현재 구현은 cam[wrist,t]가 오직 wrist 뷰 frame t만 보게 한다. 정보가 없는 곳만 보고, 있는 곳은 안 본다.

증거: 뷰별 pose 오차가 정확히 이 순서다 — exo2(정적, clean t0로 결정) 3.78° < wrist(운동, noisy t1/t2 필요) 5.17°. wrist LPIPS는 null인데 exo LPIPS만 4–5% 개선된 것도 같은 이야기다.

3 원인 2·3

2. additive 조건화는 rank-1이다 (수식 논증, 미검증)

L[i,j] = q_v[i]·k_v[j] ← i,j 모두 fine. 카메라 없음 + q_v[i]·k_c[v] ← i fine, j는 view 단위 상수 + q_c[t]·k_v[j] ← i는 frame 단위 상수, j fine + q_c[t]·k_c[v] ← (frame,view)당 스칼라 하나

PRoPE(q_vᵀ P_t P_v⁻¹ k_v)는 카메라가 bilinear form 자체를 바꿔 모든 (i,j)가 joint하게 변형된다. additive는 rank-1 bias로만 들어가서 ray correspondence ("이 픽셀이, 이 카메라일 때, 저 3D token")를 표현 못 한다.

약한 방증: branch ratio가 M2의 1/3. 다만 원인 1과 분리되지 않았다 — 신호가 없어서 안 쓴 건지 형태가 나빠서 못 쓴 건지 이 실험만으로는 못 가른다.

3. frame-wise 절반이 VGGT보다 훨씬 약하다 (확인됨)

VGGT의 frame_blocksSelfAttentionBlock — frame 내 self-attention + MLP + layerscale + qk-norm의 완전한 블록이다 (aggregator.py:43-55). 우리 CameraFrameAttentioncross-attention 한 번뿐이고 FFN이 없다. camera token을 query 전용으로 둔 것은 설계 §4.1에서 ablation을 흐리지 않으려고 의도적으로 정한 것이었지만, 원인 1과 곱해지면 "약한 모듈이 정보 없는 입력을 본다"가 된다.

4 Takeaway

null result 자체는 정보다

M4는 M1과 동률이므로 "camera token + AA를 이 형태로 붙이는 것은 효과 없음"이 확정됐다. 그리고 원인 1이 맞다면 이건 아이디어의 실패가 아니라 배선의 실패다 — 같은 아이디어를 정보가 있는 곳에 연결하면 결과가 달라질 수 있다.

더 근본적으로: 반사실 실험에서 확인했듯 절대 카메라 위치는 이미 주어진 첫 프레임 안에 있어 중복이고, 중복이 아닌 유일한 성분은 미래 운동 = action이다. camera token에게 "첫 프레임만 보고 미래 카메라 운동을 추론하라"는 건 이 프로젝트의 최종 목표를 하위 문제로 풀라는 요구다 — pose regression loss 하나로, 9,999 clip × 25.8 epoch에서.

5 다음

  1. M3-additive (2000스텝 ~5h) — 원인 2를 원인 1에서 분리한다. GT pose를 additive로 넣으면 지각이 변수에서 빠진다. M3에 근접하면 조건화 형태는 무죄, M2/M1 수준이면 조건화가 병목. 이걸 먼저 해야 배선을 고쳐도 또 null이 나오는 걸 막는다.
  2. cam[wrist,t]가 exo 뷰를 보게 — 원인 1의 직접 수정. camera_attention.pykv 인덱싱만 바꾸면 된다. key 60 → 180개, 비용 무시할 수준.
  3. frame-wise 블록에 FFN 추가 (원인 3). 위 둘 이후 잔여 격차가 있을 때.
  4. M4 체크포인트로 반사실 궤적 실험. sample_offline.py가 M4를 그대로 받는다.
하지 말아야 할 것: 더 오래 돌리기. 격차가 후반부에 벌어지지 않았다 (step≥6000에서 1.7%). M3는 step 1000부터 112/112였다. 8,000스텝이 부족해서가 아니다.