Bonjour ! Dans la leçon précédente, nous avons franchi une étape majeure en apprenant à construire une architecture de réseau de neurones à l'aide de la classe nn.Module de PyTorch. Nous savons maintenant comment définir les couches de notre modèle et comment les données les traversent.
Mais un modèle n'est rien sans données pour l'entraîner. La question qui se pose est : comment fournir ces données au modèle de manière efficace ? Pour de petits ensembles de données, nous pourrions tout charger en mémoire. Mais en deep learning, les ensembles de données peuvent être gigantesques, dépassant de loin la capacité de la RAM de votre ordinateur ou de la mémoire de votre GPU. Les entraîner en une seule fois est impossible et inefficace.
Cette leçon se concentre sur la solution de PyTorch à ce problème : préparer un jeu de données pour l'entraînement à l'aide de TensorDataset et DataLoader. Nous allons apprendre à encapsuler nos données dans un format standardisé (Dataset), puis à utiliser un outil puissant (DataLoader) pour nous servir automatiquement des petits lots de données (mini-batches), prêts à être consommés par notre modèle.
1. Le problème de l'alimentation des données
Imaginez que vous avez un jeu de données de 100 000 images. Si vous essayez de toutes les charger et de les passer à votre modèle en même temps :
- Problème de mémoire : Vous saturerez probablement la mémoire de votre GPU ou de votre CPU.
- Problème d'entraînement : L'algorithme de descente de gradient serait très lent. Calculer le gradient sur l'ensemble des données à chaque étape (ce qu'on appelle le "batch gradient descent") est coûteux.
La solution est le mini-batch gradient descent, où le modèle met à jour ses poids après avoir vu un petit sous-ensemble de données (un "mini-batch") à la fois. Cela rend l'entraînement plus rapide et souvent plus stable.
PyTorch formalise ce processus à l'aide de deux classes principales : Dataset et DataLoader.
2. Dataset : Standardiser l'accès aux données
La première étape consiste à dire à PyTorch où se trouvent nos données et comment y accéder. C'est le rôle de la classe torch.utils.data.Dataset. Un Dataset est un objet qui doit implémenter deux méthodes que nous avons déjà vues en programmation orientée objet :
__len__(self): Doit retourner le nombre total d'échantillons dans le jeu de données.__getitem__(self, index): Doit retourner le i-ème échantillon (généralement un tuple de(features, label)).
On peut créer nos propres classes Dataset personnalisées, ce qui est très puissant pour des cas complexes (par exemple, charger des images depuis des fichiers sur le disque dur).
La vidéo suivante montre comment créer une telle classe Dataset personnalisée. Cela vous aidera à comprendre le concept général, même si nous nous concentrerons ensuite sur une version plus simple.
TensorDataset : Le raccourci pour les données en Tenseurs
Souvent, nos données sont déjà sous forme de tenseurs PyTorch (ou de tableaux NumPy que nous pouvons facilement convertir). Dans ce cas, créer une classe personnalisée complète est excessif. PyTorch fournit une classe très pratique : TensorDataset.
TensorDataset est un Dataset qui prend simplement en entrée des tenseurs et s'occupe de tout pour nous. Le seul prérequis est que tous les tenseurs aient la même taille pour la première dimension (le nombre d'échantillons).
import torch
import numpy as np
from torch.utils.data import TensorDataset
# 1. Supposons que nos données sont dans des tableaux NumPy
features_np = np.random.rand(100, 10) # 100 échantillons, 10 caractéristiques
labels_np = np.random.randint(0, 3, 100) # 100 étiquettes (classes 0, 1, ou 2)
# 2. Convertissons-les en tenseurs PyTorch
features_tensor = torch.from_numpy(features_np).float()
labels_tensor = torch.from_numpy(labels_np).long()
# 3. Créons un TensorDataset
# Il prend les tenseurs de caractéristiques et d'étiquettes comme arguments
dataset = TensorDataset(features_tensor, labels_tensor)
# Maintenant, nous pouvons utiliser len() et l'indexation
print(f"Nombre total d'échantillons: {len(dataset)}")
# Accéder au premier échantillon (features, label)
first_sample_features, first_sample_label = dataset[0]
print(f"\nCaractéristiques du premier échantillon:\n{first_sample_features}")
print(f"Étiquette du premier échantillon: {first_sample_label}")
L'article suivant résume bien ces concepts et servira de référence textuelle.
3. DataLoader : Créer des lots (batches) pour l'entraînement
Maintenant que nous avons un Dataset, nous avons besoin d'un moyen de l'itérer en lots, de le mélanger (shuffle) et de le charger efficacement. C'est le rôle de torch.utils.data.DataLoader.
Le DataLoader est un itérable qui prend en entrée notre Dataset et le sert en lots. Ses arguments les plus importants sont :
dataset: L'objetDatasetque nous venons de créer.batch_size: Le nombre d'échantillons par lot. Une valeur typique est 32, 64 ou 128.shuffle: Un booléen. SiTrue, les données sont mélangées à chaque époque. C'est essentiel pour l'entraînement afin d'éviter que le modèle n'apprenne l'ordre des données et pour améliorer la généralisation. On le met généralement àFalsepour les données de validation et de test.
Continuons notre exemple :
from torch.utils.data import DataLoader
# Créons un DataLoader à partir de notre dataset
# batch_size=16 signifie que nous aurons des lots de 16 échantillons
# shuffle=True signifie que l'ordre des données est aléatoire à chaque époque
dataloader = DataLoader(dataset, batch_size=16, shuffle=True)
# Le DataLoader est un itérable. Nous pouvons l'utiliser dans une boucle for.
# C'est exactement ce que nous ferons dans notre boucle d'entraînement !
for batch_features, batch_labels in dataloader:
print("--- Nouveau lot ---")
print(f"Forme du lot de caractéristiques: {batch_features.shape}")
print(f"Forme du lot d'étiquettes: {batch_labels.shape}")
# La première dimension est la taille du lot (batch_size)
break # Arrêtons après le premier lot pour la démonstration
Vous remarquerez que la forme des caractéristiques est torch.Size([16, 10]), ce qui correspond à notre batch_size.
La vidéo suivante montre exactement comment utiliser le DataLoader après avoir créé un Dataset.
Un point pratique très important est la gestion de la mémoire. Généralement, le Dataset complet reste sur la mémoire principale (CPU). Le DataLoader charge ensuite chaque lot sur le GPU juste avant de le passer au modèle. Cela optimise l'utilisation de la mémoire limitée du GPU.
4. Le flux de travail complet
Résumons le processus avec un diagramme de flux plus détaillé.
Le processus complet est le suivant :
- Préparation des données : Chargez vos données brutes (par exemple, depuis un fichier CSV avec Pandas) et séparez-les en caractéristiques (X) et étiquettes (y).
- Conversion en Tenseurs : Convertissez vos tableaux NumPy ou listes Python en tenseurs PyTorch.
- Création du
TensorDataset: Encapsulez vos tenseurs de caractéristiques et d'étiquettes dans unTensorDataset. - Création du
DataLoader: Encapsulez votreTensorDatasetdans unDataLoader, en spécifiant la taille des lots (batch_size) et si vous souhaitez mélanger les données (shuffle=True). - Itération : Utilisez le
DataLoaderdans votre boucleford'entraînement pour obtenir des lots de données prêts à l'emploi.
Conclusion
Aujourd'hui, nous avons appris à construire le pipeline de données, un composant essentiel de tout projet de deep learning. Sans un moyen efficace de charger et de préparer les données, même le meilleur modèle ne peut pas être entraîné.
Points clés à retenir :
- L'entraînement par mini-lots est essentiel pour gérer de grands ensembles de données et pour un entraînement efficace.
- PyTorch utilise un système à deux niveaux :
Dataset: Un objet qui représente votre ensemble de données et sait comment accéder à n'importe quel échantillon viadataset[i].TensorDatasetest une implémentation pratique lorsque vos données sont déjà des tenseurs.
DataLoader: Un itérable qui prend unDatasetet fournit des fonctionnalités clés comme le batching (création de lots) et le shuffling (mélange).- Le flux de travail standard est : Données brutes -> Tenseurs ->
TensorDataset->DataLoader-> Boucle d'entraînement.
Nous avons maintenant toutes les pièces du puzzle : nous savons comment construire un modèle et comment lui fournir des données par lots. Dans la prochaine leçon, nous allons enfin assembler le tout. Nous allons implémenter une boucle d'entraînement complète, incluant la définition d'une fonction de perte, d'un optimiseur, et l'orchestration de la passe avant, de la passe arrière et de la mise à jour des poids.