Aller au contenu

Initialisation des Modèles et Dataloaders en PyTorch

Vous vous souvenez de la dernière fois que vous avez attendu une heure qu’une époque d’entraînement se termine, en sachant que la GPU restait sous-utilisée ? C’est le symptôme d’une initialisation mal conçue. L’initialisation des modèles et dataloaders en PyTorch est le fondement invisible de tout entraînement efficace : elle détermine à la fois la qualité de vos données et la vitesse à laquelle votre réseau peut les traiter.

Pourquoi cette étape change tout

Pensez à votre pipeline d’entraînement comme une chaîne de production. Le Dataset est votre source brute de matières premières—désorganisée, dispersée. Le DataLoader est la chaîne d’assemblage qui prépare des lots standardisés, les mélange intelligemment, et les livre parallèlement à votre GPU. Le modèle est l’usine prête à traiter ces lots optimisés. Sans cette organisation initiale, le traitement devient séquentiel, imprévisible, et vous gaspillez la puissance computationnelle qui vous a coûté plusieurs milliers d’euros.

Comprendre les trois piliers

1. Le Dataset : structure et encapsulation

Un Dataset PyTorch est une abstraction simple mais puissante. Vous définissez une classe qui hérite de torch.utils.data.Dataset et implémentez trois méthodes :

  • __init__ : stockage des chemins ou références de données
  • __len__ : retour du nombre total d’échantillons
  • __getitem__(idx) : chargement et retour du sample à l’indice idx

La clé : __getitem__ est appelé à la demande par le DataLoader pour chaque indice. Cela signifie qu’aucune donnée n’est préchargée en mémoire. Vous pouvez encapsuler 10 millions d’images sur disque et le Dataset en gérera seulement 32 à la fois (votre batch_size).

2. Le DataLoader : orchestration du flux

Le DataLoader est le chef d’orchestre. Il prend votre Dataset et exécute en parallèle quatre opérations :

  • Chunking : division des 10 millions d’indices en groupes de batch_size (par ex., 32)
  • Shuffling : permutation aléatoire des indices à chaque époque pour décorréler l’ordre temporel
  • Multi-worker loading : distribution du travail de chargement sur N cœurs CPU (paramètre num_workers)
  • Batching vectorisé : agrégation des samples individuels en tenseurs GPU-optimisés

Vous instanciez cela en une seule ligne :

train_loader = DataLoader(
dataset_train,
batch_size=32,
shuffle=True,
num_workers=4,
pin_memory=True
)

Et soudain, vous passez de latences imprévisibles à un flux continu.

3. Le Modèle : architectures prêtes pour l’entraînement

Votre modèle est l’architecture du réseau de neurones—définie via nn.Sequential pour les réseaux simples ou une classe personnalisée héritant de nn.Module pour les architectures complexes. L’initialisation correct signifie :

  • Placer le modèle sur le bon device (GPU/CPU) via .to(device)
  • Vérifier que les dimensions du premier layer correspondent à votre input (ex., 3 canaux pour RGB)
  • Initialiser les poids de manière appropriée (PyTorch utilise des defaults raisonnables)

Comment ça fonctionne sous le capot

Le shuffling n’est pas magique—c’est algorithmique

Quand vous posez shuffle=True, PyTorch utilise un RandomSampler qui génère une permutation complète des indices du Dataset à chaque époque. Cela garantit que :

  1. Chaque sample est vu exactement une fois par époque
  2. L’ordre est différent à chaque époque, créant une variabilité qui améliore la généralisation (c’est une régularisation implicite)
  3. Pour l’entraînement distribué sur 4 GPUs, le DistributedSampler utilise un RNG seeded par rank et epoch, garantissant des permutations cohérentes mais différentes par worker

Le multi-worker loading change tout

Par défaut, sans workers, le chargement de données bloque votre GPU. Avec num_workers=4, vous créez 4 processus séparés qui exécutent votre __getitem__ en parallèle sur 4 cœurs CPU. Une queue thread-safe accumule les résultats, décorrélant la latence de chaque sample du temps GPU principal.

Résultat empirique : réduction du temps d’époque de 3.5s → 1.2s sur CIFAR-10.

Memory pinning : les 10 microsecondes qui changent tout

Avec pin_memory=True, PyTorch alloue les tensors sur de la mémoire paginée du CPU (page-locked memory), ce qui permet les transfers DMA directs vers le GPU sans copie intermédiaire. La latence passe de ~50-100 microsecondes à ~1-2 microsecondes par batch. Sur 1000 batches, cela représente 50-100 millisecondes d’économie—imperceptible par batch, crucial sur un entraînement de 24 heures.

