Comment diviser un jeu de données en lots avec Python

Découvrez des techniques pour diviser des jeux de données en lots dans Python, améliorer l’efficacité de la mémoire, la vitesse de traitement et l’entraînement des modèles.
15 min de lecture
How to Split Dataset Into Batches blog image

Dans ce guide, vous apprendrez :

  • La définition du lot
  • Pourquoi traiter les jeux de données par lots
  • Comment diviser un ensemble de données en lots dans Python
  • L’approche de l’option map() par lots pour les jeux de données

C’est parti !

Qu’est-ce qu’un lot ?

Dans le monde du ML et du traitement des données, un lot n’est rien d’autre qu’un sous-ensemble d’un jeu de données. Les lots sont généralement utilisés pour traiter efficacement de grands volumes de données. Au lieu de traiter un jeu de données entier en une seule fois, les données sont divisées en petits morceaux, également appelés lots. Chaque lot peut être traité indépendamment, ce qui permet de réduire l’utilisation de la mémoire et d’améliorer l’efficacité des calculs.

Par exemple, supposons que vous disposiez d’un échantillon de données au format CSV :

id,nom,âge,score
1,John,28,85
2,Jane,34,90
3,Bob,25,72
4,Alice,30,88
5,Charlie,29,91
6,David,35,79
7,Eve,22,95
8,Frank,31,82
9,Grace,27,86
10,Hannah,26,80

Un lot des jeux de données ci-dessus est :

6, David, 35, 79
7, Eve, 22, 95
8, Frank, 31, 82
9, Grace, 27, 86
10, Hannah, 26, 80

Ce lot découpe l’ensemble de jeux de données original de la ligne 5 à la ligne 10.

Avantages de l’utilisation du traitement d’un jeu de données par lots

Supposons que vous disposiez d’un jeu de données que vous avez créé à l’aide de certaines techniques de sourcing de données. Si vous n’êtes pas familier avec ce processus, suivez notre guide sur la création d’un jeu de données.

Pourquoi voudriez-vous traiter cet ensemble de données par lots ? Parce que cela présente les avantages suivants :

  • Efficacité de la mémoire: travaillez avec des portions plus petites et plus faciles à gérer à la fois, au lieu de charger l’ensemble du jeu de données dans la mémoire.
  • Traitement plus rapide: le traitement par lots peut être parallélisé, ce qui réduit le temps nécessaire au traitement de grands jeux de données.
  • Meilleur apprentissage pour les modèles ML: facilite l’apprentissage des modèles de machine learning en mettant à jour les poids de manière incrémentielle, ce qui peut entraîner une convergence plus stable et plus rapide.
  • Meilleure évolutivité: facilite l’adaptation de votre traitement à des jeux de données volumineux qui ne peuvent pas tenir en mémoire en une seule fois.

Comment diviser un jeu de données en lots : les 5 meilleures approches

Avant d’explorer les meilleures méthodes Python pour diviser un jeu de données en lots, nous devons identifier certains critères d’évaluation de ces approches. Voici une liste pertinente d’aspects à prendre en compte :

  • Mise en œuvre: un extrait de code montrant comment utiliser l’approche dans un exemple simple.
  • Scénarios: les situations réelles dans lesquelles l’approche de division des jeux de données est applicable.
  • Entrée: les types de fichiers de jeux de données et les structures de données pris en charge par la stratégie de fractionnement.
  • Avantages: les avantages offerts par l’approche.
  • Inconvénients: les limites ou les inconvénients de la méthode.

Il est temps de les analyser un par un !

Approche n° 1 : découpage de tableaux

Le découpage de tableaux est une méthode simple pour diviser un jeu de données en lots plus petits et plus faciles à gérer. L’idée est de diviser un jeu de données (représenté par une liste, un tableau ou une autre séquence) en morceaux en le découpant.

👨‍💻 Mise en œuvre:

def create_batches(data, batch_size):
    return [data[i:i + batch_size] for i in range(0, len(data), batch_size)]

# exemple d'utilisation
data = list(range(1, 51))  # exemple d'ensemble de données
batches = create_batches(data, batch_size=5)

print(batches) 

# résultat : [[1, 2, 3, 4, 5], ..., [46, 47, 48, 49, 50]]

🎯 Scénarios:

  • Tâches de prétraitement des données où les limitations de mémoire sont minimes
  • Tâches de traitement de données en parallèle nécessitant des blocs de mémoire gérables
  • Traitement simple par lots dans les pipelines de données

🔠 Entrée:

  • Listes, tableaux et tuples en Python.
  • Tableaux Numpy
  • Données CSV, chargées en mémoire sous forme de liste de lignes
  • Pandas DataFrames, s’ils sont convertis en listes ou tableaux

