nemo-mbridge-perf-cpu-offloading

Par nvidia · skills

Valide et utilise le déchargement CPU dans Megatron Bridge, notamment le déchargement des activations au niveau des couches et le déchargement fractionnel des états de l'optimiseur avec HybridDeviceOptimizer.

npx skills add https://github.com/nvidia/skills --skill nemo-mbridge-perf-cpu-offloading

Déchargement CPU

Références

  • Docs stables : @docs/training/cpu-offloading.md
  • Métadonnées structurées : @skills/nemo-mbridge-perf-cpu-offloading/card.yaml

Description

Deux mécanismes indépendants pour déplacer les données de la mémoire GPU vers la mémoire CPU :

Mécanisme Espace de config Ce qui est déchargé Restriction PP
Déchargement d'activations model.cpu_offloading* Activations (et optionnellement poids) par couche transformer PP doit être 1
Déchargement d'optimiseur optimizer.optimizer_cpu_offload États d'optimiseur Adam (momentum + variance) via HybridDeviceOptimizer Aucune

Prise de décision rapide

Situation Recommandation
Grand modèle MoE (30B+), PP > 1 nécessaire Déchargement d'optimiseur — déchargement d'activations bloqué par PP=1
Petit/moyen modèle, PP=1 adapté, mémoire d'activation dominante Déchargement d'activations
Compromis mémoire-vitesse ajustable souhaité Déchargement d'optimiseur avec optimizer_offload_fraction fractionnaire
Débit prioritaire Ne pas activer — déchargement ajoute toujours une surcharge
CUDA graphs nécessaires Déchargement d'optimiseur uniquement — déchargement d'activations incompatible
Pression mémoire modérée Déchargement optimiseur à 25–50 % de fraction pour meilleure efficacité

Activation

Déchargement optimiseur CPU (recommandé pour les grands modèles)

cfg.optimizer.optimizer_cpu_offload = True
cfg.optimizer.optimizer_offload_fraction = 1.0
cfg.optimizer.overlap_cpu_optimizer_d2h_h2d = True

Surcharges CLI :

optimizer.optimizer_cpu_offload=True \
optimizer.optimizer_offload_fraction=0.5 \
optimizer.overlap_cpu_optimizer_d2h_h2d=True

Déchargement activations CPU (petits/moyens modèles uniquement)

cfg.model.cpu_offloading = True
cfg.model.cpu_offloading_num_layers = 16
cfg.model.cpu_offloading_activations = True
cfg.model.cpu_offloading_weights = False

cfg.model.pipeline_model_parallel_size = 1
cfg.model.recompute_granularity = None
cfg.model.cuda_graph_impl = "none"

Référence des paramètres de config

Déchargement d'optimiseur

Paramètre Défaut Description
optimizer_cpu_offload False Interrupteur principal
optimizer_offload_fraction 0.0 Fraction des états optimiseur sur CPU (0,0–1,0)
overlap_cpu_optimizer_d2h_h2d False Chevaucher transferts GPU↔CPU avec calcul
use_torch_optimizer_for_cpu_offload False Utiliser torch.optim au lieu d'optimiseur fusionné pour portion CPU

Déchargement d'activations

Paramètre Défaut Description
cpu_offloading False Interrupteur principal
cpu_offloading_num_layers 0 Nombre de couches transformer à décharger (0 à num_layers-1)
cpu_offloading_activations True Décharger activations
cpu_offloading_weights False Décharger poids
cpu_offloading_double_buffering False Double buffer entre couches lors du rechargement

Compatibilité et contraintes

Déchargement d'activations

  • pipeline_model_parallel_size doit être 1
  • recompute_granularity doit être None
  • Impossible de combiner avec fine_grained_activation_offloading
  • Impossible de combiner avec CUDA graphs
  • cpu_offloading_num_layers doit être dans [0, num_layers-1)

