sra-trajectory-code / docs /MOFLOW_EDGEFIX.md
po03087's picture
Fix edge-index scene mixing; add relative residual cap; guard LED sigma NaN
37c61d4 verified
|
Raw
History Blame Contribute Delete
9.73 kB
# MoFlow β€” edge-index 버그 μˆ˜μ •κ³Ό μƒ˜ν”Œλ§ λ°œμ‚° ν•΄κ²°
**ν•œ 쀄 μš”μ•½**
edge-index reshape λ²„κ·Έλ‘œ κ·Έλž˜ν”„κ°€ 씬을 λ’€μ„žκ³  μžˆμ—ˆλ‹€(`permute` 둜 μˆ˜μ •). 그런데
**정상 κ·Έλž˜ν”„λ₯Ό λ„£μž MoFlow 만 λ°œμ‚°**ν–ˆλ‹€ β€” ν•™μŠ΅μ΄ μ•„λ‹ˆλΌ **μƒ˜ν”Œλ§**μ—μ„œ. λ…Έλ“œλ³„
**residual-norm cap** 으둜 μŠ€ν…λ‹Ή 섭동을 μ œν•œν•΄ ν•΄κ²°ν–ˆλ‹€.
μˆ˜μ • λŒ€μƒ: `models/graph_interaction_nba.py`(`_make_batched_edge_index`),
`models/graph_interaction_nba_v6.py`(SRA κ·Έλž˜ν”„ 좜λ ₯λΆ€).
λͺ¨λ“  변경은 **ν™˜κ²½λ³€μˆ˜ 토글이고 κΈ°λ³Έκ°’ off** β€” μΌœμ§€ μ•ŠμœΌλ©΄ 원본과 λ™μΌν•˜κ²Œ λ™μž‘ν•΄
κΈ°μ‘΄ 체크포인트·기쑴(버그) 결과의 μž¬ν˜„μ„±μ΄ λ³΄μ‘΄λœλ‹€.
---
## 1. 버그: edge-index κ°€ 씬을 λ’€μ„žμŒ
```python
batched = single.unsqueeze(0).expand(S, -1, -1) + offsets.view(-1, 1, 1) # [S, 2, E0]
return batched.reshape(2, -1) # ← 버그
```
`[S, 2, E0]` 의 λ©”λͺ¨λ¦¬ μˆœμ„œλŠ” `s0_src, s0_dst, s1_src, s1_dst, …` 이닀.
`reshape(2,-1)` 은 이 버퍼λ₯Ό **μ•ž/λ’€ 절반**으둜 자λ₯΄λ―€λ‘œ row0 에 씬0 블둝,
row1 에 씬1 블둝이 λ“€μ–΄κ°„λ‹€ β€” src/dst κ°€ μ•„λ‹ˆλΌ **μ”¬μœΌλ‘œ λ‚˜λ‰œλ‹€**.
μ‹€μΈ‘ (A=11, S=4):
| | 같은-씬 edge | 이웃 0인 κ³ μ•„ λ…Έλ“œ | λ…Έλ“œ degree |
|---|---|---|---|
| 버그 | **0 %** | **50 %** | 0 λ˜λŠ” 20 (정상 10) |
| μˆ˜μ • | **100 %** | 0 % | μ „λΆ€ 10 |
`scores.view(B*K*A, A-1)` 의 top-N 이웃 그룹핑도 λ²„κ·Έμ—μ„œ **33 %** 만 μ‹€μ œ νƒ€κ²Ÿκ³Ό
μΌμΉ˜ν–ˆκ³ , μˆ˜μ • ν›„ **100 %** κ°€ 됐닀. 즉 이웃 선택 μžμ²΄λ„ 망가져 μžˆμ—ˆλ‹€.
인덱슀 λ²”μœ„λŠ” 정상(0~43)이라 PyG κ°€ μ—λŸ¬λ₯Ό λ‚΄μ§€ μ•ŠλŠ”λ‹€ β†’ **ν¬λž˜μ‹œ 없이 쑰용히 ν‹€λ¦°
κ·Έλž˜ν”„**λ₯Ό μ“°κ³  μžˆμ—ˆκ³ , κ·Έλž˜μ„œ 였래 λ°œκ²¬λ˜μ§€ μ•Šμ•˜λ‹€.
### μˆ˜μ • (`SRA_EDGE_FIX=1`)
```python
if os.environ.get('SRA_EDGE_FIX', ...):
return batched.permute(1, 0, 2).reshape(2, -1) # src/dst 좕을 λ¨Όμ € β†’ 씬 λ‚΄λΆ€ κ·Έλž˜ν”„
return batched.reshape(2, -1) # κΈ°λ³Έ: μ›λž˜(버그) λ™μž‘
```
**baseline 은 κ·Έλž˜ν”„λ₯Ό μΈμŠ€ν„΄μŠ€ν™”ν•˜μ§€ μ•ŠμœΌλ―€λ‘œ 이 버그와 λ¬΄κ΄€ν•˜λ‹€** β€” λΉ„κ΅μ˜ ν•œμͺ½
좕은 μ˜¨μ „ν•˜λ‹€. 영ν–₯을 받은 것은 SRA/ablation μͺ½λΏμ΄λ‹€.
---
## 2. 증상: ν•™μŠ΅μ€ 정상, μƒ˜ν”Œλ§λ§Œ λ°œμ‚°
edge λ₯Ό 고치자 MoFlow-NBA 의 eval ADE κ°€ ν­λ°œν–ˆλ‹€. κ²Œμ΄νŠΈΒ·μ΄ˆκΈ°ν™” 처방이 연달아
μ‹€νŒ¨ν•œ λ’€, train loss 와 eval 을 **μ‹œκ°„ μ •λ ¬**ν•΄μ„œ 원인을 νŠΉμ •ν–ˆλ‹€:
```
train loss : 96.5 β†’ 35.6 β†’ 20.1 β†’ 18.0 β†’ 16.6 단쑰 κ°μ†Œ (μ™„μ „ 정상)
eval ADE : 1.13 β†’ 1.07 β†’ 1.52 β†’ 1.85 β†’ 3.49 λ°œμ‚°
```
train loss κ°€ λ§€λ„λŸ½κ²Œ λ‚΄λ €κ°€λŠ” **λ°”λ‘œ κ·Έ μˆœκ°„** eval 이 ν„°μ§„λ‹€. 이 관찰이 처방의
λ°©ν–₯을 λ°”κΏ¨λ‹€ β€” λ¬Έμ œλŠ” ν•™μŠ΅μ΄ μ•„λ‹ˆλΌ **μƒ˜ν”Œλ§**이닀.
---
## 3. 원인: train/sample mismatch (MoFlow 고유)
- **ν•™μŠ΅**: 랜덀 timestep ν•˜λ‚˜μ—μ„œ κ·Έλž˜ν”„κ°€ **ν•œ 번** μ μš©λœλ‹€ β†’ 섭동이 λˆ„μ λ  κ²½λ‘œκ°€ μ—†λ‹€.
- **μƒ˜ν”Œλ§**: MoFlow 의 flow 적뢄은 10 μŠ€ν…. κ·Έλž˜ν”„κ°€ **λ§€ μŠ€ν…** 적용되고 κ·Έ 좜λ ₯이
λ‹€μŒ μŠ€ν…μ˜ μž…λ ₯(`y_abs`)으둜 λ˜λ¨Ήμž„λœλ‹€ β†’ 섭동이 **κΈ°ν•˜κΈ‰μˆ˜μ μœΌλ‘œ λˆ„μ **λœλ‹€.
λ²„κ·ΈνŒμ΄ μ•ˆ ν„°μ§„ μ΄μœ λ„ 이걸둜 μ„€λͺ…λœλ‹€: λ…Έλ“œ 절반이 κ³ μ•„μ—¬μ„œ κ·Έλž˜ν”„ 섭동이 사싀상
절반만 ν˜λ €κ³ , 그만큼 μƒ˜ν”Œλ§ λˆ„μ μ΄ μ•½ν–ˆλ‹€. **버그가 μš°μ—°νžˆ μ •κ·œν™” μ—­ν• **을 ν•˜κ³  μžˆμ—ˆλ‹€.
MIDΒ·LED λŠ” iterative denoiser 라 κ·Έλž˜ν”„λ₯Ό 반볡 μ •μ œ κ³Όμ •μ˜ μΌλΆ€λ‘œ ν‘μˆ˜ν•œλ‹€ β€”
κ·Έλž˜μ„œ 같은 V6 λ₯Ό 써도 λ°œμ‚°ν•˜μ§€ μ•ŠλŠ”λ‹€. (이전에 κ΄€μ°°ν•œ "MoFlow λŠ” plug-in λͺ¨λ“ˆμ—
μœ λ… λ―Όκ°ν•˜κ³  faithful GameFormer/C2F 도 μ „λΆ€ λ°œμ‚°"κ³Ό μ •ν™•νžˆ 같은 νŒ¨ν„΄.)
μΈ‘μ •: κ·Έλž˜ν”„ residual λ…Έλ“œ norm β‰ˆ **6.3** vs 호슀트 μž„λ² λ”© λ…Έλ“œ norm β‰ˆ **16**
β†’ 섭동이 μž„λ² λ”©μ˜ **μ•½ 40 %**. ν•™μŠ΅μ΄ μ§„ν–‰λ˜λ©΄ 이 λΆ„ν¬μ˜ 꼬리(outlier)κ°€ 컀지고,
κ·Έ outlier κ°€ μƒ˜ν”Œλ§ λˆ„μ μ„ μ΄‰λ°œν•œλ‹€.
---
## 4. μ‹€νŒ¨ν•œ μ²˜λ°©λ“€κ³Ό κ·Έ 이유
| 처방 | κ±΄λ“œλ¦° λŒ€μƒ | κ²°κ³Ό |
|---|---|---|
| `SRA_GATE_SCALE=0.1` (게이트 μ „μ—­ μΆ•μ†Œ) | ν•™μŠ΅ | eval 5νšŒμ°¨λΆ€ν„° 단쑰 μ•…ν™” |
| `SRA_SOFT_START` (out_proj zero-init + gate bias βˆ’4) | ν•™μŠ΅ μ΄ˆκΈ°κ°’ | eval **3회차** λ°œμ‚° (였히렀 더 빠름) |
| 게이트 κΈ°λ³Έκ°’ μœ μ§€ | β€” | eval 4회차 λ°œμ‚° |
μ…‹ λ‹€ **ν•™μŠ΅**을 μ‘°μ •ν–ˆλ‹€. 그런데 ν•™μŠ΅μ€ μ›λž˜ λ©€μ©‘ν–ˆμœΌλ―€λ‘œ νš¨κ³Όκ°€ μ—†μ—ˆλ‹€.
`SRA_SOFT_START` λŠ” 초기 좜λ ₯을 μ™„μ „ identity(β€–Ξ”β€–=0)둜 λ§Œλ“€μ—ˆλŠ”λ°λ„, ν•™μŠ΅ λͺ‡ μŠ€ν…
λ§Œμ— `out_proj` κ°€ 자라자 λ˜‘κ°™μ΄ ν„°μ‘Œλ‹€ β€” **초기 좩격 가섀이 반증된** μ…ˆμ΄λ‹€.
---
## 5. 해법: λ…Έλ“œλ³„ residual-norm cap (`SRA_RES_CAP`)
μƒ˜ν”Œλ§ μ‹œ **μŠ€ν…λ‹Ή λ…Έλ“œ μ„­λ™μ˜ 크기**λ₯Ό 직접 μ œν•œν•œλ‹€. μ „μ—­ μΆ•μ†Œ(gate scale)와 달리
정상 크기의 섭동은 κ·ΈλŒ€λ‘œ 두고 **λˆ„μ μ„ μœ λ°œν•˜λŠ” 큰 outlier 만** μž˜λΌλ‚Έλ‹€.
```python
res = _gs * gate * self.out_proj(nodes) # [N, D] κ·Έλž˜ν”„ 섭동
_cap = float(os.environ.get('SRA_RES_CAP', 0.0)) # 0 = 끔(κΈ°λ³Έ)
if _cap > 0:
rn = res.norm(dim=-1, keepdim=True) # [N, 1] λ…Έλ“œλ³„ norm
res = res * (rn.clamp(max=_cap) / (rn + 1e-6)) # norm μƒν•œ
out = orig + res
```
검증: `SRA_RES_CAP=3.0` μ—μ„œ λ…Έλ“œ norm 이 μ •ν™•νžˆ `≀3.0` 으둜 잘리고, gradient λŠ”
μœ μ§€λ˜λ©°(ν•™μŠ΅ 계속 κ°€λŠ₯), κ°€λ³€ A(SDD) 도 톡과.
**gate scale 과의 결정적 차이**: gate scale 은 μ‹ ν˜Έλ₯Ό 영ꡬ히 쀄여 κ·Έλž˜ν”„ νš¨κ³ΌκΉŒμ§€
같이 죽인닀. cap 은 λΆ„ν¬μ˜ 꼬리만 자λ₯΄λ―€λ‘œ κ·Έλž˜ν”„μ˜ ν‘œν˜„λ ₯을 λŒ€λΆ€λΆ„ λ³΄μ‘΄ν•œλ‹€.
---
## 6. κ²°κ³Ό
### λ°œμ‚° 지점(eval 3~5) 톡과 μ—¬λΆ€
| eval # | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|---|---|---|---|---|---|---|---|---|
| 게이트 κΈ°λ³Έ | 1.141 | 1.052 | 1.048 | **1.828** | 1.429 | 1.716 | 1.821 | β€” |
| soft-start | 1.134 | 1.075 | **1.516** | 1.852 | 1.697 | 1.862 | 1.765 | β€” |
| gate 0.1 | 1.143 | 1.104 | 1.118 | 1.107 | **1.355** | 1.468 | 1.521 | 1.622 |
| **cap 3.0** | 1.145 | 1.053 | 1.007 | 0.969 | 0.942 | 0.988 | 0.927 | **0.894** |
| **cap 6.0** | 1.138 | 1.018 | 0.999 | 0.976 | 0.950 | 0.947 | 0.962 | 0.895 |
| *baseline(μ°Έκ³ )* | 1.163 | 1.021 | 0.961 | 0.964 | 0.952 | 0.955 | 0.953 | 0.895 |
cap 두 κ°’ λͺ¨λ‘ λ°œμ‚° 지점을 ν†΅κ³Όν•˜κ³  **baseline ꢀ적을 κ·ΈλŒ€λ‘œ 따라간닀**.
### μž₯κΈ° 수렴 (2026-07-28 κΈ°μ€€, 58회 평가 Β· 10029/25500 = 39 %)
| μ„€μ • | ν˜„μž¬ best ADE/FDE | λΉ„κ³  |
|---|---|---|
| **cap 6.0** | **0.7373 / 0.9080** | λ°œμ‚° 없이 58회 |
| **cap 3.0** | **0.7399 / 0.9300** | λ°œμ‚° 없이 58회 |
| *κΈ°μ‘΄(λ²„κ·ΈνŒ) SRA* | *0.695* | μ™„μ£Όκ°’ |
| *baseline* | *0.703* | μ™„μ£Όκ°’ |
cap 3.0 κ³Ό 6.0 의 차이가 **0.0026** 에 λΆˆκ³Όν•˜λ‹€ β†’ 이 μ²˜λ°©μ€ cap 값에 λ―Όκ°ν•˜μ§€ μ•Šλ‹€
(ν•˜μ΄νΌνŒŒλΌλ―Έν„° νŠœλ‹μ— μ˜μ‘΄ν•˜λŠ” μž„μ‹œλ°©νŽΈμ΄ μ•„λ‹ˆλΌλŠ” κ·Όκ±°).
### λ‹€λ₯Έ μ„€μ •μ—μ„œμ˜ μž¬ν˜„
같은 처방(`EDGE_FIX + SOFT_START + RES_CAP=3.0`)으둜 돌린 MoFlow-NBA ablation 2건도
λ°œμ‚° 없이 ν•˜κ°• 쀑 β€” cap 의 νš¨κ³Όκ°€ full SRA ν•œ μ„€μ •μ—λ§Œ κ΅­ν•œλ˜μ§€ μ•ŠλŠ”λ‹€.
| μ‹€ν–‰ | 평가 수 | ν˜„μž¬ best |
|---|---|---|
| sparse selection (no Οƒ) | 16 | 0.8034 |
| encoding (all nbrs, no Οƒ) | 9 | 0.8648 |
---
## 7. ν•œκ³„μ™€ λ―Έν™•μ • (μ •μ§ν•˜κ²Œ)
**β‘  SOFT_START κ°€ κ΅λž€ λ³€μˆ˜λ‹€.** cap μ‹€ν–‰ 4건 λͺ¨λ‘ `SRA_SOFT_START=1` 을 ν•¨κ»˜ μΌ°λ‹€.
λ”°λΌμ„œ "cap λ‹¨λ…μœΌλ‘œ μΆ©λΆ„ν•œκ°€"λŠ” **아직 κ²€μ¦λ˜μ§€ μ•Šμ•˜λ‹€**. soft-start λŠ” λ‹¨λ…μœΌλ‘œλŠ”
μ‹€νŒ¨ν–ˆμœΌλ―€λ‘œ cap 이 핡심인 것은 λΆ„λͺ…ν•˜μ§€λ§Œ, λ‘˜μ˜ κΈ°μ—¬λ₯Ό λΆ„λ¦¬ν•˜λ €λ©΄
`RES_CAP` 만 μΌ  λŒ€μ‘° 싀행이 ν•„μš”ν•˜λ‹€.
**β‘‘ μ΅œμ’… μˆ˜λ ΄κ°’ λ―Έν™•μ •.** 39 % μ§€μ μ—μ„œ 0.737 이고, κΈ°μ‘΄(λ²„κ·ΈνŒ) 0.695 / baseline
0.703 보닀 아직 μœ„λ‹€. 남은 61 % 와 cosine LR κ°μ‡ μ—μ„œ 더 λ‚΄λ €κ°ˆ μ—¬μ§€λŠ” μžˆμœΌλ‚˜,
**0.695 λ₯Ό μž¬ν˜„ν•˜μ§€ λͺ»ν•  κ°€λŠ₯성도 μ—΄λ € μžˆλ‹€.**
**β‘’ κ·Έ 경우의 결둠도 보고할 κ°€μΉ˜κ°€ μžˆλ‹€.** "정상 씬-λ‚΄λΆ€ κ·Έλž˜ν”„λŠ” μ•ˆμ •μ μœΌλ‘œ ν•™μŠ΅λ˜λ‚˜
MoFlow 의 one-step flow matching μ—μ„œλŠ” 이득이 μ œν•œμ "μ΄λΌλŠ” 것 μžμ²΄κ°€, λ²„κ·ΈνŒμ˜
0.695 κ°€ **μ˜λ„ν•œ λ©”μ»€λ‹ˆμ¦˜μ΄ μ•„λ‹ˆλΌ μš°μ—°ν•œ μ •κ·œν™” 효과**μ˜€μ„ 수 μžˆμŒμ„ μ‹œμ‚¬ν•˜λŠ”
λ°œκ²¬μ΄λ‹€.
**β‘£ MID / LED λŠ” λ‹€λ₯Έ μ²˜λ°©μ„ μ“΄λ‹€.** RES_CAP 은 MoFlow μ „μš©μ΄λ‹€. MID λŠ”
`graph_gate_init` 0.01β†’0.001 + warmup 100β†’200 으둜 λΆ•κ΄΄λ₯Ό λ§‰μ•˜κ³ , LED λŠ” 별도
처방 없이 회볡 쀑이닀(LED sparse no-Οƒ λŠ” 이미 κΈ°μ‘΄κ°’ 0.886 β†’ **0.800** 으둜 μ•žμ„¬).
---
## 8. μž¬ν˜„
```bash
# MoFlow-NBA full SRA (ν˜„μž¬ μ‹€ν–‰ μ„€μ •)
SRA_EDGE_FIX=1 SRA_SOFT_START=1 SRA_RES_CAP=3.0 \
CUDA_VISIBLE_DEVICES=0 python fm_nba_graph_v6.py \
--cfg cfg/nba/cor_fm.yml --exp edgefix_cap3 \
--batch_size 192 --epochs 150 --fm_in_scaling --tied_noise \
--top_n_neighbors 5 --uncertainty_weight 0.01 --data_dir ./data/nba
# no-Οƒ ablation 은 μ „μš© 슀크립트 μ‚¬μš© (use_sigma_gating=False ν•˜λ“œμ½”λ”©)
SRA_EDGE_FIX=1 SRA_SOFT_START=1 SRA_RES_CAP=3.0 \
python fm_nba_graph_v6_nosigma.py --cfg cfg/nba/cor_fm.yml --exp ef_sparse_nosig \
--batch_size 192 --epochs 150 --fm_in_scaling --tied_noise --top_n_neighbors 5
```
| ν† κΈ€ | κΈ°λ³Έ | μ—­ν•  |
|---|---|---|
| `SRA_EDGE_FIX` | off | edge-index 씬 ν˜Όν•© 버그 μˆ˜μ • (**핡심**) |
| `SRA_RES_CAP` | 0 (off) | λ…Έλ“œλ³„ residual norm μƒν•œ β€” MoFlow μƒ˜ν”Œλ§ μ•ˆμ •ν™” (**핡심**) |
| `SRA_SOFT_START` | off | out_proj zero-init + gate bias βˆ’4 (보쑰, λ‹¨λ…μœΌλ‘œλŠ” μ‹€νŒ¨) |
| `SRA_GATE_SCALE` | 1.0 | 게이트 μ „μ—­ μΆ•μ†Œ (μ‹€νŒ¨ν•œ 처방, μ½”λ“œλ§Œ μž”μ‘΄) |