resume_from이 transformer weight만 복원하고 optimizer/step 상태는 저장하지 않아, requeue될 때마다 step counter가 0부터 다시 시작 — checkpoint 디렉토리명(checkpoint_step_N)에서 N을 역산해 이어붙이도록 수정furniture_all_e128 8-GPU 잡이 --qos=extra로 제출돼 이번 주에만 5회 이상 preempt — 버그가 재현되기 쉬운 조건이었음furniture_all_e128 config(8-GPU, effective batch 128) 학습이 --qos=extra로 제출돼 다른 사용자의 own 잡에 의해 자주 preempt됐다. sacct 기준 이 config의 학습 잡 체인만 봐도 111321(CANCELLED, 0s) → 111322(PREEMPTED, 7h12m) → 111606(PREEMPTED, 23m) → 111700(PREEMPTED, 2h28m) → 111859(15m만에 종료) → 111882(진행 중)로 6회 연속 재시작됐다. 매번 scripts/lingbot/train_lingbot.sh가 최신 checkpoint를 LINGBOT_RESUME_FROM으로 넘겨 재시작하는 구조인데, 이 재시작 경로 자체에 버그 2개가 숨어 있었다.
resume_from이 주석 처리된 코드로만 존재), 이 프로젝트가 own 잡이 아닌 extra QOS로 장시간 학습을 도는 순간 두 버그가 즉시 표면화됐다.wan_va/train.py)Upstream 코드는 resume_from이 있어도 transformer weight만 로드하고(_load_training_state는 주석 처리) step/optimizer 상태를 복원하지 않는다. 그 결과 requeue된 런이 self.step=0부터 다시 카운트하면서 checkpoint_step_200처럼 기존 checkpoint를 덮어쓸 수 있는 번호로 저장을 재개했다. Trainer.__init__에 정규식으로 resume_from 경로 끝의 checkpoint_step_(\d+)를 파싱해 self.step을 그 값으로 초기화하는 패치를 추가 — optimizer/scheduler 상태 복원 없이도 최소한 step 번호와 num_steps 총 예산, checkpoint 파일명의 단조 증가는 보장했다.
wan_va/distributed/util.py)init_process_group이 PyTorch 기본값(10분) timeout으로 호출되고 있었다. 8-rank FSDP가 NAS에서 checkpoint(furniture_all_e128의 transformer, 9.5GB)를 로드할 때 rank마다 로드 완료 시점이 갈리는데, 먼저 끝난 rank가 첫 FSDP all-gather에서 대기하다 600초를 넘기면 NCCL watchdog이 job 전체를 abort시켰다 — 실제로 job 111859가 이 경로로 조기 종료(05:45~06:00, 15분 만에 프로세스 종료)됐다. timeout=datetime.timedelta(minutes=60)로 연장.
| Job | State | Elapsed | 비고 |
|---|---|---|---|
| 111322 | PREEMPTED | 7:12:08 | 버그 1 패치(09-04 13:37)는 이미 반영된 상태 — resume 시 step 1000에서 정상 이어짐 |
| 111606 | PREEMPTED | 0:23:01 | 재시작 직후 재차 preempt |
| 111700 | PREEMPTED | 2:27:54 | |
| 111859 | COMPLETED(비정상 조기 종료) | 0:14:58 | NCCL 10분 timeout으로 8-rank all-gather 중 abort — util.py 패치의 직접 계기(주석에 job ID 명시) |
| 111882 | RUNNING | 2:14:01+ | 60분 timeout 패치 적용 후 첫 실행, 진행 중 |
버그 1 수정 효과는 wandb/ 로그의 "Continuing step counter from checkpoint name: step N" 라인으로 직접 확인됨 — checkpoint_step_750→step 750, checkpoint_step_1000→step 1000으로 재시작 후에도 정확히 이어짐(재시작 3회 모두 재현). 같은 구간 loss: latent_loss 0.085→0.077, action_loss 0.0157(step 750)→0.0157(step 1232), 정체 없이 계속 기록됨.
Upstream LingBot-VA 코드는 안정적인 own QOS 장시간 점유를 전제로 짜여 있어, 이 클러스터의 extra QOS(preemption 상시 발생) 환경에 옮기자마자 resume 관련 잠재 버그 2개가 즉시 발현됐다. 두 수정 모두 근본 원인(optimizer 상태 미저장, NCCL timeout 기본값)을 없애지는 못하고 증상을 우회하는 패치이지만, 실제 학습 진행(loss 하강, checkpoint 번호 단조 증가)이 preemption 사이클을 버티고 이어지게 만든 것 자체가 이번 주 FurnitureBench 학습을 가능하게 한 전제 조건이었다.
optimizer/scheduler 상태 자체는 여전히 checkpoint에 저장되지 않음 — resume 시 Adam의 momentum/variance가 초기화되므로, 매 preemption마다 사실상 warm-restart에 가까운 손실 스파이크 위험이 남아 있다(현재 로그로는 뚜렷한 spike는 미관측이지만 loss curve를 step 단위로 뜯어보지 않아 확정 불가).
111882가 완주하거나 다음 preemption을 맞으면 60분 timeout이 실제로 abort를 막는지 재확인. 이후 optimizer state를 checkpoint에 포함시키는 패치(_load_training_state 활성화)를 우선순위로 검토 — 특히 e128 레시피처럼 lr 3e-5 + warmup 200으로 민감한 스케줄일수록 optimizer 리셋의 영향이 커질 수 있음.