Index
2026-05-01 — Analysis

LGM Supervision Alignment/Normalization 체계 분석

VGGRPO | RoboCasa GT 학습 — depth per-view scale 변동 원인 추적

TL;DR

3.7×
eye_in_hand s 변동
6.49
s 최대값
1.00
pose scale (per-clip)
96
총 views (3cam×32f)

1 배경 / 목적

RoboCasa GT 학습 viz에서 "view마다 pred 값이 매우 다르다"는 현상이 관찰됨. 3 cam (agentview_left/right, eye_in_hand) × 32 frame = 96 view가 각자 다른 scale의 출력을 내고 있었다. 원인 추적을 위해 supervision signal이 어느 단위에서 어떻게 정렬되어 들어가는지 전체 alignment 체계를 정리.

핵심 질문: depth, pose, ray, scene_flow, image normalization 각각이 view 간 일관성을 보장하는지? scale mismatch가 어디서 발생하는지?

관련 학습 스크립트: scripts/train_lgm_robocasa_v0.py, scripts/train_lgm_gt_v0.py, src/data/robocasa_dataset.py

2 작업 내용

5가지 supervision 신호 각각에 대해 alignment 단위, 방식, scale 변동 여부를 코드 레벨에서 추적.

Depth — per-view (frame-level) 독립 LS fit

위치: scripts/train_lgm_gt_v0.py:align_gt_depth_to_any4d() scripts/train_lgm_robocasa_v0.py main() batch loop for v in range(N_total): # N_total = 96 (3cam × 32frame) gt_d = depths[cam_idx][t_idx] # GT metric depth [216, 384] a4d_d = target[f"pred{v+1}"]["depth_along_ray"] # pseudo [294, 518] aligned, valid = align_gt_depth_to_any4d(gt_d, a4d_d, pred_shape) # → lstsq: A @ [s, b]^T = a (valid 픽셀에서만) # → aligned = (s * gt + b).clamp(min=0)

Pose — per-clip 1 scale (median trans norm ratio)

위치: scripts/train_lgm_robocasa_v0.py:gt_poses_4x4_to_any4d_format() 1. Reference view 0 (cam0 t0) 기준 relative T 2. scale = median(a4d_norms / gt_norms) ← clip 전체에 single scalar 3. gt_trans_scaled = gt_trans * scale → view간 pose 일관성 OK

Ray — per-cam (intrinsic 고정)

위치: scripts/train_lgm_robocasa_v0.py:compute_gt_rays_per_cam() rays_per_cam = [compute_gt_rays_per_cam(K_list[c]) for c in range(3)] # 같은 cam의 32 frame이 동일 ray field 사용 # intrinsic은 cam에만 의존, frame 무관

Scene flow — pseudo only (GT 없음)

Any4D frozen 출력을 그대로 target으로 사용. 별도 alignment 없음. pseudo noise가 그대로 들어감.

Image normalization — per-frame (표준)

VAE input: /255 → [0,1] (Wan 컨벤션). Any4D input: (frame/255 - DINOv2_MEAN) / DINOv2_STD. Frame 독립, view-independent.

3 결과

Supervision 항목Alignment 단위방식Scale 변동
Depthper-view (cam × frame)LS s·gt + b ≈ pseudoview마다 변동 ← 문제
Poseper-clip (1 scale)trans norm median ratio + ref view 0clip 전체 일관
Rayper-cam (intrinsic)K⁻¹ unit normalizecam 내 일관, frame 무관
Scene flowpseudo onlyalignment 없음, pseudo noise
RGB (VAE)per-frame/255 → [0,1]표준 컨벤션
RGB (Any4D)per-frameDINOv2 mean/std표준
GT depth (metric)per-demoOpenGL z-buffer → meterdemo 내 일관

eye_in_hand depth scale s 관찰값 (PnPCounterToMicrowave demo_0, 3 frames)

Cameras means stds range판정
agentview_left1.270.03[1.25, 1.30]안정
agentview_right1.520.01[1.51, 1.53]안정
eye_in_hand3.412.18[1.77, 6.49]불안정 (3.7× range)
핵심 발견: depth target scale s가 eye_in_hand에서 frame마다 1.77 → 6.49로 3.7× 점프. pred가 view 간 일관된 scale을 학습하기 어렵다. 이것이 viz에서 본 view간 불일치의 주원인.
확인된 일관 항목: pose (per-clip), ray (per-cam), image normalization — 모두 view간 일관성 OK.
부가 이슈: depth loss 변수 ld에 depth + ray L1이 같이 합산됨 — 변수명 혼동 주의 (compute_phase_b_loss 내부).

4 Takeaway

근본 원인: monocular backbone의 frame-level scale ambiguity

Any4D는 monocular video 모델이라 각 frame의 depth scale이 independent하게 결정됨. 우리가 GT depth를 pseudo 스케일에 맞추는 방식(LS fit)은 이 scale을 frame마다 따라가게 돼 있어, pseudo가 frame마다 다른 scale을 출력하면 GT alignment도 frame마다 달라진다. eye_in_hand의 경우 손 끝/배경 geometry가 frame마다 크게 바뀌어 pseudo scale 변동이 특히 심하다.

Pose/Ray/Image는 per-clip 또는 per-cam 단위로 alignment가 설계되어 view 간 일관성이 보장된다. Depth만 이 설계에서 벗어나 있다.

5 Next Steps

단기 대응 (클린 데이터 도착 후 평가)

robocasa 측에서 "infinity 영역 제거된 클린 데이터셋"을 기다리는 중. 클린 데이터 도착 시 depth_max=5 마스크가 무해해지면 스케일 변동이 얼마나 줄어드는지 재확인.

개선 옵션 A: Disparity 변환 1/(1+d)

depth를 disparity로 바꾸면 [0, 1] 자동 bound, alignment 자체 불필요해짐. outlier도 자연 dampen. 우선순위 높음.

개선 옵션 B: Per-cam (not per-frame) depth alignment

같은 cam의 모든 frame에 single (s, b) 적용. frame-level 변동 제거, cam 간 차이는 유지.

개선 옵션 C/D: Pseudo fallback + Confidence 활용

C: masked 픽셀에도 pseudo target → "free output" 방지. D: Any4D conf 채널을 invalid mask 학습에 활용.

장기: DA3 backbone 교체

multi-view DA3는 cross-view attention으로 scale을 인코더 안에서 잡아줌 → per-view alignment 자체 제거. 클린 데이터에서도 scale 변동 잔존 시 전환 트리거 (plan-da3_backbone_migration.md 참조).