Apprentissage par transfert avec TensorFlow Hub

Voir sur TensorFlow.org Exécuter dans Google Colab Afficher sur GitHub Télécharger le cahier Voir le modèle TF Hub

TensorFlow Hub est un référentiel de modèles TensorFlow pré-entraînés.

Ce didacticiel montre comment :

  1. Utilisez des modèles de TensorFlow Hub avec tf.keras .
  2. Utilisez un modèle de classification d'images de TensorFlow Hub.
  3. Effectuez un apprentissage par transfert simple pour affiner un modèle pour vos propres classes d'images.

Installer

import numpy as np
import time

import PIL.Image as Image
import matplotlib.pylab as plt

import tensorflow as tf
import tensorflow_hub as hub

import datetime

%load_ext tensorboard

Un classificateur ImageNet

Vous commencerez par utiliser un modèle de classificateur pré-formé sur l'ensemble de données de référence ImageNet - aucune formation initiale requise !

Télécharger le classificateur

Sélectionnez un modèle pré-entraîné MobileNetV2 à partir de TensorFlow Hub et encapsulez-le en tant que couche Keras avec hub.KerasLayer . Tout modèle de classificateur d'images compatible de TensorFlow Hub fonctionnera ici, y compris les exemples fournis dans le menu déroulant ci-dessous.

mobilenet_v2 ="https://tfhub.dev/google/tf2-preview/mobilenet_v2/classification/4"
inception_v3 = "https://tfhub.dev/google/imagenet/inception_v3/classification/5"

classifier_model = mobilenet_v2
IMAGE_SHAPE = (224, 224)

classifier = tf.keras.Sequential([
    hub.KerasLayer(classifier_model, input_shape=IMAGE_SHAPE+(3,))
])

Exécutez-le sur une seule image

Téléchargez une seule image pour essayer le modèle sur :

grace_hopper = tf.keras.utils.get_file('image.jpg','https://storage.googleapis.com/download.tensorflow.org/example_images/grace_hopper.jpg')
grace_hopper = Image.open(grace_hopper).resize(IMAGE_SHAPE)
grace_hopper
Downloading data from https://storage.googleapis.com/download.tensorflow.org/example_images/grace_hopper.jpg
65536/61306 [================================] - 0s 0us/step
73728/61306 [====================================] - 0s 0us/step

png

grace_hopper = np.array(grace_hopper)/255.0
grace_hopper.shape
(224, 224, 3)

Ajoutez une dimension de lot (avec np.newaxis ) et transmettez l'image au modèle :

result = classifier.predict(grace_hopper[np.newaxis, ...])
result.shape
(1, 1001)

Le résultat est un vecteur de logits à 1001 éléments, évaluant la probabilité de chaque classe pour l'image.

L'ID de classe supérieur peut être trouvé avec tf.math.argmax :

predicted_class = tf.math.argmax(result[0], axis=-1)
predicted_class
<tf.Tensor: shape=(), dtype=int64, numpy=653>

Décodez les prédictions

Prenez l'ID predicted_class (tel que 653 ) et récupérez les étiquettes de l'ensemble de données ImageNet pour décoder les prédictions :

labels_path = tf.keras.utils.get_file('ImageNetLabels.txt','https://storage.googleapis.com/download.tensorflow.org/data/ImageNetLabels.txt')
imagenet_labels = np.array(open(labels_path).read().splitlines())
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/ImageNetLabels.txt
16384/10484 [==============================================] - 0s 0us/step
24576/10484 [======================================================================] - 0s 0us/step
plt.imshow(grace_hopper)
plt.axis('off')
predicted_class_name = imagenet_labels[predicted_class]
_ = plt.title("Prediction: " + predicted_class_name.title())

png

Apprentissage par transfert simple

Mais que se passe-t-il si vous souhaitez créer un classificateur personnalisé à l'aide de votre propre ensemble de données contenant des classes qui ne sont pas incluses dans l'ensemble de données ImageNet d'origine (sur lequel le modèle pré-formé a été formé) ?