👍 Avantages:

  • Simple et facile à mettre en œuvre
  • Ne nécessite pas de bibliothèques externes
  • Permet un contrôle direct sur la taille des lots

👎 Inconvénients:

  • Limité par la mémoire disponible
  • Ne prend pas en charge les jeux de données extrêmement volumineux ou les structures de données complexes
  • Nécessite une logique personnalisée pour le brassage des données

Approche n° 2 : générateurs

Les générateurs Python vous permettent de diviser un jeu de données en lots en produisant un lot à la fois. Si vous n’êtes pas familier avec ce mécanisme, un générateur est un type spécial de fonction qui se comporte comme un itérateur. Au lieu de renvoyer directement les données, il utilise le mot-clé yield pour produire un objet itérateur. Cela permet d’accéder aux lots de manière séquentielle à l’aide d’une boucle for ou de la fonction next().

👨‍💻 Implémentation:

def data_generator(data, batch_size):
    for i in range(0, len(data), batch_size):
        yield data[i:i + batch_size]

# exemple d'utilisation
data = list(range(1, 51))  # exemple d'ensemble de données
for batch in data_generator(data, batch_size=5):
    print(batch)

# sortie : 
# [1, 2, 3, 4, 5]
# ...
# [46, 47, 48, 49, 50]

🎯 Scénarios:

  • Traitement par lots dans les pipelines de données
  • Tâches de prétraitement et d’augmentation de données à grande échelle
  • Du traitement par lots simple au traitement par lots complexe dans les pipelines de données

🔠 Entrée:

  • Listes, tableaux et tuples
  • Tableaux NumPy
  • Jeux de données basés sur des fichiers où le chargement de chaque lot à partir du disque est possible

👍 Avantages:

  • Peut traiter de grands jeux de données sans les charger entièrement en mémoire
  • Configuration minimale et mise en œuvre facile
  • Permet un chargement contrôlé et à la demande des données

👎 Inconvénients:

  • Limité par l’ordre des données, sauf si un remaniement supplémentaire est mis en œuvre
  • Moins efficace pour les tailles de lots dynamiques ou variables
  • Peut ne pas être la meilleure solution pour le traitement parallèle, en particulier pour les opérations multithread

Approche n° 3 : PyTorch DataLoader

La classe DataLoader de PyTorch vous aide à diviser efficacement les jeux de données en lots gérables. En tant que structure de données spécialisée dans le traitement des jeux de données, elle offre également des fonctionnalités utiles telles que le mélange et le chargement parallèle des données.

Notez que DataLoader fonctionne avec TensorDataset, une autre structure de données PyTorch destinée à représenter un jeu de données. Plus précisément, un TensorDataset accepte deux arguments :

  1. inps: les données d’entrée, généralement sous la forme d’un tenseur.
  2. tgts: les étiquettes ou valeurs cibles, généralement sous forme de tenseur, correspondant aux données d’entrée.

TensorDataset associe les données et la cible, qui peuvent ensuite être chargées par DataLoader pour le traitement par lots et l’entraînement.

👨‍💻 Implémentation:

from torch.utils.data import DataLoader, TensorDataset
import torch

# données pour définir un ensemble de données simple
inputs = torch.arange(1, 51).float().reshape(-1, 1)  # un ensemble de données tensoriel 1D (entrée)
targets = inputs ** 2  # carré des valeurs d'entrée (simulant une tâche de régression)

# création d'un TensorDataset et d'un DataLoader
dataset = TensorDataset(inputs, targets)
dataloader = DataLoader(dataset, batch_size=5, shuffle=True)

# itérer à travers DataLoader
for batch in dataloader:
    print(batch)

# exemple de sortie :
# [tensor([[46.],
#         [42.],
#         [25.],
#         [10.],
#         [34.]]), tensor([[2116.],
#         [1764.],
#         [ 625.],
#         [ 100.],
#         [1156.]])]
# ...
# [tensor([[21.],
#         [ 9.],
#         [ 2.],
#         [38.],
#         [44.]]), tensor([[ 441.],
#         [  81.],
#         [   4.],
#         [1444.],
#         [1936.]])] 

🎯 Scénarios:

  • Entraînement et test de modèles d’apprentissage automatique dans PyTorch
  • Mélange des données pour obtenir des lots d’entraînement non biaisés
  • Traitement de données à grande échelle pour des tâches d’apprentissage profond

🔠 Entrée:

  • Jeux de données personnalisés chargés dans PyTorch TensorDatasets
  • Données tabulaires et tableaux numériques au format Tensor

