depth_loss_weight가 둘 다 1.0이라 총손실 구성이 달라진다 → 판단 필요.DROID world-frame pointmap 데이터 파이프라인은 전 구간 검증을 마쳤다(이전 리포트). 하지만 학습을 돌리기 전에 두 가지가 미해결이었고, 둘 다 실패하면 A/B 자체가 성립하지 않는 종류였다.
_prepare_condition의 torch.cat([gt_rgb, gt_depth], dim=0) → 한 번의 vae.encode). 자연영상으로 학습된 VAE가 “절반이 상수 −1(sentinel)인 이미지”를 인코딩해야 하고, X-WAM에는 depth loss mask가 없어 모델이 sentinel 자체를 예측 타깃으로 배운다.덤으로, 260731 분석에서 미해결로 남긴 “wrist ~5 cm 오차 바닥의 출처”—VAE 표현 한계인가 diffusion 예측 오차인가—도 같은 실험 하나로 판정된다.
tmp/vae_roundtrip.py)GT depth/pointmap을 실제 학습 경로와 동일하게 로더에서 뽑아 vae.encode → vae.decode만 태우고, sentinel 인코딩을 역산해 metric 단위(cm)로 오차를 낸다. diffusion은 전혀 개입하지 않으므로 순수 표현 상한(ceiling)이다. 4 클립 × 3뷰 × 9프레임.
비교군 4개를 동일 VAE로 통과시켜 정규화 스킴을 직접 대조했다 — RGB(참조) / depth 고정 [0.05, 1.8] m(대조군) / pointmap world XYZ box B(실험군) / X-WAM 원래 per-episode×per-view min-max(유효 픽셀의 metric depth를 뷰별로 재정규화해 에뮬레이션).
mean/std는 Wan2_2_VAE wrapper의 buffer다. vae.model.cuda()만 하면 Expected all tensors to be on the same device로 죽는다. vae.cuda()로 wrapper를 옮겨야 한다.tmp/smoke_droid.sh)로그인 1 GPU, 100 step, pretrained_checkpoint=null(wan22_5b base부터). depth arm=GPU 1 / pointmap arm=GPU 3 병행.
scripts/train_sft_repro.py에 save_last_ckpt 노브를 추가했다(기본 True → 실제 학습 동작 불변). 스모크가 111 GB DeepSpeed 체크포인트를 쓰지 않게 하기 위한 것 — 실제로 experiments/smoke_droid_*가 각 3.5 K로 끝났다.
| 표현 | ext1 med | ext2 med | wrist med | ext1 p90 | ext1 p99 |
|---|---|---|---|---|---|
| depth 고정 [0.05, 1.8] m 대조군 | 0.23 cm | 0.24 | 0.12 | 0.99 | 12.58 |
| pointmap world XYZ (3D 유클리드) 실험군 | 0.64 cm | 0.63 | 0.65 | 3.28 | 28.64 |
| X-WAM 원래 per-view min-max | 0.20 cm | 0.22 | 0.10 | 0.82 | 9.37 |
| RGB (참조, 8-bit level) | 0.92 | 1.02 | 1.13 | 3.35 | 14.59 |
| arm | invalid 유지 | valid→invalid 오염 |
|---|---|---|
| depth | 98.3% | 0.03% |
| pointmap | 98.6% | 0.27% |
| step | depth arm video | depth arm depth | pm arm video | pm arm depth |
|---|---|---|---|---|
| 9 | 0.669 | 1.183 | 0.668 | 1.531 |
| 29 | 0.923 | 0.437 | 0.848 | 0.787 |
| 59 | 0.464 | 0.483 | 0.453 | 0.876 |
| 99 | 0.559 | 0.459 | 0.551 | 0.840 |
action_loss/proprio_loss가 여러 step에서 정확히 0.000이다. clean_action_ratio=0.5가 action_proprio_loss_sample_mask = 1 - clean_action_mask로 들어가는데 batch=1이라 샘플 단위 동전던지기가 배치 전체를 0으로 만든다. 실제 학습(batch 4×8=32)에서는 평균화된다.depth_loss_weight=1.0이 두 arm에 동일하니 pointmap arm은 depth 그래디언트를 ~1.8× 더 받는다. 총 손실 구성이 달라진다:
선택지 — (a) 둘 다 1.0 유지: “표현을 바꾸면 손실 크기도 같이 바뀐다”는 자연스러운 as-is 비교, 비율을 명시. (b) pm arm의 weight를 0.55(=0.459/0.840)로 맞춰 표현과 가중을 분리. (a)를 주로 하고 결과가 애매하면 (b)를 3번째 arm으로 추가하는 게 합리적이다 — 재정규화는 그 자체로 임의성을 들여오고, 실무자가 실제로 쓰는 목적함수는 (a)이기 때문.
depth_loss = (depth_latents_pred[0] - gt_depth_latents).pow(2).mean(), 마스크 없음. invalid 56.8%면 depth 용량의 절반 이상이 “상수 sentinel 출력”을 배우는 데 쓰인다. 두 arm에 동일하게 적용되니 A/B는 공정하지만, baseline 비효율이자 명확한 개선 축(masked depth loss)이다.~/repos/Robotics/RoboLab(NVIDIA Isaac Lab 기반, arXiv 2604.09860, RoboLab-120 태스크)를 직접 확인했다. DROID 임베디먼트와 X-WAM 클라이언트가 이미 존재한다 — 우리 학습 도메인과 평가 도메인이 일치한다.
카메라 3개가 DROID의 2 exo + wrist와 그대로 대응하고, 우리 데이터셋의 ext1/ext2/wrist와 일치한다.
evaluation/robolab_server.py:95가 proprio = torch.zeros((1, proprio_dim))인데, 클라이언트가 proprio를 아예 보내지 않기 때문이다. 그런데 RoboLab DROID는 필요한 값을 이미 제공한다 — robolab/robots/droid.py:381의 ProprioceptionObservationCfg에 eef_pos(3) · eef_quat(4) · gripper_pos(1) = X-WAM proprio 스펙(팔당 8dim)과 정확히 일치하고, policies/pi0_family/client.py:84는 이미 raw_obs["proprio_obs"]를 쓰고 있다._extract_observation/_pack_request에 3개 항목 추가 + 서버가 zeros 대신 사용. 좌표계 확인 필요 — 우리 통계는 robot-base(proprio_left_ee_xyz q01=[0.288, −0.414, 0.087])이고 RoboLab eef_pos는 env/world 프레임일 가능성이 있다.--decode-video로 받은 상상 depth/pointmap을 sim GT와 대조하는 별도 지표가 필요하다. → A/B는 SR(간접) + 상상 geometry 오차(직접) 두 축으로 보고하는 게 맞다.RoboLab 환경은 아직 미설치 — uv venv + Isaac Sim 5.0 / Isaac Lab 2.2.0(uv sync), sudo apt install ffmpeg, OMNI_KIT_ACCEPT_EULA=Y. uv run pytest tests/가 설치 검증 스위트다.
trajectory_im128.h5 존재 2,469(285 누락). RoboCasa 1,235의 2.0배.