Pour ce faire, vous pouvez :

  1. Sélectionnez un modèle pré-formé à partir de TensorFlow Hub ; et
  2. Réentraînez la couche supérieure (dernière) pour reconnaître les classes de votre jeu de données personnalisé.

Base de données

Dans cet exemple, vous utiliserez l'ensemble de données de fleurs TensorFlow :

data_root = tf.keras.utils.get_file(
  'flower_photos',
  'https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz',
   untar=True)
Downloading data from https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz
228818944/228813984 [==============================] - 7s 0us/step
228827136/228813984 [==============================] - 7s 0us/step

Tout d'abord, chargez ces données dans le modèle en utilisant les données d'image hors disque avec tf.keras.utils.image_dataset_from_directory , qui générera un tf.data.Dataset :

batch_size = 32
img_height = 224
img_width = 224

train_ds = tf.keras.utils.image_dataset_from_directory(
  str(data_root),
  validation_split=0.2,
  subset="training",
  seed=123,
  image_size=(img_height, img_width),
  batch_size=batch_size
)

val_ds = tf.keras.utils.image_dataset_from_directory(
  str(data_root),
  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 2936 files for training.
Found 3670 files belonging to 5 classes.
Using 734 files for validation.

Le jeu de données flowers a cinq classes :

class_names = np.array(train_ds.class_names)
print(class_names)
['daisy' 'dandelion' 'roses' 'sunflowers' 'tulips']

Deuxièmement, étant donné que la convention de TensorFlow Hub pour les modèles d'image est d'attendre des entrées flottantes dans la plage [0, 1] , utilisez la couche de prétraitement tf.keras.layers.Rescaling pour y parvenir.

normalization_layer = tf.keras.layers.Rescaling(1./255)
train_ds = train_ds.map(lambda x, y: (normalization_layer(x), y)) # Where x—images, y—labels.
val_ds = val_ds.map(lambda x, y: (normalization_layer(x), y)) # Where x—images, y—labels.

Troisièmement, terminez le pipeline d'entrée en utilisant la prélecture tamponnée avec Dataset.prefetch , afin que vous puissiez produire les données du disque sans problèmes de blocage d'E/S.

Voici quelques-unes des méthodes tf.data les plus importantes que vous devez utiliser lors du chargement de données. Les lecteurs intéressés peuvent en savoir plus à leur sujet, ainsi que sur la façon de mettre en cache des données sur le disque et d'autres techniques, dans le 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)
for image_batch, labels_batch in train_ds:
  print(image_batch.shape)
  print(labels_batch.shape)
  break
(32, 224, 224, 3)
(32,)
2022-01-26 05:06:19.465331: 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.

Exécuter le classifieur sur un lot d'images

Maintenant, exécutez le classificateur sur un lot d'images :

result_batch = classifier.predict(train_ds)
predicted_class_names = imagenet_labels[tf.math.argmax(result_batch, axis=-1)]
predicted_class_names
array(['daisy', 'coral fungus', 'rapeseed', ..., 'daisy', 'daisy',
       'birdhouse'], dtype='<U30')

Vérifiez comment ces prédictions s'alignent sur les images :

plt.figure(figsize=(10,9))
plt.subplots_adjust(hspace=0.5)
for n in range(30):
  plt.subplot(6,5,n+1)
  plt.imshow(image_batch[n])
  plt.title(predicted_class_names[n])
  plt.axis('off')
_ = plt.suptitle("ImageNet predictions")

png

Les résultats sont loin d'être parfaits, mais raisonnables étant donné que ce ne sont pas les classes pour lesquelles le modèle a été formé (sauf pour "marguerite").

Télécharger le modèle sans tête

TensorFlow Hub distribue également des modèles sans la couche de classification supérieure. Ceux-ci peuvent être utilisés pour effectuer facilement un apprentissage par transfert.

Sélectionnez un modèle pré-entraîné MobileNetV2 à partir de TensorFlow Hub . Tout modèle vectoriel de caractéristiques d'image compatible de TensorFlow Hub fonctionnera ici, y compris les exemples du menu déroulant.

mobilenet_v2 = "https://tfhub.dev/google/tf2-preview/mobilenet_v2/feature_vector/4"
inception_v3 = "https://tfhub.dev/google/tf2-preview/inception_v3/feature_vector/4"

feature_extractor_model = mobilenet_v2

Créez l'extracteur de caractéristiques en enveloppant le modèle pré-formé en tant que couche Keras avec hub.KerasLayer . Utilisez l'argument trainable=False pour geler les variables, de sorte que la formation ne modifie que la nouvelle couche de classificateur :

feature_extractor_layer = hub.KerasLayer(
    feature_extractor_model,
    input_shape=(224, 224, 3),
    trainable=False)

L'extracteur de caractéristiques renvoie un vecteur de 1 280 longs pour chaque image (la taille du lot d'images reste à 32 dans cet exemple) :