👍 Avantages:

  • Optimisé pour les grands jeux de données avec traitement par lots et brassage
  • Prend en charge le chargement parallèle des données, accélérant la récupération par lots
  • Fonctionne de manière transparente avec les modèles PyTorch et les boucles d’entraînement
  • Compatible avec le traitement GPU

👎 Inconvénients:

  • Nécessite PyTorch
  • Nécessite la conversion des données en tenseurs.
  • N’est pas idéal pour les tâches de traitement par lots non liées au ML

Approche n° 4 : méthode batch() de TensorFlow

La méthode batch() de TensorFlow Dataset permet de diviser les Jeux de données en lots. Cette méthode divise les Jeux de données en morceaux plus petits, avec des fonctionnalités telles que la parallélisation, le contrôle de l’ordre de traitement et la dénomination.

En tant que bibliothèque d’apprentissage automatique, TensorFlow offre également des fonctionnalités supplémentaires telles que le mélange, la répétition et la prélecture.

👨‍💻 Implémentation:

import tensorflow as tf

# créer un exemple de jeu de données
inputs = tf.range(1, 51, dtype=tf.float32)  # un jeu de données tensoriel 1D (entrées)
targets = inputs ** 2  # carré des valeurs d'entrée (simulant une tâche de régression)

# convertir les entrées et les cibles en tf.data.Dataset
inputs_dataset = tf.data.Dataset.from_tensor_slices(inputs)
targets_dataset = tf.data.Dataset.from_tensor_slices(targets)

# créer un ensemble de données en compressant les entrées et les cibles ensemble
ensemble_de_données = tf.data.Dataset.zip((ensemble_de_données_entrées, ensemble_de_données_cibles))

# produire un ensemble de données par lots
ensemble_de_données_par_lots = ensemble_de_données.batch(batch_size=5)

pour lot dans ensemble_de_données_par_lots :
    imprimer(lot)

# sortie :
# (<tf.Tensor: shape=(5,), dtype=float32, numpy=array([1., 2., 3., 4., 5.], dtype=float32)>, <tf.Tensor: shape=(5,), dtype=float32, numpy=array([ 1.,  4.,  9., 16., 25.], dtype=float32)>)
# ...
# (<tf.Tensor: shape=(5,), dtype=float32, numpy=array([46., 47., 48., 49., 50.], dtype=float32)>, <tf.Tensor: shape=(5,), dtype=float32, numpy=array([2116., 2209., 2304., 2401., 2500.], dtype=float32)>)

🎯 Scénarios:

  • Entraînement et test de modèles d’apprentissage automatique dans PyTorch
  • Mélange des données pour obtenir des lots d’entraînement non biaisés
  • Traitement de données à grande échelle pour des tâches d’apprentissage profond

🔠 Entrée:

  • Objets TensorFlow tf.data.Dataset
  • Tableaux NumPy (qui peuvent être convertis en Dataset)
  • Fichiers TFRecord, un format de fichier binaire spécial couramment utilisé pour stocker de grands jeux de données dans TensorFlow

👍 Avantages:

  • Optimisé pour une utilisation efficace de la mémoire
  • S’intègre parfaitement à l’écosystème TensorFlow pour l’entraînement et l’évaluation des modèles
  • Prend en charge le shuffling, le préchargement et d’autres fonctionnalités utiles
  • Prend en charge une grande variété de formats de données, y compris les images, le texte et les données structurées

👎 Inconvénients:

  • Nécessite TensorFlow
  • Pour les jeux de données plus complexes, une configuration supplémentaire peut être nécessaire pour formater et prétraiter correctement les données
  • Peut entraîner une surcharge pour le traitement par lots de petits jeux de données

Approche n° 5 : format HDF5

HDF5 est un format de données largement adopté pour la gestion de grands jeux de données, en particulier lorsqu’il s’agit de structures de données hiérarchiques. Il permet de diviser un grand jeu de données en morceaux et de les stocker efficacement.

La bibliothèque Python h5py fournit des outils pour travailler avec des fichiers HDF5 et les charger en tant que structures de données NumPy. Cela ouvre la voie au traitement par lots des Jeux de données en accédant à des tranches ou segments spécifiques de données à la demande.

👨‍💻 Mise en œuvre:

import h5py
import numpy as np

# charger et traiter par lots les données du fichier HDF5
def load_data_in_batches(batch_size=10):
    # ouvrir un fichier HDF5
    with h5py.File("dataset.h5", "r") as f:
        inputs = f["input"]
        targets = f["target"]

        # regrouper les données par lots à l'aide d'un itérateur à partir du disque
        for i in range(0, len(data), batch_size):
            yield inputs[i:i+batch_size], targets[i:i+batch_size]

