default_collate가 불가능해 batch_size=1이 강제였던 구조를, 길이 버킷 배치 샘플러 + zero-padding + valid_frames 마스킹으로 GPU당 실배치 4까지 확장.FlexAttnFunc의 attention과 compute_loss 양쪽에서 seq_id=-1 / frame_valid mask로 완전히 격리 — gradient에 기여하지 않음을 코드 구조로 보장.furniture_all_e128_pack(8GPU, 배치4×accum4) 실측 24.3s/it가 furniture_all_train(4GPU, 배치1×accum10, non-pack) 19.3s/it보다 오히려 느림 — GPU 수가 다르지만, 최소한 packing이 처리량을 즉각 끌어올리지는 못했음.
FurnitureBench all_low 데이터셋의 에피소드는 latent 프레임 수가 에피소드마다 다르다
(furniture_all_e128_pack 로그 실측: 103~362 프레임). PyTorch의 default_collate는
서로 다른 길이의 텐서를 하나의 배치 텐서로 쌓을 수 없어서, 기존 va_furniture_all_train_cfg.py는
"batch_size는 반드시 1이어야 한다"는 제약을 코드 주석으로 명시하고 있었다
("the dataset returns whole episodes of variable latent length (120-166 frames), so default_collate cannot stack them").
그 결과 effective batch를 128까지 끌어올리는 유일한 수단이 gradient_accumulation_steps를 16까지 늘리는 것뿐이었다
(furniture_all_e128_train: batch 1 × accum 16 × 8 GPU = 128). 매 micro-step이 배치 크기 1짜리 forward/backward라,
GPU 한 번에 처리하는 텐서가 작아 실제 배치 병렬화 이득 없이 16번을 순차적으로 도는 구조였다.
batch_size=1 고정 → GPU가 한 번에 에피소드 하나만 처리, accum만 늘려서는 커널 하나당 연산량이 늘지 않아 배치화의 실질적 이득(커널 launch 오버헤드 상각, 텐서 코어 활용도 상승)을 못 얻음.변수 길이 에피소드를 실제로 배치화하기 위해 3개 파일에 걸쳐 packing 메커니즘을 새로 구현했다.
config.batch_size > 1일 때만 packing 경로로 분기(기존 batch=1 경로는 그대로 유지, 하위 호환).
compute_loss에서 valid_frames로부터 frame_valid 마스크를 만들어
latent_loss / action_loss 평균 계산에서 padding 프레임을 제외했다.
추가로 LINGBOT_PROFILE=<n> 환경변수 기반 프로파일링 훅을 넣었다 — micro-step당 video/action/text
토큰 수, fwd+bwd 시간(s), peak memory(GB)를 출력하고, 마지막 profiled micro-step에서는 torch.profiler로
self-time 기준 top-14 CUDA 커널 테이블을 출력한다. packing 도입 후 실제로 어떤 attention 커널(FlexAttention/Triton vs
Flash)이 선택되는지, GPU가 얼마나 활용되는지 확인하기 위한 도구다.
packing의 정확성은 attention 마스킹에 달려 있다. valid_frames 인자를 추가해, padding 프레임에 해당하는
토큰의 seq_id를 -1로 설정 — FlexAttention이 동일 seq_id끼리만 attend하는 구조이므로,
-1인 토큰은 query·key 양쪽에서 실질적으로 attention 밖으로 밀려난다(다른 에피소드로의 leak도 함께 차단).
batch_size=4/GPU × accum=4 × 8 GPU = effective 128 — 기존 furniture_all_e128_train
(batch 1 × accum 16 × 8 GPU = 128)과 동일한 effective batch를 유지하되, 이번엔 GPU당 4개 에피소드를 실제로 한 forward에
묶어 처리한다. pad_frames_multiple=8, bucket_pool_factor=50는 config 기본값.
| Run | GPU | batch × accum | Eff. Batch | s/it (실측) | 비고 |
|---|---|---|---|---|---|
furniture_all_train (no-pack) | 4 | 1 × 10 | 40 | 19.34s | step 7500 부근, 기존 baseline |
furniture_all_e128_pack (pack) | 8 | 4 × 4 | 128 | 24.26~24.32s | step ~1800 부근, packing 적용 |
학습 로그로 packing 메커니즘 자체는 정상 동작이 확인됐다:
"packing: batch 4/GPU, pad_frames_multiple=8, 40 batches/epoch/rank, latent F range 103-362".
furniture_all_e128_pack은 preemption으로 두 차례(1z8ijh3z → hp3o4tam) 재개된 끝에 step 2999/3000까지
완주했고, 최종 wandb summary는 avg_action_loss 0.0112 / avg_video_loss 0.0616 / grad_norm 0.12
(누적 runtime ≈ 8.3시간, requeue 구간 포함).
pad_frames_multiple 단위(8 latent frame)마다 F 조합이 바뀔 때 FlexAttention/inductor가 매번 recompile(코드 주석 근거: 4~6초/회)되는 오버헤드가 유력한 후보이나, 아직 LINGBOT_PROFILE을 실제로 돌려 확인하지는 못했다.
packing은 "batch_size=1 강제"라는 근본 제약을 풀어 진짜 배치 병렬화를 가능하게 한 인프라 개선이고,
attention·loss 마스킹의 정확성도 코드 구조로 보장됐다. 다만 이번 구현에서 "같은 effective batch를 더 적은
micro-step으로 달성하면 학습이 빨라질 것"이라는 가설은 아직 뒷받침되지 않았다 — accum 감소(16→4)로 얻은 이득이
packing 자체의 오버헤드(길이 편차가 큰 pool 정렬, F 변경마다의 recompile)에 상쇄됐을 가능성이 있다.
즉 packing은 "필요조건"(가변 길이를 실배치로 처리 가능)은 만족시켰지만, "충분조건"(처리량 개선)은 아직 증명되지 않은 상태다.
packed run(8GPU)과 non-pack baseline(4GPU)의 GPU 수가 달라 s/it 비교가 confound됨 — 같은 GPU 수(8) 조건에서
furniture_all_e128_train(non-pack, 8GPU)과 furniture_all_e128_pack(pack, 8GPU)의 s/it를
직접 비교한 데이터가 로컬 로그에 없다.
(1) 동일 GPU 수(8)에서 non-pack e128_train vs pack e128_pack의 s/it를 직접 비교. (2) LINGBOT_PROFILE=3으로
실제 프로파일을 떠서 top CUDA 커널이 FlexAttention/Triton인지 Flash인지, GPU peak memory와 fwd+bwd 시간을 확인.
(3) pad_frames_multiple을 8→16/32로 늘려 F 조합 recompile 빈도 자체를 낮췄을 때 s/it 변화 관찰.