feature_batch = feature_extractor_layer(image_batch)
print(feature_batch.shape)
(32, 1280)

Joindre un chef de classement

Pour terminer le modèle, encapsulez la couche d'extraction d'entités dans un modèle tf.keras.Sequential et ajoutez une couche entièrement connectée pour la classification :

num_classes = len(class_names)

model = tf.keras.Sequential([
  feature_extractor_layer,
  tf.keras.layers.Dense(num_classes)
])

model.summary()
Model: "sequential_1"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 keras_layer_1 (KerasLayer)  (None, 1280)              2257984   
                                                                 
 dense (Dense)               (None, 5)                 6405      
                                                                 
=================================================================
Total params: 2,264,389
Trainable params: 6,405
Non-trainable params: 2,257,984
_________________________________________________________________
predictions = model(image_batch)
predictions.shape
TensorShape([32, 5])

Former le modèle

Utilisez Model.compile pour configurer le processus de formation et ajoutez un rappel tf.keras.callbacks.TensorBoard pour créer et stocker des journaux :

model.compile(
  optimizer=tf.keras.optimizers.Adam(),
  loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
  metrics=['acc'])

log_dir = "logs/fit/" + datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
tensorboard_callback = tf.keras.callbacks.TensorBoard(
    log_dir=log_dir,
    histogram_freq=1) # Enable histogram computation for every epoch.

Utilisez maintenant la méthode Model.fit pour entraîner le modèle.

Pour que cet exemple soit court, vous ne vous entraînerez que pendant 10 époques. Pour visualiser ultérieurement la progression de l'entraînement dans TensorBoard, créez et stockez des journaux dans un rappel TensorBoard .

NUM_EPOCHS = 10

history = model.fit(train_ds,
                    validation_data=val_ds,
                    epochs=NUM_EPOCHS,
                    callbacks=tensorboard_callback)
Epoch 1/10
92/92 [==============================] - 7s 42ms/step - loss: 0.7904 - acc: 0.7210 - val_loss: 0.4592 - val_acc: 0.8515
Epoch 2/10
92/92 [==============================] - 3s 33ms/step - loss: 0.3850 - acc: 0.8713 - val_loss: 0.3694 - val_acc: 0.8787
Epoch 3/10
92/92 [==============================] - 3s 33ms/step - loss: 0.3027 - acc: 0.9057 - val_loss: 0.3367 - val_acc: 0.8856
Epoch 4/10
92/92 [==============================] - 3s 33ms/step - loss: 0.2524 - acc: 0.9237 - val_loss: 0.3210 - val_acc: 0.8869
Epoch 5/10
92/92 [==============================] - 3s 33ms/step - loss: 0.2164 - acc: 0.9373 - val_loss: 0.3124 - val_acc: 0.8896
Epoch 6/10
92/92 [==============================] - 3s 33ms/step - loss: 0.1888 - acc: 0.9469 - val_loss: 0.3070 - val_acc: 0.8937
Epoch 7/10
92/92 [==============================] - 3s 33ms/step - loss: 0.1668 - acc: 0.9550 - val_loss: 0.3032 - val_acc: 0.9005
Epoch 8/10
92/92 [==============================] - 3s 33ms/step - loss: 0.1487 - acc: 0.9619 - val_loss: 0.3004 - val_acc: 0.9005
Epoch 9/10
92/92 [==============================] - 3s 33ms/step - loss: 0.1335 - acc: 0.9687 - val_loss: 0.2981 - val_acc: 0.9019
Epoch 10/10
92/92 [==============================] - 3s 33ms/step - loss: 0.1206 - acc: 0.9748 - val_loss: 0.2964 - val_acc: 0.9046

