demo_clean 50 ep×50 task(2,500 demo)만 학습 후 c2c/c2r 평가.optimizer_path 유령 상속(이전 run 가중치를 조용히 이어받는 함정) 발견·제거, (2) NCCL hang(rank6 CPU stall) 원인 특정 후 timeout 1000→5400s, (3) teardown exitcode 120을 실패로 오판하는 재시도 래퍼 버그 수정.지금까지 RoboTwin 실험(run1/run2)은 aloha-agilex_randomized_500(50 task × 500 ep = 25,000 ep)으로 학습했다.
X-WAM(90.7% 보고)과 동일 세팅이라 X-WAM 직접 비교에는 유효하지만, WebFetch로 확인한
robotwin-platform.github.io/leaderboard 공식 규격은 다르다: 학습은 demo_clean 50 ep × 50 task = 2,500 demo만,
평가는 task당 100 trial을 clean/randomized 양쪽에서 실행해 c2c/c2r/평균 3개 지표를 낸다.
optimizer_path 유령 상속 (제출 후 시작 전 수정)runner/posttrain.py:904가 첫 체크포인트 저장 시 config YAML을 직접 덮어써서
optimizer_path: <save_folder>/latest_accelerator_state를 자동 기입한다(preempt 후 자기 재개용).
v4를 run1 config를 복사해서 만들었기 때문에 run1 경로가 그대로 딸려 왔고, posttrain.py:597-599는
이걸 무조건 accelerator.load_state()로 로드한다 — model/optimizer/scheduler/RNG를 전부 복원.
tmp/posttrain_robotwin_v4.yaml.bak에 백업. 재개 기능 자체는 유지(step 3,000에서 runner가 v4 자신의 경로를 다시 기입).16:42:59 시작 → 17:03:20 종료, sacct State=COMPLETED, ExitCode=0:0(PREEMPTED가 아니라서 sbmr 자동 재큐도 안 걸림).
TORCH_NCCL_TRACE_BUFFER_SIZE=2000 + DUMP_ON_TIMEOUT=1 덤프(tmp/nccl_trace/99963_rank_*)로 원인 특정:
| rank | 기록된 collective 수 | 마지막 상태 |
|---|---|---|
| 0,1,2,3,4,5,7 | 139개 | seq 139 all_reduce, scheduled |
| rank 6 | 47개 | seq 47 broadcast, completed |
rank 6만 seq 48을 enqueue조차 못함 — enqueue는 CPU 측 동작이라 Python/CPU(데이터로더 또는 파일 I/O)에서 stall됐다는 뜻. 자원 할당(AllocTRES 동일)·dataloader 설정·데이터 손상(2,500 hdf5 전수 개봉 40초 완주, 실패 0)·코드 문제(run1/run2 같은 코드로 완주)를 전부 배제 → n34의 일시적 stall로 결론(재현되는 결함 아님).
시작 시점 검증(16:43 KST, n34): Successfully load optimizer state 0회(run1 상태 미로드 확인),
mismatched_keys 전 rank [](pretrained 전량 로드), step 1 loss_action=0.422 — run1 종료 시점 action loss(~0.031)와
다르고 pretrained 초기값(run1 초기 0.62와 같은 계열)과 일치해 optimizer_path 수정이 실제로 먹었음을 확인.
학습이 step_30000 저장까지 끝난 뒤, teardown 단계에서 rank 5가 exitcode 120으로 죽었다
(전 rank에 Exception ignored in sys.unraisablehook — Python finalization 에러). torchrun이 이를 실패로 보고 →
train_retry.sh가 27,000~30,000 구간을 무의미하게 재실행(attempt 2)했다.
train_retry.sh가 런처 출력을 tmp/train_retry_attempt<N>.log로 tee하고,
max train step reached 마커가 있으면 exit code와 무관하게 성공 처리하도록 가드 추가(PIPESTATUS[0]로 rc는 별도 보존).
attempt 2는 29,801 step에서 scancel — 저장 창(30,000) 진입 전이라 attempt 1의 step_30000(02:13:55)은 손상 없이 보존됨.job 100337 완주: 30000/30000 [8:52:10, 1.07 s/it], >>> max train step reached, 재시도 0회(attempt 1에서 완료).
| step | 2,698 | 6,003 | 9,360 | 12,739 | 16,152 | 19,582 | 22,973 | 26,366 | 29,748 |
|---|---|---|---|---|---|---|---|---|---|
| loss_action | 0.0489 | 0.0446 | 0.0181 | 0.0171 | 0.0424 | 0.0171 | 0.0183 | 0.0188 | 0.0137 |
optimizer_path 자동 주입은 이 레포의 조용한 함정이다 — config를 복사해 새 run을 만들 때마다
optimizer_path/latest_global_step/latest_log_dir을 반드시 지워야 하고, 안 지우면 로그상으로는
정상으로 보이면서 이전 run의 연장이 된다. 분산 학습 래퍼에서 exit code를 성공 판정에 쓰면 안 된다는 것도 확인됐다 —
NCCL 행은 sacct COMPLETED 0:0으로, 정상 완료는 exitcode 120으로 나타나 둘 다 반대로 읽힌다.
이 run4가 나와야 비로소 "τ₀-WM을 리더보드 규칙대로 돌리면 몇 점인가"에 답할 수 있다.
학습 loss만으로는 리더보드 성능을 판단할 수 없다 — clean-50 단독 학습이 randomized 평가로 전이되는지는 별도의 c2c/c2r 평가가 필요하다(진행 중, 다음 카드).
step_30000 체크포인트로 TASK_CONFIG=demo_clean(c2c) / demo_randomized(c2r) 각 50 task × 100 trial 평가 제출
(job 101482, 101483) → 리더보드 3개 지표 산출.