save_top_k=0의 실패 모드save_top_k=0이라 사본이 하나뿐이었다. ZeRO는 전 shard가 필요하므로 4,479 step(~5시간) 복구 불가.extra QOS에서 이 설정은 preempt 1회당 유일본을 잃을 확률이 무시할 수준이 아니다.save_top_k 기본값 0→1 (+1.7h/+96GB). PENDING job을 취소하지 않고 스크립트만 고쳐 13시간치 queue age를 지켰다.ab_depth / ab_pm는 depth-vs-pointmap A/B의 두 arm이고 --qos=extra로 돈다. 이 클러스터에서 preempt는 예외가 아니라 기본값이라, sbmr 자동 재제출과 save_interval=500을 이미 걸어뒀다. 대비는 해뒀다고 생각했다.
08-09 오후 두 arm이 연달아 preempt됐고, 13시간이 지나도 큐에서 움직이지 않았다. 진행 상황을 확인하다가 ab_depth의 체크포인트가 읽을 수 없는 상태인 걸 발견했다.
gate_base.sh 헤더의 논리에 구멍이 있었다. save_top_k=0을 비용으로 정당화하고 있었다 — 저장 1회가 96 GB / ~2.7분인데 save_top_k=1이면 192 GB / ~5분 20초. 맞는 계산이지만 사본이 하나뿐이라는 사실이 빠져 있었다.
첫 단서는 파일 mtime이었다. ab_depth는 rank_0만 12:55이고 나머지는 13:26. 크기도 rank_5/6이 다른 shard와 달랐다.
torch는 zip 아카이브로 저장하므로 zipfile.ZipFile()이 end-of-central-directory 레코드만 읽어 잘림을 즉시 잡는다 — 96 GB를 로드할 필요가 없고 밀리초면 끝난다.
| 파일 | ab_depth (76102) | 판정 | ab_pm (75912) | 판정 |
|---|---|---|---|---|
| rank_0 optim | 10.01 GB | OK | 10.01 GB | OK |
| rank_1 optim | 10.01 GB | OK | 10.01 GB | OK |
| rank_2 optim | 10.01 GB | OK | 10.01 GB | OK |
| rank_3 optim | 10.01 GB | BadZipFile | 10.01 GB | OK |
| rank_4 optim | 10.01 GB | BadZipFile | 10.01 GB | OK |
| rank_5 optim | 6.67 GB | 잘림 | 10.01 GB | OK |
| rank_6 optim | 6.67 GB | 잘림 | 10.01 GB | OK |
| rank_7 optim | 10.01 GB | BadZipFile | 10.01 GB | OK |
| model_states | 11.78 GB | 잘림 | 38.89 GB | OK |
use_depth=true, 같은 base, 같은 레시피). model_states가 38.89 GB여야 하는데 11.78 GB라는 건 해석의 여지가 없다. ZeRO는 모든 shard가 있어야 로드되므로 멀쩡한 3개는 쓸모가 없다.
저장이 grace의 절반을 차지한다. 그 사이에 preempt가 들어오면 last.ckpt는 새 데이터로 덮어써지다가 중단되고, 이전 상태는 이미 사라진 뒤다. ab_depth는 5회 preempt 중 정확히 이 창에 걸렸다.
운이다. 같은 종류의 preempt였지만 저장 중이 아니었을 뿐이다. 두 arm의 차이는 설정이 아니라 타이밍이고, 그게 바로 이 설정이 위험한 이유다 — 실패가 확률적이라 몇 번 무사히 넘어가면 안전해 보인다.
13시간 대기의 원인은 다른 사용자가 아니었다. AssocGrpGRES, core 계정 70/72이고 그 중 17장이 전부 내 것이었다.
| job | GPU | QOS | 경과 / 남은 시간 |
|---|---|---|---|
cosmos_armA | 4 | own | 18h 36m / 6d 05h |
cosmos_armB | 4 | own | 18h 34m / 6d 05h |
rc24_D (flux2 4b) | 8 | extra | 1h 18m / 22h |
ev_armA_15000 | 1 | extra | 27m / 3h 33m |
own QOS로 6일 예약을 걸면 preempt는 면하지만 같은 계정의 다른 실험이 들어올 자리도 같이 막는다. cosmos 두 개가 8장을 6일간 점유하는 동안 A/B 두 arm(각 8장)은 대기다.
결정: 아무것도 내리지 않고 대기. rc24_D가 ~22시간 뒤 끝나면 슬롯이 난다.
CORRUPT_last.ckpt.76102로 이동. 그대로 두면 재개 시 crash → sbmr 재제출 루프로 13시간 기다려 얻은 슬롯을 날린다.
step 체크포인트가 last.ckpt와 별도로 남으므로, 찢어진 last가 있어도 최대 500 step 뒤처진 로드 가능한 사본이 남는다.
train_ab.sh는 worker가 launch 시점에 읽고, 제출 라인에 AB_SAVE_TOP_K가 없어서 기본값이 그대로 적용된다. 취소·재제출했으면 13시간치 queue age를 버렸을 것이다.
구버전 tmp/ab_watch.sh는 step 정체를 51회 잡아냈지만 로그 파일에만 썼고, 그동안 체크포인트가 죽어 있는 건 아무도 몰랐다. 새 버전은 매 사이클 zip 무결성을 검사하고, 새 job이 뜨면 [resume] 라인을 출력한다.
ab_pm이 11.5k가 아니라 0부터 다시 시작하면 몇 시간 동안 완벽히 정상적인 학습처럼 보인다. 손실을 발견했을 땐 이미 늦다.
100-step 스모크에서 pointmap arm의 depth_loss가 depth arm의 1.76배로 나왔고, 나는 이걸 "구조적"이라고 설명했다 — sim depth는 매끈한 grayscale이라 image VAE가 쉽게 처리하는 반면 pointmap은 chroma를 세 축에 나눠 쓴다는 것. 실제 학습이 이를 반박한다.
| step 구간 | depth | pm | pm/depth |
|---|---|---|---|
| 0 – 1k | 0.1715 | 0.2136 | 1.25× |
| 1k – 2k | 0.0822 | 0.0763 | 0.93× |
| 2k – 3k | 0.0702 | 0.0615 | 0.88× |
| 3k – 4k | 0.0667 | 0.0566 | 0.85× |
2k–4.16k 매칭 108개 로그 지점 기준 pm 0.0587 vs depth 0.0682, t = −40. 포인트맵 arm이 gradient를 더 먹는 게 아니라 덜 먹는다. 1.76×는 초기 transient였고, 스모크가 1 GPU / batch 1이었던 것도 겹쳤다.
| loss | depth | pm | Δ | t |
|---|---|---|---|---|
| video | 0.1748 | 0.1741 | −0.0007 | −2.9 |
| action | 0.0631 | 0.0635 | +0.0004 | 0.4 |
| proprio | 0.0471 | 0.0452 | −0.0018 | −2.0 |
video_loss 차이 0.4%는 통계적으로 잡히지만 크기가 무의미하고, 애초에 depth 브랜치가 trunk를 공유하니 0일 수 없다 — 그 커플링이 실험 대상 자체다. action_loss는 아직 차이 없음(t=0.4) → 4k step 시점에 SR을 예측할 신호는 없다.
extra QOS에서 save_top_k=0은 체크포인트가 없는 것과 같은 실패 모드를 갖는다. 저장 윈도우가 grace 윈도우의 절반을 차지하므로 preempt 1회당 유일본을 잃을 확률이 무시할 수준이 아니다. +1.7시간은 5시간 손실 앞에서 명백히 싼 보험이다.
own 장기 예약은 스스로의 큐를 막는 형태로 되돌아온다.
| 항목 | 상태 |
|---|---|
rc24_D 종료 후 A/B 슬롯 확보 (~22h) | 대기 |
ab_pm step ~11,500 재개 → 20k 완주 | 대기 |
ab_depth step 0 재시작 → 20k | 5h 손실 확정 |
gate_base.sh도 같은 save_top_k=0 문제 — arm D 진행 중이라 판단 필요 | 미결 |
원자적 저장(임시 경로 → rename)이 근본 수정. save_top_k=1은 확률만 낮춘다 | 보류 |
arm B의 24번째 task(PnPCounterToStove) 평가 | 미착수 |