Repository navigation
mednext: opt-in training speed knobs for ~2.6x faster affinity training - #222
Open
isWangjunjie wants to merge 2 commits into
Open
isWangjunjie wants to merge 2 commits into
isWangjunjie wants to merge 2 commits into
Conversation
Training-side speedups for MedNeXt affinity training. Every knob is opt-in; the only default change is optimization.benchmark: true in tutorials/banis.yaml. - Pass optimization.benchmark/deterministic to the Lightning Trainer (both fields existed but were never forwarded, so cudnn.benchmark was off). - optimization.optimizer.fused / merge_param_groups: fused Adam/AdamW on CUDA parameters and one param group per (lr, weight_decay). - configure_gradient_clipping clips fused optimizers directly when there is no GradScaler (Lightning refuses this even under bf16); a preflight rejects fused Adam/AdamW + clipping under fp16 GradScaler precision. - model.compile / compile_mode: in-place torch.compile in configure_model; MetaTensor batches are unwrapped only when compiling. - model.mednext_depthwise_impl=shift_sum (requires model.compile on CUDA): stride-1 depthwise Conv3d as shifted multiply-adds that inductor fuses, replacing the slow at::native conv_depthwise3d kernels; state_dict keys unchanged. - DDP: declared optimization.ddp_find_unused_parameters / ddp_static_graph (replaces an undeclared cfg.model read) and opt-in model.mednext_freeze_dummy_tensor, which lets MedNeXt without deep supervision or heads run with find_unused_parameters=False. - Always-on, equivalence-tested rewrites: host-sync-free vectorized PerChannelBCEWithLogitsLoss mean reduction (falls back to the per-channel loop for weight shapes it does not cover) and foreach EMA updates for same-dtype fp32/fp64 groups (low-precision groups keep the scalar path). 4xH200, BF16 MedNeXt-L, global batch 8: 0.600 -> 0.316 s/step with these knobs and 6 loader workers/GPU (cudnn.benchmark -27%, compile -20%, fused + merged groups -12%); 0.228 s/step together with shift_sum, the fast affine resample and 8 workers/GPU. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
data.augmentation.affine.fast_resample (default off) swaps RandAffined's MONAI Resample for NumbaAffineResample: 3D reflection-padded nearest/bilinear in float32 as single-pass numba kernels that mirror torch's CPU grid_sampler_3d, sharing source coordinates across the keys of one call. Output equals MONAI Resample(dtype=float32) voxel for voxel; ineligible inputs (non-CPU, autograd, non-float32 output, fp16/bf16 images, other modes/padding) fall back to MONAI. numba is imported only when the option is enabled. RandAffined was 45% of per-sample loader CPU in the MedNeXt-L affinity pipeline (393 -> 150 ms per call), which made 4xH200 training data-bound once the GPU side got faster. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Opt-in speedups for MedNeXt affinity training. On 4×H200 (BF16, MedNeXt-L, global batch 8) step time goes from 0.600 to 0.228 s/step (~2.6×), so a 200k-step run takes ~13 h instead of ~33 h. Every new knob defaults to off. The only default change is
optimization.benchmark: trueintutorials/banis.yaml.Commit 1: training knobs + shift-sum depthwise conv
optimization.benchmark/deterministicare now forwarded to the Lightning Trainer. Both fields existed but were never forwarded, so cudnn.benchmark was always off.optimizer.fused/merge_param_groupsadd fused Adam/AdamW and one param group per (lr, wd).model.compile/compile_modecompiles the model in place viaconfigure_model. MetaTensor batches are unwrapped only when compiling.model.mednext_depthwise_impl: shift_sum(requiresmodel.compileon CUDA) computes stride-1 depthwise Conv3d as shifted multiply-adds fused by inductor, replacing the slowconv_depthwise3dkernels. state_dict keys are unchanged.model.mednext_freeze_dummy_tensorlets MedNeXt run DDP withfind_unused_parameters=Falsewhen it has no deep supervision and no heads. The newoptimization.ddp_find_unused_parameters/ddp_static_graphfields replace an undeclaredcfg.modelread. Default DDP decisions are unchanged.PerChannelBCEWithLogitsLossmean reduction is vectorized and makes no host sync. Weight shapes outside the vectorized path use the old loop. If every weight is invalid, the returned zero now stays attached to the graph.Commit 2: numba fast resample for
RandAffineddata.augmentation.affine.fast_resamplereplaces MONAIResamplewith numba kernels for 3D reflection nearest/bilinear. Output equalsResample(dtype=float32)voxel for voxel.Benchmark (4×H200, 32 CPU, 30 warmup + 100 timed steps):
shift_sum,fast_resampleand 8 workers/GPU.With the faster GPU path, 2 workers/GPU leave ~20% of each step waiting on data, so use ≥6 workers/GPU.
A full 200k-step BANIS+ reproduction (BF16, 4×H200) with all of these knobs enabled scored test NERL (mt1) 60.04%. The published checkpoint scores 58.65%, and the recipe's reported range is 59.6 ± 1.1%.
Recommended for MedNeXt affinity training:
model.compile: truemodel.mednext_depthwise_impl: shift_sumoptimization.optimizer.fused: trueoptimization.optimizer.merge_param_groups: truedata.augmentation.affine.fast_resample: truebf16-mixedmodel.mednext_freeze_dummy_tensor: true(without deep supervision or heads)Test plan
test_train_speed_knobs.pytest_train_speed_loss.pytest_fast_resample.pytest_fast_resample_import.py: subprocesses with numba absent or broken.test_ddp_mednext_freeze.py: 2-process gloo DDP.set_sync_debug_mode("error")validate_tutorial_configs.py: no new failures vs origin/master. Existing failures come from theneuron_j0126tutorials and missingcloud-volume.🤖 Generated with Claude Code