Afin de faciliter la lecture du code, il en organisé sous forme d’onglets, à lire de gauche à droite !
Imports
On importe aussi tensorflow_hub car on effectue du transfer learning à partir d’un modèle disponible sur le Hub.
import os
import matplotlib.pylab as plt
import numpy as np
import tensorflow as tf
import tensorflow_hub as hub
print("Version: ", tf.__version__)
print("Eager mode: ", tf.executing_eagerly())
print("Hub version: ", hub.__version__)
print("GPU is", "available" if tf.config.list_physical_devices('GPU') else "NOT AVAILABLE")
On se met en mode GPU, sur Google Colab, car le modèle est complexe et à base de convolutions.
Version: 2.3.0
Eager mode: True
Hub version: 0.9.0
GPU is available
Un widget propose de choisir son modèle.
Gardons celui proposé par défaut : mobilenet_v2.
Il est décrit de la façon suivante :
- tf2-preview/mobilenet_v2/feature_vector
- Feature vectors of images with MobileNet V2 trained on ImageNet (ILSVRC-2012-CLS)
Le modèle a été entraîné sur ImageNet, avec des images au format 224*224.
Il fait 9,9K. Il est au format SavedModel.
FV_SIZE est la Feature Vectors Size.
The output of the module is a batch of feature vectors. For each input image, the feature vector has size
https://tfhub.dev/google/tf2-preview/mobilenet_v2/feature_vector/4num_features
= 1280. The feature vectors can then be used further, e.g., for classification as above.
module_selection = ("mobilenet_v2", 224, 1280) #@param ["(\"mobilenet_v2\", 224, 1280)", "(\"inception_v3\", 299, 2048)"] {type:"raw", allow-input: true}
handle_base, pixels, FV_SIZE = module_selection
MODULE_HANDLE ="https://tfhub.dev/google/tf2-preview/{}/feature_vector/4".format(handle_base)
IMAGE_SIZE = (pixels, pixels)
print("Using {} with input size {} and output dimension {}".format(
MODULE_HANDLE, IMAGE_SIZE, FV_SIZE))
Data Load
Les données sont importées de tfds.
import tensorflow_datasets as tfds
tfds.disable_progress_bar()
Un split est effectué pour séparer les données en train/validation/test
(train_examples, validation_examples, test_examples), info = tfds.load(
'cats_vs_dogs',
split=['train[80%:]', 'train[80%:90%]', 'train[90%:]'],
with_info=True,
as_supervised=True,
)
num_examples = info.splits['train'].num_examples
num_classes = info.features['label'].num_classes
print (num_examples, num_classes)
23262 2
Preprocessing
Ensuite on shuffle et batch les datasets.
def format_image(image, label):
image = tf.image.resize(image, IMAGE_SIZE) / 255.0
return image, label
BATCH_SIZE = 32 #@param {type:"integer"}
train_batches = train_examples.shuffle(num_examples // 4).map(format_image).batch(BATCH_SIZE).prefetch(1)
validation_batches = validation_examples.map(format_image).batch(BATCH_SIZE).prefetch(1)
test_batches = test_examples.map(format_image).batch(1)
Le modèle
Commençons avec un freeze de toutes les couches.
do_fine_tuning = False #@param {type:"boolean"}
feature_extractor = hub.KerasLayer(MODULE_HANDLE,
input_shape=IMAGE_SIZE + (3,),
output_shape=[FV_SIZE],
trainable=do_fine_tuning)
print("Building model with", MODULE_HANDLE)
model = tf.keras.Sequential([
feature_extractor,
tf.keras.layers.Dense(num_classes)
])
model.summary()
Building model with https://tfhub.dev/google/tf2-preview/mobilenet_v2/feature_vector/4
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
keras_layer_1 (KerasLayer) (None, 1280) 2257984
_________________________________________________________________
dense (Dense) (None, 2) 2562
=================================================================
Total params: 2,260,546
Trainable params: 2,562
Non-trainable params: 2,257,984
Notre modèle est exactement celui défini par mobilenet_v2 auquel on ajoute une couche dense avec 2 neurones.
Apprentissage
On commence sans fine tuning. En cas de fine tuning, on utilise SGD comme optimizer, sinon c’est Adam.
if do_fine_tuning:
model.compile(
optimizer=tf.keras.optimizers.SGD(lr=0.002, momentum=0.9),
loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
else:
model.compile(
optimizer='adam',
loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
EPOCHS = 5
hist = model.fit(train_batches,
epochs=EPOCHS,
validation_data=validation_batches)
Epoch 1/5
146/146 [==============================] - 8s 53ms/step - loss: 0.0859 - accuracy: 0.9714 - val_loss: 0.0421 - val_accuracy: 0.9854
Epoch 2/5
146/146 [==============================] - 7s 50ms/step - loss: 0.0354 - accuracy: 0.9877 - val_loss: 0.0263 - val_accuracy: 0.9944
Epoch 3/5
146/146 [==============================] - 7s 51ms/step - loss: 0.0257 - accuracy: 0.9923 - val_loss: 0.0196 - val_accuracy: 0.9944
Epoch 4/5
146/146 [==============================] - 7s 50ms/step - loss: 0.0202 - accuracy: 0.9944 - val_loss: 0.0145 - val_accuracy: 0.9970
Epoch 5/5
146/146 [==============================] - 7s 51ms/step - loss: 0.0159 - accuracy: 0.9959 - val_loss: 0.0123 - val_accuracy: 0.9974
Inutile de se faire, pour l’instant, du fine tuning avec 99,6% d’accuracy.
Sauvegarde du modèle
CATS_VS_DOGS_SAVED_MODEL = "exp_saved_model"
tf.saved_model.save(model, CATS_VS_DOGS_SAVED_MODEL)
!saved_model_cli show --dir $CATS_VS_DOGS_SAVED_MODEL --tag_set serve --signature_def serving_default
The given SavedModel SignatureDef contains the following input(s):
inputs['keras_layer_1_input'] tensor_info:
dtype: DT_FLOAT
shape: (-1, 224, 224, 3)
name: serving_default_keras_layer_1_input:0
The given SavedModel SignatureDef contains the following output(s):
outputs['dense'] tensor_info:
dtype: DT_FLOAT
shape: (-1, 2)
name: StatefulPartitionedCall:0
Method name is: tensorflow/serving/predict
loaded = tf.saved_model.load(CATS_VS_DOGS_SAVED_MODEL)
print(list(loaded.signatures.keys()))
infer = loaded.signatures["serving_default"]
print(infer.structured_input_signature)
print(infer.structured_outputs)
['serving_default']
((), {'keras_layer_1_input': TensorSpec(shape=(None, 224, 224, 3), dtype=tf.float32, name='keras_layer_1_input')})
{'dense': TensorSpec(shape=(None, 2), dtype=tf.float32, name='dense')}
converter = tf.lite.TFLiteConverter.from_saved_model(CATS_VS_DOGS_SAVED_MODEL)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
Création d’un jeu de données représentatif
def representative_data_gen():
for input_value, _ in test_batches.take(100):
yield [input_value]
converter.representative_dataset = representative_data_gen
Quantization
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
Conversion du modèle
tflite_model = converter.convert()
tflite_model_file = 'converted_model.tflite'
with open(tflite_model_file, "wb") as f:
f.write(tflite_model)
# Load TFLite model and allocate tensors.
interpreter = tf.lite.Interpreter(model_path=tflite_model_file)
interpreter.allocate_tensors()
input_index = interpreter.get_input_details()[0]["index"]
output_index = interpreter.get_output_details()[0]["index"]
from tqdm import tqdm
# Gather results for the randomly sampled test images
predictions = []
test_labels, test_imgs = [], []
for img, label in tqdm(test_batches.take(10)):
interpreter.set_tensor(input_index, img)
interpreter.invoke()
predictions.append(interpreter.get_tensor(output_index))
test_labels.append(label.numpy()[0])
test_imgs.append(img)
#@title Utility functions for plotting
# Utilities for plotting
class_names = ['cat', 'dog']
def plot_image(i, predictions_array, true_label, img):
predictions_array, true_label, img = predictions_array[i], true_label[i], img[i]
plt.grid(False)
plt.xticks([])
plt.yticks([])
img = np.squeeze(img)
plt.imshow(img, cmap=plt.cm.binary)
predicted_label = np.argmax(predictions_array)
if predicted_label == true_label:
color = 'green'
else:
color = 'red'
plt.xlabel("{} {:2.0f}% ({})".format(class_names[predicted_label],
100*np.max(predictions_array),
class_names[true_label]),
color=color)
#@title Visualize the outputs { run: "auto" }
index = 0 #@param {type:"slider", min:0, max:9, step:1}
plt.figure(figsize=(6,3))
plt.subplot(1,2,1)
plot_image(index, predictions, test_labels, test_imgs)
plt.show()


