Aller au contenu principal

🗂️ Fichiers HDF5 (h5py)

Un fichier HDF5 (.h5) permet de stocker des tableaux NumPy (images, étiquettes, métadonnées) dans un seul fichier.

En apprentissage machine, c'est pratique pour :

  • regrouper les donnĂ©es d'entraĂ®nement dans un format compact ;
  • lire/Ă©crire de très gros jeux de donnĂ©es sans tout charger en mĂ©moire vive.

1. Import​

import h5py

2. Créer un fichier .h5​

Exemple avec des images 28x28 en niveaux de gris et des étiquettes de classes.

import numpy as np

# Exemple de données
x_train = np.random.rand(1000, 28, 28, 1).astype("float32")
y_train = np.random.randint(0, 10, size=(1000,), dtype="int32")
x_valid = np.random.rand(500, 28, 28, 1).astype("float32")
y_valid = np.random.randint(0, 10, size=(500,), dtype="int32")

# Création du fichier HDF5
with h5py.File("dataset.h5", "w") as f:
f.create_dataset("x_train", data=x_train)
f.create_dataset("y_train", data=y_train)
f.create_dataset("x_valid", data=x_valid)
f.create_dataset("y_valid", data=y_valid)

# Optionnel : métadonnées utiles
f.attrs["description"] = "Jeu de données de démonstration pour Keras"
f.attrs["nb_classes"] = 10

Ce code crée un fichier dataset.h5 avec :

  • un dataset x_train ;
  • un dataset y_train ;
  • un dataset x_valid ;
  • un dataset y_valid ;
  • des attributs (mĂ©tadonnĂ©es) du fichier.

3. Relire le fichier .h5​

with h5py.File("dataset.h5", "r") as f:
x_train = f["x_train"][:] # On charge tout en mémoire vive
y_train = f["y_train"][:]
x_valid = f["x_valid"][:]
y_valid = f["y_valid"][:]
print(f.attrs["description"])

print(x_train.shape) # (1000, 28, 28, 1)
print(y_train.shape) # (1000,)
print(x_valid.shape) # (500, 28, 28, 1)
print(y_valid.shape) # (500,)

On peut ensuite utiliser les variables x_train et y_train directement avec la fonction fit de Keras.

attention

En faisant f["x_train"][:], on charge tout l'ensemble de données en mémoire vive.


4. Gros jeux de données : blocs de données (chunks)​

Quand un jeu de données est trop gros pour la RAM, on n'utilise pas [:] (chargement complet).

En HDF5, on peut stocker les données en blocs (chunks) :

astuce

Il est judicieux de choisir comme taille des chunks un multiple de la taille des minibatchs afin de s'assurer qu'ils soient bien alignés (réduction du nombre de lectures).

attention

Des chunks trop petits multiplient les accès disque. Des chunks trop gros consomment plus de mémoire à chaque lecture.

danger

Il est important de mélanger les données pour remplir le fichier .h5 (les minibatchs seront définies par l'ordre des données dans le fichier) et d'utiliser shuffle=False comme argument de la fonction fit (pour éviter des lectures inutiles).

with h5py.File("dataset_gros.h5", "w") as f:
# Ici un chunk correspond Ă  1000 exemples
dset = f.create_dataset("x_train", shape = (50000, 128, 128, 3), chunks=(1000, 128, 128, 3))
dset_y = f.create_dataset("y_train", shape = (50000), chunks=(1000,))
# On peut remplir les données du fichier .h5 séquentiellement comme cela :
for i in range(0, 50000, 1000):
dset[i:i+1000] = np.random.rand(1000, 128, 128, 3).astype("float32")
dset_y[i:i+1000] = np.random.randint(0, 10, size=(1000,), dtype="int32")

# dataset_gros.h5 fait 8 Go en tout

# Exemple de lecture partielle
with h5py.File("dataset_gros.h5", "r") as f:
print(f["x_train"][1000:1500])
# On ne chargera en mémoire vive qu'un chunk de 1000 exemples pour extraire ces données

Comment utiliser les chunks avec Keras​

Si on fournit à la méthode fit un objet qui retourne une minibatch à la fois, Keras appelle cet objet automatiquement à chaque itération. On peut donc lire un bloc HDF5 par minibatch sans charger tout le fichier. Pour faire cela, nous créons ici une nouvelle classe HDF5Sequence qui hérite de la classe keras.utils.Sequence et qui permet de faire une lecture séquentielle des blocs du fichier .h5.

import keras

# Création de notre classe personnalisée permettant de lire séquentiellement nos données
class HDF5Sequence(keras.utils.Sequence):
# La méthode __init__ permet d'initialiser nos objets de cette classe (c'est le constructeur)
def __init__(self, h5_path, batch_size=100):
self.h5_path = h5_path
self.batch_size = batch_size
with h5py.File(self.h5_path, "r") as f:
self.n = len(f["x_train"])

# Méthode retournant le nombre de minibatchs dans nos données
def __len__(self):
return int(np.ceil(self.n / self.batch_size))

# Méthode retournant la minibatch indexée par idx
def __getitem__(self, idx):
start = idx * self.batch_size
end = min(start + self.batch_size, self.n)
with h5py.File(self.h5_path, "r") as f:
x = f["x_train"][start:end] # cela ne va charger que le chunk correspondant
y = f["y_train"][start:end] # cela ne va charger que le chunk correspondant
return x, y

train_data = HDF5Sequence("dataset_gros.h5", batch_size=100)
model.fit(train_data, epochs=10)

Dans cet exemple :

  • L'objet HDF5Sequence fournit les minibatchs ;
  • Keras orchestre automatiquement les epochs ;
  • la mĂ©moire vive reste plus stable, car on ne charge qu'une petite partie des donnĂ©es Ă  la fois.
remarque

Lorsque model.fit reçoit un objet Sequence, c'est cet objet qui décide comment chaque minibatch est lue. L'argument batch_size de fit n'est alors pas utilisé (la taille des minibatchs est définie dans HDF5Sequence).