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 κ°€ 씬을 λ’€μ„žμŒ

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)

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 만 μž˜λΌλ‚Έλ‹€.

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. μž¬ν˜„

# 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 게이트 μ „μ—­ μΆ•μ†Œ (μ‹€νŒ¨ν•œ 처방, μ½”λ“œλ§Œ μž”μ‘΄)