Trasferisci l'apprendimento con TensorFlow Hub

Visualizza su TensorFlow.org Esegui in Google Colab Visualizza su GitHub Scarica quaderno Vedi modello TF Hub

TensorFlow Hub è un repository di modelli TensorFlow pre-addestrati.

Questo tutorial mostra come:

  1. Usa i modelli di TensorFlow Hub con tf.keras .
  2. Utilizzare un modello di classificazione delle immagini da TensorFlow Hub.
  3. Esegui un semplice trasferimento di apprendimento per mettere a punto un modello per le tue classi di immagini.

Impostare

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 classificatore ImageNet

Inizierai utilizzando un modello di classificatore pre-addestrato sul set di dati benchmark ImageNet , senza necessità di formazione iniziale!

Scarica il classificatore

Seleziona un modello MobileNetV2 pre-addestrato da TensorFlow Hub e avvolgilo come un livello Keras con hub.KerasLayer . Qualsiasi modello di classificatore di immagini compatibile di TensorFlow Hub funzionerà qui, inclusi gli esempi forniti nel menu a discesa seguente.

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,))
])

Eseguilo su una singola immagine

Scarica una singola immagine su cui provare il modello:

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)

Aggiungi una dimensione batch (con np.newaxis ) e passa l'immagine al modello:

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

Il risultato è un vettore di logit a 1001 elementi, che valuta la probabilità di ciascuna classe per l'immagine.

L'ID della classe superiore può essere trovato con tf.math.argmax :

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

Decodifica le previsioni

Prendi il predicted_class ID (come 653 ) e recupera le etichette del set di dati ImageNet per decodificare le previsioni:

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

Semplice trasferimento di apprendimento

Ma cosa succede se si desidera creare un classificatore personalizzato utilizzando il proprio set di dati con classi che non sono incluse nel set di dati ImageNet originale (su cui è stato addestrato il modello pre-addestrato)?

Per farlo, puoi:

  1. Seleziona un modello pre-addestrato da TensorFlow Hub; e
  2. Riaddestra il livello superiore (ultimo) per riconoscere le classi dal tuo set di dati personalizzato.

Set di dati

In questo esempio, utilizzerai il set di dati dei fiori 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

Innanzitutto, carica questi dati nel modello utilizzando i dati dell'immagine fuori disco con tf.keras.utils.image_dataset_from_directory , che genererà 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.

Il set di dati dei fiori ha cinque classi:

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

In secondo luogo, poiché la convenzione di TensorFlow Hub per i modelli di immagine prevede input float nell'intervallo [0, 1] , utilizzare il livello di preelaborazione tf.keras.layers.Rescaling per ottenere questo risultato.

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.

In terzo luogo, termina la pipeline di input utilizzando il precaricamento nel buffer con Dataset.prefetch , in modo da poter restituire i dati dal disco senza problemi di blocco I/O.

Questi sono alcuni dei metodi tf.data più importanti che dovresti usare durante il caricamento dei dati. I lettori interessati possono saperne di più su di loro, nonché su come memorizzare nella cache i dati su disco e altre tecniche, nella guida all'API tf.data per prestazioni migliori .

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.

Esegui il classificatore su un batch di immagini

Ora, esegui il classificatore su un batch di immagini:

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')

Controlla come queste previsioni si allineano con le immagini:

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

I risultati sono tutt'altro che perfetti, ma ragionevoli considerando che queste non sono le classi per le quali il modello è stato addestrato (tranne "daisy").

Scarica il modello senza testa

TensorFlow Hub distribuisce anche modelli senza il livello di classificazione superiore. Questi possono essere utilizzati per eseguire facilmente l'apprendimento di trasferimento.

Seleziona un modello MobileNetV2 pre-addestrato da TensorFlow Hub . Qualsiasi modello vettoriale di funzionalità dell'immagine compatibile da TensorFlow Hub funzionerà qui, inclusi gli esempi dal menu a discesa.

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

Crea l'estrattore di funzionalità avvolgendo il modello pre-addestrato come un livello Keras con hub.KerasLayer . Usa l'argomento trainable=False per bloccare le variabili, in modo che l'addestramento modifichi solo il nuovo livello del classificatore:

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

L'estrattore di funzionalità restituisce un vettore lungo 1280 per ogni immagine (in questo esempio la dimensione del batch dell'immagine rimane a 32):

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

Allega una testata di classificazione

Per completare il modello, avvolgi il livello dell'estrattore di funzionalità in un modello tf.keras.Sequential e aggiungi un livello completamente connesso per la classificazione:

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])

Allena il modello

Utilizzare Model.compile per configurare il processo di addestramento e aggiungere un callback tf.keras.callbacks.TensorBoard per creare e archiviare i registri:

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.

Ora usa il metodo Model.fit per addestrare il modello.

Per mantenere questo esempio breve, ti allenerai solo per 10 epoche. Per visualizzare l'avanzamento della formazione in TensorBoard in un secondo momento, creare e archiviare i registri e una richiamata 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

Avvia TensorBoard per visualizzare come cambiano le metriche a ogni epoca e per tenere traccia di altri valori scalari:

%tensorboard --logdir logs/fit

Controlla le previsioni

Ottieni l'elenco ordinato dei nomi delle classi dalle previsioni del modello:

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']

Traccia le previsioni del modello:

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

Esporta e ricarica il tuo modello

Ora che hai addestrato il modello, esportalo come SavedModel per riutilizzarlo in seguito.

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'

Conferma che puoi ricaricare SavedModel e che il modello è in grado di produrre gli stessi risultati:

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

Prossimi passi

Puoi utilizzare SavedModel per caricare per l'inferenza o convertirlo in un modello TensorFlow Lite (per l'apprendimento automatico sul dispositivo) o in un modello TensorFlow.js (per l'apprendimento automatico in JavaScript).

Scopri altri tutorial per imparare a utilizzare i modelli pre-addestrati da TensorFlow Hub su attività di immagini, testo, audio e video.