Voir sur TensorFlow.org | Exécuter dans Google Colab | Voir la source sur GitHub | Télécharger le cahier |
Ce didacticiel montre comment charger et prétraiter un jeu de données d'image de trois manières :
- Tout d'abord, vous utiliserez des utilitaires de prétraitement Keras de haut niveau (tels que
tf.keras.utils.image_dataset_from_directory
) et des couches (telles quetf.keras.layers.Rescaling
) pour lire un répertoire d'images sur le disque. - Ensuite, vous écrirez votre propre pipeline d'entrée à partir de zéro en utilisant tf.data .
- Enfin, vous téléchargerez un jeu de données à partir du vaste catalogue disponible dans TensorFlow Datasets .
Installer
import numpy as np
import os
import PIL
import PIL.Image
import tensorflow as tf
import tensorflow_datasets as tfds
print(tf.__version__)
2.8.0-rc1
Téléchargez le jeu de données sur les fleurs
Ce tutoriel utilise un jeu de données de plusieurs milliers de photos de fleurs. Le jeu de données flowers contient cinq sous-répertoires, un par classe :
flowers_photos/
daisy/
dandelion/
roses/
sunflowers/
tulips/
import pathlib
dataset_url = "https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz"
data_dir = tf.keras.utils.get_file(origin=dataset_url,
fname='flower_photos',
untar=True)
data_dir = pathlib.Path(data_dir)
Après le téléchargement (218 Mo), vous devriez maintenant avoir une copie des photos de fleurs disponibles. Il y a 3 670 images au total :
image_count = len(list(data_dir.glob('*/*.jpg')))
print(image_count)
3670
Chaque répertoire contient des images de ce type de fleur. Voici quelques roses :
roses = list(data_dir.glob('roses/*'))
PIL.Image.open(str(roses[0]))
roses = list(data_dir.glob('roses/*'))
PIL.Image.open(str(roses[1]))
Charger des données à l'aide d'un utilitaire Keras
Chargeons ces images hors disque à l'aide de l'utilitaire utile tf.keras.utils.image_dataset_from_directory
.
Créer un ensemble de données
Définissez quelques paramètres pour le chargeur :
batch_size = 32
img_height = 180
img_width = 180
Il est recommandé d'utiliser une division de validation lors du développement de votre modèle. Vous utiliserez 80% des images pour la formation et 20% pour la validation.
train_ds = tf.keras.utils.image_dataset_from_directory(
data_dir,
validation_split=0.2,
subset="training",
seed=123,
image_size=(img_height, img_width),
batch_size=batch_size)
Found 3670 files belonging to 5 classes. Using 2936 files for training.
val_ds = tf.keras.utils.image_dataset_from_directory(
data_dir,
validation_split=0.2,
subset="validation",
seed=123,
image_size=(img_height, img_width),
batch_size=batch_size)
Found 3670 files belonging to 5 classes. Using 734 files for validation.
Vous pouvez trouver les noms de classe dans l'attribut class_names
sur ces ensembles de données.
class_names = train_ds.class_names
print(class_names)
['daisy', 'dandelion', 'roses', 'sunflowers', 'tulips']
Visualisez les données
Voici les neuf premières images de l'ensemble de données d'entraînement.
import matplotlib.pyplot as plt
plt.figure(figsize=(10, 10))
for images, labels in train_ds.take(1):
for i in range(9):
ax = plt.subplot(3, 3, i + 1)
plt.imshow(images[i].numpy().astype("uint8"))
plt.title(class_names[labels[i]])
plt.axis("off")
Vous pouvez entraîner un modèle à l'aide de ces jeux de données en les transmettant à model.fit
(illustré plus loin dans ce didacticiel). Si vous le souhaitez, vous pouvez également parcourir manuellement l'ensemble de données et récupérer des lots d'images :
for image_batch, labels_batch in train_ds:
print(image_batch.shape)
print(labels_batch.shape)
break
(32, 180, 180, 3) (32,)
L' image_batch
est un tenseur de la forme (32, 180, 180, 3)
. Il s'agit d'un lot de 32 images de forme 180x180x3
(la dernière dimension fait référence aux canaux de couleur RVB). Le label_batch
est un tenseur de la forme (32,)
, ce sont des labels correspondants aux 32 images.
Vous pouvez appeler .numpy()
sur l'un de ces tenseurs pour les convertir en numpy.ndarray
.
Standardiser les données
Les valeurs des canaux RVB sont dans la plage [0, 255]
. Ce n'est pas idéal pour un réseau neuronal ; en général, vous devriez chercher à rendre vos valeurs d'entrée petites.
Ici, vous allez normaliser les valeurs pour qu'elles soient dans la plage [0, 1]
en utilisant tf.keras.layers.Rescaling
:
normalization_layer = tf.keras.layers.Rescaling(1./255)
Il existe deux façons d'utiliser cette couche. Vous pouvez l'appliquer au jeu de données en appelant Dataset.map
:
normalized_ds = train_ds.map(lambda x, y: (normalization_layer(x), y))
image_batch, labels_batch = next(iter(normalized_ds))
first_image = image_batch[0]
# Notice the pixel values are now in `[0,1]`.
print(np.min(first_image), np.max(first_image))
0.0 0.96902645
Ou, vous pouvez inclure la couche dans la définition de votre modèle pour simplifier le déploiement. Vous utiliserez ici la deuxième approche.
Configurer l'ensemble de données pour les performances
Assurons-nous d'utiliser la prélecture tamponnée afin que vous puissiez récupérer des données à partir du disque sans que les E/S ne deviennent bloquantes. Voici deux méthodes importantes que vous devez utiliser lors du chargement des données :
-
Dataset.cache
conserve les images en mémoire après leur chargement hors disque au cours de la première époque. Cela garantira que l'ensemble de données ne devienne pas un goulot d'étranglement lors de la formation de votre modèle. Si votre jeu de données est trop volumineux pour tenir en mémoire, vous pouvez également utiliser cette méthode pour créer un cache sur disque performant. -
Dataset.prefetch
chevauche le prétraitement des données et l'exécution du modèle pendant la formation.
Les lecteurs intéressés peuvent en savoir plus sur les deux méthodes, ainsi que sur la mise en cache des données sur le disque dans la section Prélecture du guide Meilleures performances avec l'API tf.data .
AUTOTUNE = tf.data.AUTOTUNE
train_ds = train_ds.cache().prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)
Former un modèle
Pour être complet, vous montrerez comment entraîner un modèle simple à l'aide des ensembles de données que vous venez de préparer.
Le modèle séquentiel se compose de trois blocs de convolution ( tf.keras.layers.Conv2D
) avec une couche de regroupement maximum ( tf.keras.layers.MaxPooling2D
) dans chacun d'eux. Il y a une couche entièrement connectée ( tf.keras.layers.Dense
) avec 128 unités dessus qui est activée par une fonction d'activation ReLU ( 'relu'
). Ce modèle n'a été ajusté d'aucune façon. L'objectif est de vous montrer les mécanismes à l'aide des ensembles de données que vous venez de créer. Pour en savoir plus sur la classification des images, consultez le didacticiel Classification des images .
num_classes = 5
model = tf.keras.Sequential([
tf.keras.layers.Rescaling(1./255),
tf.keras.layers.Conv2D(32, 3, activation='relu'),
tf.keras.layers.MaxPooling2D(),
tf.keras.layers.Conv2D(32, 3, activation='relu'),
tf.keras.layers.MaxPooling2D(),
tf.keras.layers.Conv2D(32, 3, activation='relu'),
tf.keras.layers.MaxPooling2D(),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(num_classes)
])
Choisissez l'optimiseur tf.keras.optimizers.Adam
et la fonction de perte tf.keras.losses.SparseCategoricalCrossentropy
. Pour afficher la précision de la formation et de la validation pour chaque époque de formation, transmettez l'argument metrics
à Model.compile
.
model.compile(
optimizer='adam',
loss=tf.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
model.fit(
train_ds,
validation_data=val_ds,
epochs=3
)
Epoch 1/3 92/92 [==============================] - 4s 21ms/step - loss: 1.3091 - accuracy: 0.4281 - val_loss: 1.0982 - val_accuracy: 0.5599 Epoch 2/3 92/92 [==============================] - 1s 12ms/step - loss: 1.0196 - accuracy: 0.5879 - val_loss: 0.9572 - val_accuracy: 0.6213 Epoch 3/3 92/92 [==============================] - 1s 12ms/step - loss: 0.8455 - accuracy: 0.6775 - val_loss: 0.8839 - val_accuracy: 0.6512 <keras.callbacks.History at 0x7ff10c168850>
Vous remarquerez peut-être que la précision de la validation est faible par rapport à la précision de l'entraînement, ce qui indique que votre modèle est surajusté. Vous pouvez en savoir plus sur le surajustement et comment le réduire dans ce didacticiel .
Utiliser tf.data pour un contrôle plus fin
L'utilitaire de prétraitement Keras ci-dessus - tf.keras.utils.image_dataset_from_directory
- est un moyen pratique de créer un tf.data.Dataset
à partir d'un répertoire d'images.
Pour un contrôle plus précis du grain, vous pouvez écrire votre propre pipeline d'entrée à l'aide tf.data
. Cette section montre comment faire exactement cela, en commençant par les chemins de fichier du fichier TGZ que vous avez téléchargé précédemment.
list_ds = tf.data.Dataset.list_files(str(data_dir/'*/*'), shuffle=False)
list_ds = list_ds.shuffle(image_count, reshuffle_each_iteration=False)
for f in list_ds.take(5):
print(f.numpy())
b'/home/kbuilder/.keras/datasets/flower_photos/roses/14267691818_301aceda07.jpg' b'/home/kbuilder/.keras/datasets/flower_photos/daisy/2641151167_3bf1349606_m.jpg' b'/home/kbuilder/.keras/datasets/flower_photos/sunflowers/6495554833_86eb8faa8e_n.jpg' b'/home/kbuilder/.keras/datasets/flower_photos/tulips/4578030672_e6aefd45af.jpg' b'/home/kbuilder/.keras/datasets/flower_photos/dandelion/144686365_d7e96941ee_n.jpg'
L'arborescence des fichiers peut être utilisée pour compiler une liste de class_names
de classes.
class_names = np.array(sorted([item.name for item in data_dir.glob('*') if item.name != "LICENSE.txt"]))
print(class_names)
['daisy' 'dandelion' 'roses' 'sunflowers' 'tulips']
Divisez l'ensemble de données en ensembles d'apprentissage et de validation :
val_size = int(image_count * 0.2)
train_ds = list_ds.skip(val_size)
val_ds = list_ds.take(val_size)
Vous pouvez imprimer la longueur de chaque ensemble de données comme suit :
print(tf.data.experimental.cardinality(train_ds).numpy())
print(tf.data.experimental.cardinality(val_ds).numpy())
2936 734
Écrivez une fonction courte qui convertit un chemin de fichier en une paire (img, label)
:
def get_label(file_path):
# Convert the path to a list of path components
parts = tf.strings.split(file_path, os.path.sep)
# The second to last is the class-directory
one_hot = parts[-2] == class_names
# Integer encode the label
return tf.argmax(one_hot)
def decode_img(img):
# Convert the compressed string to a 3D uint8 tensor
img = tf.io.decode_jpeg(img, channels=3)
# Resize the image to the desired size
return tf.image.resize(img, [img_height, img_width])
def process_path(file_path):
label = get_label(file_path)
# Load the raw data from the file as a string
img = tf.io.read_file(file_path)
img = decode_img(img)
return img, label
Utilisez Dataset.map
pour créer un ensemble de données de paires d' image, label
:
# Set `num_parallel_calls` so multiple images are loaded/processed in parallel.
train_ds = train_ds.map(process_path, num_parallel_calls=AUTOTUNE)
val_ds = val_ds.map(process_path, num_parallel_calls=AUTOTUNE)
for image, label in train_ds.take(1):
print("Image shape: ", image.numpy().shape)
print("Label: ", label.numpy())
Image shape: (180, 180, 3) Label: 1
Configurer l'ensemble de données pour les performances
Pour former un modèle avec cet ensemble de données, vous aurez besoin des données :
- Être bien mélangé.
- A mettre en lot.
- Les lots seront disponibles dès que possible.
Ces fonctionnalités peuvent être ajoutées à l'aide de l'API tf.data
. Pour plus de détails, consultez le guide sur les performances du pipeline d'entrée .
def configure_for_performance(ds):
ds = ds.cache()
ds = ds.shuffle(buffer_size=1000)
ds = ds.batch(batch_size)
ds = ds.prefetch(buffer_size=AUTOTUNE)
return ds
train_ds = configure_for_performance(train_ds)
val_ds = configure_for_performance(val_ds)
Visualisez les données
Vous pouvez visualiser cet ensemble de données de la même manière que celui que vous avez créé précédemment :
image_batch, label_batch = next(iter(train_ds))
plt.figure(figsize=(10, 10))
for i in range(9):
ax = plt.subplot(3, 3, i + 1)
plt.imshow(image_batch[i].numpy().astype("uint8"))
label = label_batch[i]
plt.title(class_names[label])
plt.axis("off")
2022-01-26 06:29:45.209901: W tensorflow/core/kernels/data/cache_dataset_ops.cc:768] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.
Continuer la formation du modèle
Vous avez maintenant créé manuellement un tf.data.Dataset
similaire à celui créé par tf.keras.utils.image_dataset_from_directory
ci-dessus. Vous pouvez continuer à entraîner le modèle avec. Comme auparavant, vous vous entraînerez pendant quelques époques seulement pour réduire le temps de course.
model.fit(
train_ds,
validation_data=val_ds,
epochs=3
)
Epoch 1/3 92/92 [==============================] - 3s 21ms/step - loss: 0.7305 - accuracy: 0.7245 - val_loss: 0.7311 - val_accuracy: 0.7139 Epoch 2/3 92/92 [==============================] - 1s 13ms/step - loss: 0.5279 - accuracy: 0.8069 - val_loss: 0.7021 - val_accuracy: 0.7316 Epoch 3/3 92/92 [==============================] - 1s 13ms/step - loss: 0.3739 - accuracy: 0.8644 - val_loss: 0.8266 - val_accuracy: 0.6948 <keras.callbacks.History at 0x7ff0ee071f10>
Utiliser des ensembles de données TensorFlow
Jusqu'à présent, ce didacticiel s'est concentré sur le chargement de données hors disque. Vous pouvez également trouver un ensemble de données à utiliser en explorant le vaste catalogue d'ensembles de données faciles à télécharger sur TensorFlow Datasets .
Comme vous avez précédemment chargé le jeu de données Flowers hors disque, importons-le maintenant avec les jeux de données TensorFlow.
Téléchargez l'ensemble de données Flowers à l'aide des ensembles de données TensorFlow :
(train_ds, val_ds, test_ds), metadata = tfds.load(
'tf_flowers',
split=['train[:80%]', 'train[80%:90%]', 'train[90%:]'],
with_info=True,
as_supervised=True,
)
Le jeu de données flowers a cinq classes :
num_classes = metadata.features['label'].num_classes
print(num_classes)
5
Récupérez une image de l'ensemble de données :
get_label_name = metadata.features['label'].int2str
image, label = next(iter(train_ds))
_ = plt.imshow(image)
_ = plt.title(get_label_name(label))
2022-01-26 06:29:54.281352: W tensorflow/core/kernels/data/cache_dataset_ops.cc:768] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.
Comme précédemment, n'oubliez pas de regrouper, de mélanger et de configurer les ensembles d'entraînement, de validation et de test pour les performances :
train_ds = configure_for_performance(train_ds)
val_ds = configure_for_performance(val_ds)
test_ds = configure_for_performance(test_ds)
Vous pouvez trouver un exemple complet d'utilisation de l'ensemble de données Flowers et des ensembles de données TensorFlow en consultant le didacticiel d'augmentation des données .
Prochaines étapes
Ce didacticiel a montré deux manières de charger des images à partir du disque. Tout d'abord, vous avez appris à charger et à prétraiter un jeu de données d'image à l'aide des couches et des utilitaires de prétraitement Keras. Ensuite, vous avez appris à écrire un pipeline d'entrée à partir de zéro en utilisant tf.data
. Enfin, vous avez appris à télécharger un ensemble de données à partir des ensembles de données TensorFlow.
Pour vos prochaines étapes :
- Vous pouvez apprendre à ajouter une augmentation de données .
- Pour en savoir plus sur
tf.data
, vous pouvez consulter le guide tf.data: Build TensorFlow input pipelines .