nvalchemi.training.hooks.DDPHook#
- pydantic model nvalchemi.training.hooks.DDPHook[source]#
Wrap training models with
DistributedDataParallelat setup time.DDPHookis the standard way to make aTrainingStrategyrun data-parallel across ranks. It is a training hook bound toSETUP(via thestageclass attribute), so the strategy dispatches it once, before models are moved to their devices and before the training loop begins. In a single call it:initializes
torch.distributedwhenauto_initis set,WORLD_SIZE > 1, and no manager or process group has already established communication (typically fromtorchrunenvironment variables);reads rank/device metadata from
TrainingStrategy.distributed_managerwhen one is supplied, otherwise from the environment;wraps the selected models (
model_keys, or all models with optimizer configs whenmodel_keysisNone) intorch.nn.parallel.DistributedDataParallel, forwardingfind_unused_parameters,broadcast_buffers,static_graph, andprocess_group; andinjects a distributed sampler (
sampler_clswithsampler_kwargs, defaulttorch.utils.data.DistributedSampler) into the active dataloader so each rank sees a disjoint shard.
Register it by adding it to the strategy’s
hooks=[...]list. Whenworld_size <= 1the hook is effectively a no-op: no wrapping and no sampler rewrite occur, so the same script runs unchanged on a single process. On teardown the hook restores the original (unwrapped) models and, if it initialized the process group itself, destroys it.Examples
Enable data-parallel training by dropping the hook into the strategy’s hook list; launch the script with
torchrun:>>> import torch >>> from nvalchemi.training import ( ... EnergyMSELoss, OptimizerConfig, TrainingStrategy, default_training_fn, ... ) >>> from nvalchemi.training.hooks.ddp import DDPHook >>> strategy = TrainingStrategy( ... models=model, ... optimizer_configs=OptimizerConfig( ... optimizer_cls=torch.optim.Adam, optimizer_kwargs={"lr": 1e-3}, ... ), ... training_fn=default_training_fn, ... loss_fn=EnergyMSELoss(), ... num_epochs=10, ... devices=[torch.device("cuda")], ... hooks=[DDPHook()], ... ) >>> strategy.run(train_loader)
Wrap only specific models and forward DDP options, for example a CPU
gloorun that must allow unused parameters:>>> hook = DDPHook( ... model_keys=("main",), ... backend="gloo", ... find_unused_parameters=True, ... broadcast_buffers=False, ... )
Notes
When
world_size > 1but distributed communication has not been initialized, model wrapping raises aRuntimeError: either setauto_init=True(the default) so the hook callsinit_process_groupitself, launch undertorchrun, or pass an already-initializeddistributed_manager.find_unused_parametersandbroadcast_buffersleft asNoneinherit from the external manager when it exposes them, otherwise default toFalse. For nvalchemiDataLoaderobjects using the default sampler class, the hook installs a distributed batch sampler (MultiDatasetBatchSamplerfor aMultiDataset, otherwise aDistributedSamplerwrapped in aBatchSampler) rather than a sample-level sampler; dataloaders that already carry a distributed sampler are left untouched, and a pre-existing non-distributedbatch_sampleris rejected. Missingnum_replicas,rank,shuffle,seed, anddrop_lastare inferred from the manager and dataloader before usersampler_kwargsare applied. Only dataloaders exposing asamplerattribute are rewritten; arbitrary iterables are left as caller-managed inputs.- field model_keys: tuple[str, ...] | None = None#
Named models to wrap.
Nonewraps all models that have optimizer configs.
- field find_unused_parameters: bool | None = None#
Forwarded to
DistributedDataParallel.Noneuses the external manager’s setting when present, otherwiseFalse.
- field broadcast_buffers: bool | None = None#
Forwarded to
DistributedDataParallel.Noneuses the external manager’s setting when present, otherwiseFalse.
- field static_graph: bool = False#
Forwarded to
DistributedDataParallel.
- field process_group: Any | None = None#
Explicit process group. Defaults to a process group exposed by the external distributed manager or PyTorch’s default group.
- field backend: str | None = None#
Backend used when this hook initializes
torch.distributed.
- field auto_init: bool = True#
If
True, initializetorch.distributedwhenWORLD_SIZE > 1and no manager/process group has already initialized communication.
- field sampler_cls: Callable[[...], Any] = <class 'torch.utils.data.distributed.DistributedSampler'>#
Sampler class or factory used for supported dataloaders. The callable is invoked as
sampler_cls(dataset, **sampler_kwargs). The default istorch.utils.data.DistributedSampler.
- field sampler_kwargs: dict[str, Any] [Optional]#
Keyword arguments forwarded to
sampler_cls. For the defaultDistributedSamplerand sampler callables that accept PyTorch’s distributed sampler keywords, missingnum_replicas,rank,shuffle,seed, anddrop_lastvalues are inferred from the manager and dataloader before user-provided kwargs are applied.