Start with hypothesis-driven isolation: reproduce the problem with minimal changes, then expand. Follow these steps in order (quick checks first, then deeper causes).1) Reproduce single-GPU baseline- Confirm same code, data, hyperparams converge on one GPU. Save model/optimizer state and a small training log for comparison.2) Verify deterministic settings & seeds- Set seeds globally on all nodes/processes (example PyTorch):python
import torch, random, numpy as np
seed=42
random.seed(seed); np.random.seed(seed)
torch.manual_seed(seed); torch.cuda.manual_seed_all(seed)
torch.use_deterministic_algorithms(True)
- Log seeds and RNG states per rank. If deterministic mode errors, note non-deterministic ops.3) Check nondeterminism sources- Inspect data loader workers, shuffling, augmentations, multiprocessing start method. Force worker_init_fn to set per-worker seeds. Disable asynchronous augmentation to test.4) BatchNorm issues- If using BatchNorm, try SyncBatchNorm vs local BN: - Test with sync off and very large batch per GPU to emulate single-GPU behavior. - If using SyncBatchNorm, ensure correct all-reduce implementation and that all processes participate each step. Log per-rank batch counts; mismatched counts break stats.5) Effective batch size & gradient accumulation- Compute effective batch size = per-GPU batch * num_GPUs / accumulation_steps.- Confirm learning rate scaling (linear scaling rule) and optimizer state identical. Check if gradient_accumulation loops match across ranks and that gradients are synchronized only when expected.6) Padding/packing inconsistencies- For variable-length inputs (NLP, audio), ensure padding masks, pack_padded_sequence, and bucketing are identical across ranks. Log input shapes per rank. Mismatched sequence lengths change BatchNorm/statistics and loss.7) Numerical precision and reduction errors- Compare float32 vs mixed precision (AMP) behavior. Disable AMP to test. Check different NCCL/blas versions across nodes; ensure same GPU drivers, CUDA, cuDNN, and framework builds. Enable deterministic reductions (use torch.distributed.reduce_op and check). Log gradient norms per rank to catch divergences early.8) Learning rate and optimizer divergence- Compare initial gradient norms, parameter norms, and loss per rank for first few steps. If divergence appears after an epoch, compare dataset sharding and epoch boundary handling (drop_last, shuffle seed per epoch).9) Instrumentation and bisecting- Insert checkpoints: after forward, after loss, after backward, after optimizer.step — serialize small tensors (loss, grad norm, param sums) per rank and diff them to find the first mismatch step.- Run two-process debug (rank 0 vs rank 1) with identical inputs to isolate communication errors.10) Remediation and final checks- Once root cause found: fix (seed propagation, correct SyncBN usage, consistent accumulation, align software stacks), retrain with logging. Add unit tests: deterministic tiny-training test, distributed sanity check comparing single-GPU run.Key rationale: differences usually arise from RNG/augmentation, mismatched batch statistics, or communication/order mismatches. Systematic logging and bisecting the training step will locate the first divergence.