Démarrez le TensorBoard pour voir comment les métriques changent à chaque époque et pour suivre d'autres valeurs scalaires :

%tensorboard --logdir logs/fit

Vérifiez les pronostics

Obtenez la liste ordonnée des noms de classe à partir des prédictions du modèle :

predicted_batch = model.predict(image_batch)
predicted_id = tf.math.argmax(predicted_batch, axis=-1)
predicted_label_batch = class_names[predicted_id]
print(predicted_label_batch)
['roses' 'dandelion' 'tulips' 'sunflowers' 'dandelion' 'roses' 'dandelion'
 'roses' 'tulips' 'dandelion' 'tulips' 'tulips' 'sunflowers' 'tulips'
 'dandelion' 'roses' 'daisy' 'tulips' 'dandelion' 'dandelion' 'dandelion'
 'tulips' 'sunflowers' 'roses' 'sunflowers' 'dandelion' 'tulips' 'roses'
 'roses' 'sunflowers' 'tulips' 'sunflowers']

Tracez les prédictions du modèle :

plt.figure(figsize=(10,9))
plt.subplots_adjust(hspace=0.5)

for n in range(30):
  plt.subplot(6,5,n+1)
  plt.imshow(image_batch[n])
  plt.title(predicted_label_batch[n].title())
  plt.axis('off')
_ = plt.suptitle("Model predictions")

png

Exportez et rechargez votre modèle

Maintenant que vous avez formé le modèle, exportez-le en tant que modèle enregistré pour le réutiliser ultérieurement.

t = time.time()

export_path = "/tmp/saved_models/{}".format(int(t))
model.save(export_path)

export_path
2022-01-26 05:07:03.429901: W tensorflow/python/util/util.cc:368] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
INFO:tensorflow:Assets written to: /tmp/saved_models/1643173621/assets
INFO:tensorflow:Assets written to: /tmp/saved_models/1643173621/assets
'/tmp/saved_models/1643173621'

Confirmez que vous pouvez recharger le SavedModel et que le modèle est capable de générer les mêmes résultats :

reloaded = tf.keras.models.load_model(export_path)
result_batch = model.predict(image_batch)
reloaded_result_batch = reloaded.predict(image_batch)
abs(reloaded_result_batch - result_batch).max()
0.0
reloaded_predicted_id = tf.math.argmax(reloaded_result_batch, axis=-1)
reloaded_predicted_label_batch = class_names[reloaded_predicted_id]
print(reloaded_predicted_label_batch)
['roses' 'dandelion' 'tulips' 'sunflowers' 'dandelion' 'roses' 'dandelion'
 'roses' 'tulips' 'dandelion' 'tulips' 'tulips' 'sunflowers' 'tulips'
 'dandelion' 'roses' 'daisy' 'tulips' 'dandelion' 'dandelion' 'dandelion'
 'tulips' 'sunflowers' 'roses' 'sunflowers' 'dandelion' 'tulips' 'roses'
 'roses' 'sunflowers' 'tulips' 'sunflowers']
plt.figure(figsize=(10,9))
plt.subplots_adjust(hspace=0.5)
for n in range(30):
  plt.subplot(6,5,n+1)
  plt.imshow(image_batch[n])
  plt.title(reloaded_predicted_label_batch[n].title())
  plt.axis('off')
_ = plt.suptitle("Model predictions")

png

Prochaines étapes

Vous pouvez utiliser le SavedModel pour le charger à des fins d'inférence ou le convertir en un modèle TensorFlow Lite (pour l'apprentissage automatique sur appareil) ou un modèle TensorFlow.js (pour l'apprentissage automatique en JavaScript).

Découvrez d' autres didacticiels pour apprendre à utiliser des modèles pré-entraînés de TensorFlow Hub sur des tâches d'image, de texte, d'audio et de vidéo.