Introduction aux TFRecords
Pour l'entraînement de modèles d'apprentissage automatique avec TensorFlow, la gestion efficace des données est primordiale. Les fichiers TFRecord constituent un format binaire standardisé par TensorFlow, optimisé pour le stockage de grands ensembles de données. Ce format permet de sérialiser des données complexes, telles que des images ou des caractéristiques numériques, ainsi que leurs étiquettes associées, en un seul fichier. L'un des principaux avantages des TFRecords est leur capacité à être lus et traités de manière rapide et optimisée pour le streaming, ce qui est particulièrement bénéfique pour les pipelines d'entraînement intensifs en données.
Contrairement à d'autres formats, les TFRecords ne compressent pas les données par défaut, ce qui permet un chargement rapide en mémoire. Ils sont idéaux pour le traitement séquentiel de vastes volumes de données, bien qu'ils ne soient pas conçus pour un accès aléatoire rapide à des éléments individuels.
Concepts Fondamentaux des TFRecords
Le format TFRecord repose sur une structure hiérarchique qui encapsule les données brutes de manière structurée. Les composants clés sont tf.train.Feature, tf.train.Features et tf.train.Example.
tf.train.Feature : Le Conteneur de Données Individuel
Une tf.train.Feature est une structure simple qui peut contenir une liste de types de données spécifiques :
tf.train.BytesList: pour les données binaires (chaînes de caractères, images encodées, etc.).tf.train.FloatList: pour les nombres à virgule flottante.tf.train.Int64List: pour les entiers.
Lors de la création d'une Feature, vous encapsluez votre valeur dans la liste appropriée. Par exemple :
# Exemple de création d'une Feature pour un entier ou des octets
feature_entier = tf.train.Feature(int64_list=tf.train.Int64List(value=[42]))
feature_octets = tf.train.Feature(bytes_list=tf.train.BytesList(value=[b'donnees_binaires']))
tf.train.Features : Une Collection de Caractéristiques
tf.train.Features est un dictionnaire qui regroupe plusieurs objets tf.train.Feature. Chaque clé du dictionnaire est une chaîne de caractères représentant le nom de la caractéristique (par exemple, "image_brute", "etiquette_classe"), et chaque valeur est un objet tf.train.Feature.
# Construction d'un dictionnaire de caractéristiques
dict_caracteristiques = {
"identifiant": tf.train.Feature(int64_list=tf.train.Int64List(value=[123])),
"donnees_seriees": tf.train.Feature(bytes_list=tf.train.BytesList(value=[b'un_blob_de_donnees']))
}
collection_caracteristiques = tf.train.Features(feature=dict_caracteristiques)
tf.train.Example : L'Unité de Données Sérialisable
L'objet tf.train.Example est le conteneur de plus haut niveau pour une seule entrée de données. Il contient un objet tf.train.Features. L'importance de tf.train.Example réside dans sa méthode SerializeToString(), qui convertit l'objet en une chaîne d'octets binaire. C'est cette chaîne qui est ensuite écrite dans le fichier TFRecord.
# Création d'un exemple complet et sérialisation
exemple_donnees = tf.train.Example(features=collection_caracteristiques)
exemple_serialise = exemple_donnees.SerializeToString()
Pour la lecture, l'opération inverse tf.train.Example.FromString(chaine_octets) permet de désérialiser la chaîne d'octets en un objet tf.train.Example.
Génération d'un Fichier TFRecord à partir d'Images
L'écriture de données dans un fichier TFRecord implique la collecte des données brutes, leur conversion en types de données compatibles avec tf.train.Feature, puis leur sérialisation. L'exemple suivant montre comment créer un fichier TFRecord à partir d'un ensemble d'images organisées en sous-dossiers représentant des catégories.
Fonctions d'aide pour les Feature
Pour simplifier la création des objets tf.train.Feature, on peut définir des fonctions utilitaires :
import tensorflow as tf
import os
from PIL import Image
from pathlib import Path
def _bytes_feature(value):
"""Convertit une valeur en octets en tf.train.Feature (BytesList)."""
if isinstance(value, type(tf.constant(0))): # Gère les Tensors
value = value.numpy()
return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))
def _int64_feature(value):
"""Convertit une valeur entière en tf.train.Feature (Int64List)."""
return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))
def creer_exemple_image(image_bytes, etiquette):
"""Crée un tf.train.Example pour une image brute et son étiquette."""
caracteristiques = {
'image_brute': _bytes_feature(image_bytes),
'etiquette_classe': _int64_feature(etiquette)
}
return tf.train.Example(features=tf.train.Features(feature=caracteristiques))
Script de conversion d'images en TFRecord
Ce script parcourt un répertoire contenant des images classées dans des sous-dossiers nommés par leur catégorie numérique et génère un fichier TFRecord.
def convertir_images_en_tfrecords(chemin_dossier_images, nom_fichier_tfrecords, taille_image=(224, 224)):
"""
Parcourt un dossier d'images structuré par catégories (sous-dossiers numériques)
et les sérialise dans un fichier TFRecord.
"""
chemin_racine = Path(chemin_dossier_images)
if not chemin_racine.exists():
raise FileNotFoundError(f"Le dossier '{chemin_dossier_images}' n'existe pas.")
print(f"Génération du fichier TFRecord : {nom_fichier_tfrecords}...")
with tf.io.TFRecordWriter(nom_fichier_tfrecords) as enregistreur:
for categorie_dossier in sorted(chemin_racine.iterdir()):
if categorie_dossier.is_dir() and categorie_dossier.name.isdigit():
etiquette_numerique = int(categorie_dossier.name)
print(f" - Traitement de la catégorie {etiquette_numerique}...")
for fichier_image in categorie_dossier.glob("*.jpg"): # Peut être ajusté pour d'autres formats
try:
with Image.open(fichier_image) as img:
# Redimensionnement et conversion en octets
img_redimensionnee = img.resize(taille_image)
donnees_image_brutes = img_redimensionnee.tobytes()
exemple = creer_exemple_image(donnees_image_brutes, etiquette_numerique)
enregistreur.write(exemple.SerializeToString())
except Exception as e:
print(f" ! Erreur lors du traitement de l'image {fichier_image.name}: {e}")
print(f"Fichier TFRecord '{nom_fichier_tfrecords}' généré avec succès.")
# Exemple d'utilisation (créer un dossier 'data' avec des sous-dossiers '1', '2', etc.)
# convertir_images_en_tfrecords("./data", "mon_dataset_images.tfrecords")
Lecture et Décodage des Données TFRecord
Après la création du fichier TFRecord, l'étape suivente consiste à le lire et à désérialiser les exemples qu'il contient pour les utiliser dans un modèle TensorFlow. Le moyen le plus moderne et efficace de le faire est via l'API tf.data.Dataset.
Fonction d'analyse pour les exemples sérialisés
Cette fonction est responsable de prendre une chaîne d'octets sérialisée (un exemple TFRecord) et de la convertir en un Tensor d'image et un Tensor d'étiquette.
def analyser_exemple_tfrecord(exemple_serie):
"""
Analyse un exemple TFRecord sérialisé pour en extraire l'image et l'étiquette.
"""
# Définition de la spécification des caractéristiques
specification_features = {
'image_brute': tf.io.FixedLenFeature([], tf.string),
'etiquette_classe': tf.io.FixedLenFeature([], tf.int64),
}
donnees_parsees = tf.io.parse_single_example(exemple_serie, specification_features)
# Décodage de l'image brute en un Tensor et normalisation
image_decodee = tf.io.decode_raw(donnees_parsees['image_brute'], tf.uint8)
image_redimensionnee = tf.reshape(image_decodee, [224, 224, 3]) # Assumer 224x224x3 pour RGB
image_normalisee = tf.cast(image_redimensionnee, tf.float32) / 255.0 # Normalisation [0,1]
# Décodage de l'étiquette
etiquette_numerique = tf.cast(donnees_parsees['etiquette_classe'], tf.int64)
return image_normalisee, etiquette_numerique
Création d'un pipeline de données avec tf.data.Dataset
L'API tf.data.Dataset permet de construire des pipelines d'entrée robustes, performants et flexibles. Elle gère le chargement, le pré-traitement, le mélange et le lotissement des données.
def creer_pipeline_dataset_tfrecords(chemin_fichier_tfrecord, taille_batch, melanger=True, buffer_melange=1000):
"""
Crée un pipeline de données TensorFlow Dataset à partir d'un fichier TFRecord,
incluant le mélange et le lotissement.
"""
# Crée un Dataset à partir du fichier TFRecord
dataset = tf.data.TFRecordDataset(chemin_fichier_tfrecord)
# Mélanger les données si requis
if melanger:
dataset = dataset.shuffle(buffer_melange)
# Appliquer la fonction d'analyse à chaque exemple du dataset
dataset = dataset.map(analyser_exemple_tfrecord, num_parallel_calls=tf.data.AUTOTUNE)
# Créer des lots de données
dataset = dataset.batch(taille_batch)
# Préfétcher les données pour optimiser les performances d'entrée/sortie
dataset = dataset.prefetch(buffer_size=tf.data.AUTOTUNE)
return dataset
Intégration dans un Pipeline d'Entraînement
L'utilisation du pipeline tf.data.Dataset dans une boucle d'entraînement (ici simulée avec une session TensorFlow 1.x pour rester proche de l'esprit de l'article original, bien que tf.data soit idéal pour TensorFlow 2.x en mode eager) est simple et efficace.
# Configuration pour TensorFlow 1.x (si nécessaire)
tf.compat.v1.disable_eager_execution()
def simuler_boucle_entrainement(chemin_fichier_tfrecord, taille_batch, nb_pas_simulation=3):
"""
Simule une boucle d'entraînement simple en utilisant tf.data.Dataset
dans un environnement TensorFlow 1.x (mode graphique).
"""
dataset_entrainement = creer_pipeline_dataset_tfrecords(chemin_fichier_tfrecord, taille_batch)
# Création d'un itérateur pour la session TensorFlow 1.x
iterateur = tf.compat.v1.data.make_one_shot_iterator(dataset_entrainement)
prochain_element_lot = iterateur.get_next()
print("Démarrage de la simulation d'entraînement...")
with tf.compat.v1.Session() as sess:
# Aucun tf.global_variables_initializer() n'est strictement nécessaire ici
# car nous ne simulons pas un modèle complet avec des variables entraînables.
for i in range(nb_pas_simulation):
try:
images_lot, etiquettes_lot = sess.run(prochain_element_lot)
print(f"\nÉtape de simulation {i+1}:")
print(f" Forme du lot d'images: {images_lot.shape}")
print(f" Forme du lot d'étiquettes: {etiquettes_lot.shape}")
# Ici, on intégrerait les opérations d'entraînement (forward/backward pass)
# avec un modèle TensorFlow.
except tf.errors.OutOfRangeError:
print("\nFin du dataset : tous les exemples ont été traités.")
break
print("\nFin de la simulation de la boucle d'entraînement.")
# Exemple d'exécution (assurez-vous que 'mon_dataset_images.tfrecords' existe)
# simuler_boucle_entrainement("mon_dataset_images.tfrecords", taille_batch=5)
Ce pipeline illustre comment les TFRecords peuvent être effficacement utilisés pour alimenter un modèle TensorFlow en données, en tirant parti des fonctionnalités avancées de l'API tf.data.Dataset pour une gestion optimale du chargement et du pré-traitement.