Le batching vectorisé : pourquoi les batches existent

Votre modèle traite les samples individuellement dans une boucle Python native. Les GPUs, eux, sont des machines à multiplication matricielle parallèle. Un batch de 32 samples permet à CUDA de vectoriser les opérations : 32 activations en parallèle, 32 multiplications matricielles simultanées. Sans batching, vous utiliseriez 2-3% de la puissance GPU. Avec batching, vous approchez les 80-95%.

Mise en pratique : les 10 étapes concrètes

  1. Structurer en classe Dataset : Créer une classe avec __init__, __len__, et __getitem__(idx). Stocker les références disque, pas les données.

  2. Définir les transformations : transforms.Compose() avec normalisation, augmentation spatiale. Appliquées lazy dans __getitem__.

  3. Instancier DataLoader entraînement : DataLoader(dataset_train, batch_size=32, shuffle=True, num_workers=4, pin_memory=True).

  4. Instancier DataLoader validation : DataLoader(dataset_val, batch_size=64, shuffle=False, num_workers=0). Pas de shuffling, batch_size plus grand.

  5. Initialiser l’architecture : model = nn.Sequential(...) ou classe personnalisée. .to(device).

  6. Configurer optimizer et loss : optim.Adam(model.parameters(), lr=1e-3), nn.CrossEntropyLoss().

  7. Boucle d’entraînement : for batch_idx, (data, target) in enumerate(train_loader): → forward/backward/step.

  8. Distributed setup : Si multi-GPU, utiliser DistributedSampler et nn.parallel.DistributedDataParallel.

  9. Profiling : Mesurer temps data loading vs forward/backward. Ajuster num_workers et batch_size pour saturation GPU >80%.

  10. Checkpointing : torch.save({'model_state_dict': model.state_dict(), ...}, path) pour reprises d’entraînement.

Cas réel : ImageNet-scale training

Une startup de vision par ordinateur entraîne ResNet50 sur 500K images propriétaires avec batch_size=256, shuffle=True, pin_memory=True, et num_workers=8.

  • Avant optimisation : Epoch = 45 minutes. GPU utilisation = 62%. Goulot d’étranglement : data loading.
  • Après : Epoch = 12 minutes. GPU utilisation = 94%. Les 8 workers CPU cachent la latence I/O SSD. Le GPU ne stall jamais en attente de batches.

Impact business : réduction du coût d’entraînement de 6 GPU-jours → 1.5 GPU-jour. À 0.50/GPUheure:eˊconomiede0.50/GPU-heure : économie de 108 par entraînement. Répété 50 fois/an = $5400 d’économies directes, sans compter l’accélération du time-to-market.

Les débats non résolus

num_workers optimal ? La littérature recommande ≈ nombre de cœurs CPU. Empiriquement, 4-8 est souvent optimal. Au-delà, les rendements décroissants dominent. Profiling requis.

drop_last=True vs False ? Abandonner les batches < batch_size stabilise les gradients mais biaise l’apprentissage. Pour grandes datasets (N >> batch_size), négligeable. Pour petites, débat non résolu. Recommandation : drop_last=True pour stabilité.

Determinism vs randomization ? Reproductibilité requise en recherche (fixer les seeds). Évaluation robuste requiert plusieurs runs stochastiques. PyTorch offre set_seed(), mais vraie determinism fragile (certains ops CUDA non-déterministes).

Historique et évolution

  • 2016 : Facebook AI Research publie PyTorch. Primitives Dataset et DataLoader abstraient enfin la gestion manuelle des données.
  • 2017 : DistributedSampler natif. Sharding automatique des données sur multi-GPU.
  • 2018 : Workers multi-processus (num_workers). Révolution de vitesse via parallelization I/O.
  • 2020 : prefetch_factor et persistent_workers. Optimisations haute-performance pour LLMs.
  • 2023-2026 : Intégration avec distributed frameworks (DeepSpeed, FSDP). Initialisation devient critique pour scaling 100B+ parameter models.

Notions liées

Sources & Références

  • PyTorch Official Documentation - Datasets & DataLoaders Tutorial
  • IDRIS (Institut du Développement et des Ressources en Informatique Scientifique) - PyTorch : Chargement de bases de données pour l’apprentissage distribué
  • Stanford CS231N - A detailed example of how to generate your data in parallel
  • Machine Learning Mastery - Training a PyTorch Model with DataLoader and Dataset
  • Microsoft Azure Machine Learning - Tutoriel : Effectuer l’apprentissage de votre premier modèle
  • Hangar Documentation - Dataloaders for Machine Learning (TensorFlow & PyTorch)
  • KodeKloud Documentation - Datasets and Dataloaders