Déchargement d'optimiseur

  • Nécessite use_distributed_optimizer = True (défaut dans la plupart des recipes)
  • Aucune restriction PP, recompute, ou CUDA graph
  • optimizer_offload_fraction doit être dans [0,0, 1,0]

Pratique : grands modèles MoE

Le déchargement d'activations est bloqué pour Qwen3-30B-A3B et modèles MoE similaires. La contrainte PP=1 signifie que chaque GPU retient les 48 couches ; poids du modèle + états optimiseur seuls (~70 GB) dépassent la capacité H100 de 80 GB.

Config minimale fonctionnelle

Déchargement optimiseur (50 %, équilibré)

cfg.optimizer.optimizer_cpu_offload = True
cfg.optimizer.optimizer_offload_fraction = 0.5

Déchargement optimiseur (100 % + chevauchement, max d'économies)

cfg.optimizer.optimizer_cpu_offload = True
cfg.optimizer.optimizer_offload_fraction = 1.0
cfg.optimizer.overlap_cpu_optimizer_d2h_h2d = True

Déchargement activations (petit modèle, PP=1)

cfg.model.cpu_offloading = True
cfg.model.cpu_offloading_num_layers = 16
cfg.model.cpu_offloading_activations = True
cfg.model.cpu_offloading_weights = False
cfg.model.pipeline_model_parallel_size = 1
cfg.model.recompute_granularity = None

Déchargement poids uniquement (petit modèle, PP=1)

cfg.model.cpu_offloading = True
cfg.model.cpu_offloading_num_layers = 8
cfg.model.cpu_offloading_activations = False
cfg.model.cpu_offloading_weights = True
cfg.model.pipeline_model_parallel_size = 1
cfg.model.recompute_granularity = None

Activations et poids (petit modèle, PP=1)

cfg.model.cpu_offloading = True
cfg.model.cpu_offloading_num_layers = 8
cfg.model.cpu_offloading_activations = True
cfg.model.cpu_offloading_weights = True
cfg.model.pipeline_model_parallel_size = 1
cfg.model.recompute_granularity = None

Déchargement poids et déchargement activations partagent les mêmes contraintes (PP=1, pas de recompute, pas CUDA graphs). Déchargement poids n'a pas été testé dans expériences Qwen3-30B-A3B — résultats mesurés couvrent déchargement optimiseur uniquement.

Commande minimale exécutable

uv run python scripts/training/run_recipe.py \
  --recipe qwen3_30b_a3b_pretrain_config \
  optimizer.optimizer_cpu_offload=True \
  optimizer.optimizer_offload_fraction=0.5 \
  train.train_iters=20 \
  train.global_batch_size=8 \
  train.micro_batch_size=1

Vérification

Tests unitaires

uv run python -m pytest \
  tests/unit_tests/models/test_gpt_full_te_layer_autocast_spec.py -k "cpu_offload" \
  tests/unit_tests/peft/test_utils.py -k "cpu_offload" -q

Critères de succès

  • Validation config réussit pour mode déchargement sélectionné
  • Entraînement se termine sans erreurs OOM ou NCCL
  • Loss correspond baseline non-déchargé (delta max < 0,001)
  • Utilisation mémoire diminue proportionnellement à fraction déchargement

Ancres de code

Contraintes déchargement activations MCore

        if self.cpu_offloading and (
            self.cpu_offloading_num_layers < 0 or self.cpu_offloading_num_layers >= self.num_layers
        ):
            raise ValueError(...)

        if self.cpu_offloading and self.pipeline_model_parallel_size > 1:
            raise ValueError(
                "Currently there is no support for Pipeline parallelism with CPU offloading"
            )

        if self.cpu_offloading and self.recompute_granularity is not None:
            raise ValueError(
                "CPU offloading does not work when activation recomputation is enabled"
            )

Incompatibilité CUDA graphs MCore

            if self.cpu_offloading:
                raise ValueError("CUDA graphs not supported with CPU offloading.")

Exclusion mutuelle déchargement fin-grained MCore

        if self.fine_grained_activation_offloading:
            assert (
                not self.cpu_offloading
            ), "fine_grained_activation_offloading cannot be enabled with cpu_offloading."

Instanciation HybridDeviceOptimizer MCore

        if config.optimizer_cpu_offload:
            # ... setup cpu/gpu optimizer classes ...
            optimizer = HybridDeviceOptimizer(
                param_groups,
                offload_fraction=config.optimizer_offload_fraction,
                cpu_optimizer_cls=cpu_optimizer_cls,
                gpu_optimizer_cls=gpu_optimizer_cls,
                overlap_cpu_optimizer_d2h_h2d=config.overlap_cpu_optimizer_d2h_h2d,
                pin_cpu_grads=config.pin_cpu_grads,
                pin_cpu_params=config.pin_cpu_params,
            )

Garde CUDA graph Bridge

        assert not config.cpu_offloading and config.recompute_granularity is None, "Cudagraphs not supported"

Déchargement activations Bridge en PEFT

        if self.config.cpu_offloading and self.config.cpu_offloading_activations:
            x.activation_offloading = True
        x, _ = self.linear_in(x)
        x = self.activation(x)
        if self.config.cpu_offloading and self.config.cpu_offloading_activations:
            x.activation_offloading = True
        x, _ = self.linear_out(x)

Champs model_parallel_config MCore

    cpu_offloading: bool = False
    cpu_offloading_num_layers: int = 0
    cpu_offloading_activations: bool = True
    cpu_offloading_weights: bool = False
    cpu_offloading_double_buffering: bool = False
    cpu_offloading_retain_pinned_cpu_buffers: bool = False

Config déchargement optimiseur MCore

    optimizer_cpu_offload: bool = False
    optimizer_offload_fraction: float = 0.0
    use_torch_optimizer_for_cpu_offload: bool = False
    overlap_cpu_optimizer_d2h_h2d: bool = False

Diagnostic d'erreurs

Symptôme Cause probable Comment confirmer Correction
Currently there is no support for Pipeline parallelism with CPU offloading Déchargement activations + PP > 1 Vérifier pipeline_model_parallel_size Définir PP=1 ou utiliser déchargement optimiseur
CPU offloading does not work when activation recomputation is enabled Déchargement activations + recompute Vérifier recompute_granularity Définir recompute_granularity=null
fine_grained_activation_offloading cannot be enabled with cpu_offloading Deux modes déchargement activés Vérifier deux flags Utiliser l'un ou l'autre
CUDA graphs not supported with CPU offloading CUDA graphs + déchargement activations Vérifier cuda_graph_impl Définir cuda_graph_impl="none"
OOM avec déchargement activations Modèle trop grand pour PP=1 Vérifier mémoire allouée vs 80 GB Utiliser déchargement optimiseur avec PP > 1
Ralentissement extrême (>4x) 100 % déchargement optimiseur, goulot Adam CPU Comparer temps iter à fractions différentes Réduire fraction ou activer overlap_cpu_optimizer_d2h_h2d
OOM à déchargement optimiseur partiel Déchargement insuffisant pour cette config Vérifier mémoire à fractions différentes Augmenter fraction ou ajouter PP

Limitations connues

  • Déchargement activations nécessite PP=1, rendant impratique pour grands modèles (30B+ MoE) nécessitant parallelisme pipeline.
  • Pénalité débit déchargement optimiseur s'échelonne linéairement (~1,9x à 25 %, ~4,2x à 100 % pour Qwen3-30B-A3B).
  • Chevauchement D2H/H2D fournit seulement ~7 % d'accélération car calcul Adam CPU est goulot dominant.
  • fine_grained_activation_offloading est approche module-level séparate fonctionnant avec PP > 1 mais impossible à combiner avec cpu_offloading level-couche.

Skills similaires