feat: optional multi-GPU (DDP) finetuning#21
Conversation
Adds a DistributedDataParallel launcher for finetuning, mirroring the existing pretraining DDP path so users can accelerate finetunes across multiple GPUs. - finetune_ddp.py: one-process-per-GPU launcher (mp.spawn, WORLD_SIZE env, NCCL topology auto-config, per-rank determinism), routed through cross_validate -> run_training. - kermt/util/ddp_utils.py: shared ddp_setup + configure_nccl_for_topology so the pretrain and finetune launchers share one bootstrap. - run_training / train (task/train.py): thread rank/world_size; DDP-wrap the model (find_unused_parameters=True, since the finetune model carries params the FFN forward does not exercise); DistributedSampler + set_epoch on the train loader; run validation on the unwrapped module over the full val set on every rank (consistent best-model selection); gate checkpoint saving, logging and test-set evaluation to rank 0; all-reduce the standalone MTLLoss.log_sigma gradient. Also use a distinct train_loader instead of reassigning train_data, which fixes the ensemble_size>1 DataLoader double-wrap. - cross_validate: thread rank/world_size; gate wandb + reporting to rank 0. - build_lr_scheduler: world-size-aware steps_per_epoch. Single-process finetune (python main.py finetune ...) is unchanged: DDP only activates when a process group is initialized (i.e. via finetune_ddp.py). Batch size is per-GPU; effective global batch = batch_size * world_size. Signed-off-by: Eva Xue <evax@nvidia.com>
Adds a --num-gpus option to the finetune runner and documents it in the skill. Default is 1 (single-process `main.py finetune`, unchanged). With --num-gpus N (N>1) the runner launches finetune_ddp.py with WORLD_SIZE=N instead, for data-parallel training; --batch-size is per-GPU. - agent/scripts/run_finetune_local.py: --num-gpus arg; _build_argv selects main.py vs finetune_ddp.py; set WORLD_SIZE in the exec + replay env; skip the single-device --gpu flag when distributed (each rank pins its own device); record num_gpus/distributed in run.json. - agent/skills/kermt-finetune/SKILL.md: document --num-gpus across the hardware, arguments, invocation-template, and error-hint sections. Signed-off-by: Eva Xue <evax@nvidia.com>
|
I wonder if we should have only 1 entrypoint for finetuning. Currently, we have two |
sveccham
left a comment
There was a problem hiding this comment.
Thanks for the DDP implementation. Overall, useful useful feature to have. I have left comments in-line.
| "passing '0,1' is rejected with a clear error.") | ||
| help="Single GPU id for single-process finetune (default 0). " | ||
| "Ignored when --num-gpus > 1 (DDP uses ranks 0..N-1).") | ||
| p.add_argument("--num-gpus", type=int, default=1, |
There was a problem hiding this comment.
There is a similar argument --gpus elsewhere. I think that one is mostly ignored. If so, we can remove that arg.
| # from the loaded pretrain checkpoint that the FFN task head does not use). | ||
| # DDP would otherwise error ("expected to have finished reduction ..."). | ||
| if is_distributed: | ||
| model = DDP(model, device_ids=[rank], find_unused_parameters=True) |
There was a problem hiding this comment.
Why do we need find_unused_parameters=True. I think there might be a a situation where we are not using some views from prertaining. It would be good to delete those. I am scared that this param would suppress some genuine bugs when params are not being trained correctly.
Summary
Adds opt-in data-parallel (DistributedDataParallel) finetuning so finetunes can be accelerated across multiple GPUs, mirroring the existing pretraining DDP path. Single-GPU finetuning is unchanged and remains the default — DDP only
engages through a dedicated launcher.
What's new
Core (
feat(finetune): add optional multi-GPU DDP training)finetune_ddp.py: a one-process-per-GPU launcher (torch.multiprocessing spawn,WORLD_SIZEenv, NCCL topology auto-config, per-rank determinism), routed through the normalcross_validate -> run_trainingfinetune path.kermt/util/ddp_utils.py: sharedddp_setup+configure_nccl_for_topologyso the pretrain and finetune launchers use one DDP bootstrap.task/train.py(run_training/train): threadrank/world_size; wrap the model in DDP (find_unused_parameters=True, since the finetune model carries parameters the FFN forward does not exercise); shard the train loader with aDistributedSampler(+set_epoch); run validation on the unwrapped module over the full val set on every rank so best-model selection stays consistent; gate checkpoint saving, logging and test-set evaluation to rank 0; all-reduce the standaloneMTLLoss.log_sigmagradient. Also uses a distincttrain_loaderinstead of reassigningtrain_data, fixing anensemble_size>1DataLoader double-wrap.cross_validate: threadrank/world_size; gate W&B + reporting to rank 0.build_lr_scheduler: world-size-awaresteps_per_epoch.Skill (
feat(kermt-finetune skill): expose optional multi-GPU DDP finetune)agent/scripts/run_finetune_local.py: new--num-gpus Noption. Default 1 (single-processmain.py finetune, unchanged);N>1launchesfinetune_ddp.pywithWORLD_SIZE=N. Under DDP it omits the single-device--gpuflag and recordsnum_gpus/distributedinrun.json.agent/skills/kermt-finetune/SKILL.md: documents--num-gpusin the hardware, arguments, invocation-template, and error-hint sections.Design
finetune_ddp.py).python main.py finetune ...and--num-gpus 1behave exactly as before.--batch_sizeis per process; effective global batch isbatch_size * WORLD_SIZE. The LR schedule is world-size-aware, so the same--batch_sizecan be reused as GPUs are added.Usage
Testing
main.py finetune) runs unchanged end-to-end (2-epoch regression finetune from a pretrained checkpoint): trains, selects best by validation, saves checkpoints, evaluates on test.finetune_ddp.py,WORLD_SIZE=1): full DDP wrap, DistributedSampler +set_epoch, unwrapped-module eval, rank-0 checkpoint/test — completes with comparable validation/test metrics to the single-process run.--num-gpus 1dispatches tomain.py finetune;--num-gpus N(N>1) dispatches tofinetune_ddp.pywithWORLD_SIZE=N(verified by dry-run command inspection and an actual launch). Existing finetune-runner unit tests (19) pass.