cam[wrist,t1..2]는 노이즈만 본다.8,000스텝 완주(18h51m, max_steps=8000 reached 정상 종료). 정합 129개 검증점.
| arm | wrist LPIPS (step≥1000) | exo1 | exo2 | wrist PSNR | vs M1 |
|---|---|---|---|---|---|
| M1 | 0.2579 | 0.0599 | 0.0542 | 18.52 | — |
| M2 | 0.2622 | 0.0621 | 0.0567 | 18.45 | 48/112 |
| M4 | 0.2560 | 0.0573 | 0.0516 | 18.54 | 56/112 |
| M3 (oracle) | 0.1438 | 0.0532 | 0.0492 | 21.74 | 112/112 |
| arm | 보고된 rot_deg | wrist만 | exo1 | exo2 |
|---|---|---|---|---|
| M1 | 4.71 | 4.71 | — | — |
| M3 | 4.08 | 4.08 | — | — |
| M4 | 3.66 | 5.17 | 2.03 | 3.78 |
branch 계측도 같은 방향이다: M4 ratio_mean 0.063 / attn_max_mean 2.05 vs
M2 0.186 / 6.99 — M4의 branch가 M2의 1/3만 기여한다.
쓸 만한 신호를 못 받아 스스로 기여를 줄인 모양새다.
이게 핵심이고, 설계 단계에서 놓친 구조적 오류다.
hook은 input_seq의 video 부분을 frame-wise attention에 넘긴다. 그런데 그 video는
noised latent다 — _forward_single이 받는 x가
xt_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는 모든 프레임이 관측된 세팅이다. "자기 프레임을 보고 자기 카메라를 추론하라"가 성립한다. 우리는 생성 세팅이라 그 프레임이 곧 만들어내야 할 대상이다.
설계문서 §3에서 VGGT 소스를 직접 읽고 "camera token은 프레임당 하나, 일반 시퀀스에 concat, 전용 카메라-카메라 레이어 없음"까지는 정확히 옮겼다. 그런데 "그 프레임이 clean하다"는 전제는 명시조차 하지 않았다. 메커니즘을 옮길 때는 그것이 무엇을 전제하는지 다시 유도해야 한다.
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% 개선된 것도 같은 이야기다.
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과 분리되지 않았다 — 신호가 없어서 안 쓴 건지 형태가 나빠서 못 쓴 건지 이 실험만으로는 못 가른다.
VGGT의 frame_blocks는 SelfAttentionBlock — frame 내
self-attention + MLP + layerscale + qk-norm의 완전한 블록이다
(aggregator.py:43-55). 우리 CameraFrameAttention은
cross-attention 한 번뿐이고 FFN이 없다. camera token을 query 전용으로 둔 것은
설계 §4.1에서 ablation을 흐리지 않으려고 의도적으로 정한 것이었지만, 원인 1과 곱해지면
"약한 모듈이 정보 없는 입력을 본다"가 된다.
M4는 M1과 동률이므로 "camera token + AA를 이 형태로 붙이는 것은 효과 없음"이 확정됐다. 그리고 원인 1이 맞다면 이건 아이디어의 실패가 아니라 배선의 실패다 — 같은 아이디어를 정보가 있는 곳에 연결하면 결과가 달라질 수 있다.
더 근본적으로: 반사실 실험에서 확인했듯 절대 카메라 위치는 이미 주어진 첫 프레임 안에 있어 중복이고, 중복이 아닌 유일한 성분은 미래 운동 = action이다. camera token에게 "첫 프레임만 보고 미래 카메라 운동을 추론하라"는 건 이 프로젝트의 최종 목표를 하위 문제로 풀라는 요구다 — pose regression loss 하나로, 9,999 clip × 25.8 epoch에서.
cam[wrist,t]가 exo 뷰를 보게 — 원인 1의 직접 수정.
camera_attention.py의 kv 인덱싱만 바꾸면 된다. key 60 → 180개, 비용 무시할 수준.sample_offline.py가 M4를 그대로 받는다.