# itérer à travers les lots
for batch_data, batch_target in load_data_in_batches():
    print("Lot d'entrée :", batch_input)
    print("Lot cible :", batch_target)

# sortie :
# Lot d'entrée : [[ 1]
#  [ 2]
#  [ 3]
#  [ 4]
#  [ 5]
#  [ 6]
#  [ 7]
#  [ 8]
#  [ 9]
#  [10]]
# Lot cible : [[  1]
#  [  4]
#  [  9]
#  [ 16]
#  [ 25]
#  [ 36]
#  [ 49]
#  [ 64]
#  [ 81]
#  [100]]
#  ...
# Lot d'entrée : [[41]
#  [42]
#  [43]
#  [44]
#  [45]
#  [46]
#  [47]
#  [48]
#  [49]
#  [50]]
# Lot cible : [[1681]
#  [1764]
#  [1849]
#  [1936]
#  [2026]
#  [2116]
#  [2209]
#  [2304]
#  [2401]
#  [2500]]

🎯 Scénarios:

  • Idéal pour les très grands jeux de données qui ne peuvent pas être chargés entièrement en mémoire
  • Utile lorsque vous travaillez avec des données multidimensionnelles
  • Convient pour stocker et récupérer des données à partir d’un disque dans un format compressé efficace pour les tâches d’apprentissage automatique

🔠 Entrée:

  • Fichiers HDF5

👍 Avantages:

  • HDF5 prend en charge la compression et le découpage des données, ce qui réduit les besoins en stockage pour les grands jeux de données
  • Permet un accès aléatoire efficace à certaines parties de grands jeux de données sans tout charger en mémoire
  • Peut stocker plusieurs jeux de données dans un seul fichier, ce qui le rend particulièrement adapté aux jeux de données complexes.
  • Prise en charge par de nombreuses bibliothèques scientifiques, notamment NumPy, TensorFlow et PyTorch

👎 Inconvénients:

  • Nécessite une configuration supplémentaire et une connaissance du format HDF5.
  • Pour disposer d’une API complète permettant de traiter les fichiers HDF5, il faut recourir à la bibliothèque h5py
  • Tous les jeux de données ne sont pas disponibles au format HDF5

Autres solutions

Bien que les approches présentées ci-dessus comptent parmi les meilleures façons de diviser un jeu de données en lots, il existe également d’autres solutions viables.

Une autre solution possible consiste à utiliser la bibliothèque de Jeux de données Hugging Face. Celle-ci vous fournit tout ce dont vous avez besoin pour appliquer des transformations à l’ensemble d’un jeu de données tout en le traitant par lots. En définissant batched=True, vous pouvez définir des transformations au niveau des lots sans avoir à découper manuellement le jeu de données, comme dans l’exemple ci-dessous :

from Jeux de données import load_dataset

# charger un exemple de jeu de données
dataset = load_dataset("imdb", split="train")

# définir une fonction de traitement par lots
def process_batch(batch):
    # tâche de tokenisation simple
    return {"tokens": [text.split() for text in batch["text"]]}

# appliquer le traitement par lots
batched_dataset = dataset.map(process_batch, batched=True, batch_size=32)

L’option dataset map() batched=True est idéale lorsque vous devez appliquer des transformations, telles que la tokenisation, par lots.

Notez que l’utilisation de map(batched=True) est très efficace pour le traitement par lots, car elle minimise l’utilisation de la mémoire et accélère les workflows de transformation. Cette méthode est particulièrement utile pour traiter des données textuelles et tabulaires dans le cadre de tâches de NLP et d’apprentissage automatique.

Conclusion

Dans ce guide sur la manière de diviser un jeu de données en lots, vous avez découvert les meilleures approches, bibliothèques et solutions pour découper des données en Python. L’objectif est de diviser un jeu de données volumineux en parties plus faciles à gérer afin de simplifier et d’accélérer le traitement des données.

Quelle que soit l’approche choisie, toutes les solutions ci-dessus dépendent de l’accès à un jeu de données contenant les informations recherchées. Si certains jeux de données sont librement accessibles pour la recherche scientifique, ce n’est pas toujours le cas.

Si vous avez besoin d’ensembles de données couvrant des catégories allant de la finance aux données cinématographiques, consultez la plateforme Dataset Marketplace de Bright Data. Celle-ci donne accès à des centaines de jeux de données provenant de sites populaires, classés dans les catégories suivantes :

Si ces options prédéfinies ne répondent pas à vos besoins, envisagez nosservices de collecte de données personnalisés.

De plus, Bright Data propose une large gamme d’outils de scraping puissants, notamment des API Web Scraper et Navigateur de scraping.

Créez gratuitement un compte Bright Data pour commencer à explorer ces Jeux de données !