Étiquettes : , ,

Dans ce tutoriel, nous allons entrainer un modèle MobileNetV2 TensorFlow avec Keras pour qu’il s’applique à notre problématique. Nous allons ensuite pouvoir l’utiliser ne temps réel pour classifier de nouvelles images.

Pour ce tutoriel, nous supposons que vous avez suivit les tutoriel précédent: utilisation d’un modèle TensorFlow et préparation d’une base de données pour l’entrainement.

N.B.: je n’ai pas trouvé la bonne méthode pour entrainer le model ssd mobilenetV2, tel quel, avec tensorflow. Je suis donc passé sous Yolo. Si vous avez la bonne méthode n’hésitez pas à laisser un commentaire.

Récupérer une base d’image

Télechargez une des nombreuses bases d’image comme cats-and-dogs ou créez votre propre banque d’image

Dezippé le dossier dans sous Tensorflow>data

Entrainement du modèle

Pour entrainer le modèle, vous pouvez utiliser le script suivant qui va:

  • charger et augmenter la base de données
  • créer un modèle (model) à partir du modèle MobileNetV2(base_model)
  • entrainer les nouveaux gains du model
  • entrainer finement les gains du base_model
import matplotlib.pyplot as plt
import numpy as np
import os
import tensorflow as tf

#_URL = 'https://storage.googleapis.com/mledu-datasets/cats_and_dogs_filtered.zip'
#path_to_zip = tf.keras.utils.get_file('cats_and_dogs.zip', origin=_URL, extract=True)
#PATH = os.path.join(os.path.dirname(path_to_zip), 'cats_and_dogs_filtered')

PATH="./data/cats_and_dogs_filtered"
train_dir = os.path.join(PATH, 'train')
validation_dir = os.path.join(PATH, 'validation')

BATCH_SIZE = 32
IMG_SIZE = (160, 160)

#create train and validation sets
train_dataset = tf.keras.utils.image_dataset_from_directory(train_dir,
                                                            shuffle=True,
                                                            batch_size=BATCH_SIZE,
                                                            image_size=IMG_SIZE)

validation_dataset = tf.keras.utils.image_dataset_from_directory(validation_dir,
                                                                 shuffle=True,
                                                                 batch_size=BATCH_SIZE,
                                                                 image_size=IMG_SIZE)

class_names = train_dataset.class_names

plt.figure(figsize=(10, 10))
for images, labels in train_dataset.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")

val_batches = tf.data.experimental.cardinality(validation_dataset)
test_dataset = validation_dataset.take(val_batches // 5)
validation_dataset = validation_dataset.skip(val_batches // 5)

print('Number of validation batches: %d' % tf.data.experimental.cardinality(validation_dataset))
print('Number of test batches: %d' % tf.data.experimental.cardinality(test_dataset))


#configure performance
AUTOTUNE = tf.data.AUTOTUNE
train_dataset = train_dataset.prefetch(buffer_size=AUTOTUNE)
validation_dataset = validation_dataset.prefetch(buffer_size=AUTOTUNE)
test_dataset = test_dataset.prefetch(buffer_size=AUTOTUNE)

#augmented data (usefull for small data sets)
data_augmentation = tf.keras.Sequential([
  tf.keras.layers.RandomFlip('horizontal'),
  tf.keras.layers.RandomRotation(0.2),
])

for image, _ in train_dataset.take(1):
  plt.figure(figsize=(10, 10))
  first_image = image[0]
  for i in range(9):
    ax = plt.subplot(3, 3, i + 1)
    augmented_image = data_augmentation(tf.expand_dims(first_image, 0))
    plt.imshow(augmented_image[0] / 255)
    plt.axis('off')


preprocess_input = tf.keras.applications.mobilenet_v2.preprocess_input
rescale = tf.keras.layers.Rescaling(1./127.5, offset=-1)


# Create the base model from the pre-trained model MobileNet V2
IMG_SHAPE = IMG_SIZE + (3,)
base_model = tf.keras.applications.MobileNetV2(input_shape=IMG_SHAPE,
                                               include_top=False,
                                               weights='imagenet')

#or load your own
#base_model= tf.saved_model.load("./pretrained_models/ssd_mobilenet_v2_320x320_coco17_tpu-8/saved_model")

                                               
image_batch, label_batch = next(iter(train_dataset))
feature_batch = base_model(image_batch)
print(feature_batch.shape)

base_model.trainable = False
base_model.summary()

#classification header
global_average_layer = tf.keras.layers.GlobalAveragePooling2D()
feature_batch_average = global_average_layer(feature_batch)
print(feature_batch_average.shape)

prediction_layer = tf.keras.layers.Dense(1)
prediction_batch = prediction_layer(feature_batch_average)
print(prediction_batch.shape)


#create new neural network based on MobileNet
inputs = tf.keras.Input(shape=(160, 160, 3))
x = data_augmentation(inputs)
x = preprocess_input(x)
x = base_model(x, training=False)
x = global_average_layer(x)
x = tf.keras.layers.Dropout(0.2)(x)
outputs = prediction_layer(x)
model = tf.keras.Model(inputs, outputs)

base_learning_rate = 0.0001
model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=base_learning_rate),
              loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
              metrics=['accuracy'])

initial_epochs = 10

loss0, accuracy0 = model.evaluate(validation_dataset)
print("initial loss: {:.2f}".format(loss0))
print("initial accuracy: {:.2f}".format(accuracy0))

history = model.fit(train_dataset,
                    epochs=initial_epochs,
                    validation_data=validation_dataset)


#plot learning curves
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']

loss = history.history['loss']
val_loss = history.history['val_loss']

plt.figure(figsize=(8, 8))
plt.subplot(2, 1, 1)
plt.plot(acc, label='Training Accuracy')
plt.plot(val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.ylabel('Accuracy')
plt.ylim([min(plt.ylim()),1])
plt.title('Training and Validation Accuracy')

plt.subplot(2, 1, 2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.ylabel('Cross Entropy')
plt.ylim([0,1.0])
plt.title('Training and Validation Loss')
plt.xlabel('epoch')
plt.show()


#fine tuning
base_model.trainable = True
# Let's take a look to see how many layers are in the base model
print("Number of layers in the base model: ", len(base_model.layers))

# Fine-tune from this layer onwards
fine_tune_at = 100

# Freeze all the layers before the `fine_tune_at` layer
for layer in base_model.layers[:fine_tune_at]:
  layer.trainable = False

model.compile(loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
              optimizer = tf.keras.optimizers.RMSprop(learning_rate=base_learning_rate/10),
              metrics=['accuracy'])

model.summary()
fine_tune_epochs = 10
total_epochs =  initial_epochs + fine_tune_epochs

history_fine = model.fit(train_dataset,
                         epochs=total_epochs,
                         initial_epoch=history.epoch[-1],
                         validation_data=validation_dataset)

#plot fine learning curves
acc += history_fine.history['accuracy']
val_acc += history_fine.history['val_accuracy']

loss += history_fine.history['loss']
val_loss += history_fine.history['val_loss']

plt.figure(figsize=(8, 8))
plt.subplot(2, 1, 1)
plt.plot(acc, label='Training Accuracy')
plt.plot(val_acc, label='Validation Accuracy')
plt.ylim([0.8, 1])
plt.plot([initial_epochs-1,initial_epochs-1],
          plt.ylim(), label='Start Fine Tuning')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')

plt.subplot(2, 1, 2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.ylim([0, 1.0])
plt.plot([initial_epochs-1,initial_epochs-1],
         plt.ylim(), label='Start Fine Tuning')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.xlabel('epoch')
plt.show()


#evaluate
loss, accuracy = model.evaluate(test_dataset)
print('Test accuracy :', accuracy)

model.save('saved_models/my_model')

keras-learning-cats-dogs Entrainer un modèle TensorFlow2 avec Keras

Utilisation du modèle entrainé

Vous pouvez utiliser le modèle entrainé pour classifier de nouvelles images contenant un seul type d’objet par image. Pour cela, il vous suffit de charger le modèle précédemment sauvegardé (saved_models/my_model)

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#  ObjectRecognitionTFVideo.py
#  Description:
#		Use ModelNetV2-SSD model to detect objects on video
#
#  www.aranacorp.com

# import packages
import sys
from imutils.video import VideoStream
from imutils.video import FPS
import numpy as np
import argparse
import imutils
import time
import cv2
import tensorflow as tf
from PIL import Image

# load model from path
#model= tf.saved_model.load("./pretrained_models/ssd_mobilenet_v2_320x320_coco17_tpu-8/saved_model")
model= tf.saved_model.load("./saved_models/my_model")
#model.summary()

print("model loaded")

#load class names
#category_index = label_map_util.create_category_index_from_labelmap(PATH_TO_LABELS,use_display_name=True)
def read_label_map(label_map_path):

    item_id = None
    item_name = None
    items = {}
    
    with open(label_map_path, "r") as file:
        for line in file:
            line.replace(" ", "")
            if line == "item{":
                pass
            elif line == "}":
                pass
            elif "id" in line:
                item_id = int(line.split(":", 1)[1].strip())
            elif "display_name" in line: #elif "name" in line:
                item_name = line.split(":", 1)[1].replace("'", "").strip()

            if item_id is not None and item_name is not None:
                #items[item_name] = item_id
                items[item_id] = item_name
                item_id = None
                item_name = None

    return items

#class_names=read_label_map("./pretrained_models/ssd_mobilenet_v2_320x320_coco17_tpu-8/mscoco_label_map.pbtxt")
class_names = read_label_map("./saved_models/label_map.pbtxt")
class_names = list(class_names.values()) #convert to list
class_colors = np.random.uniform(0, 255, size=(len(class_names), 3))
print(class_names)

if __name__ == '__main__':

	# Open image
	#img= cv2.imread('./data/cats_and_dogs_filtered/train/cats/cat.1.jpg') #from image file
	img= cv2.imread('./data/cats_and_dogs_filtered/train/dogs/dog.1.jpg') #from image file
	img = cv2.resize(img,(160,160))
	img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

	#input_tensor = np.expand_dims(img, 0)
	input_tensor = tf.convert_to_tensor(np.expand_dims(img, 0), dtype=tf.float32)

	# predict from model
	resp = model(input_tensor)
	print("resp: ",resp)
	score= tf.nn.sigmoid(resp).numpy()[0][0]*100
	cls = int(score>0.5)
	print("classId: ",int(cls))
	print("score: ",score)
	print("score: ",tf.nn.sigmoid(tf.nn.sigmoid(resp)))
	
			
	# write classname for bounding box
	cls=int(cls) #convert tensor to index
	label = "{}".format(class_names[cls])
	img = cv2.resize(img,(640,640))	
	cv2.putText(img, label, (5, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, class_colors[cls], 2)
		
	# Show frame
	cv2.imshow("Frame", img)
	cv2.waitKey(0)



keras-model-training-result Entrainer un modèle TensorFlow2 avec Keras

Applications

  • reconnaissance de différentes races d’animaux
  • reconnaissance de différents type d’objets comme des cartes électroniques

Autres modèles de classification à considérer

  • vgg16
  • vgg19
  • resnet50
  • resnet101
  • resnet152
  • densenet121
  • densenet169
  • densenet201
  • inceptionresnetv2
  • inceptionv3
  • mobilenet
  • mobilenetv2
  • nasnetlarge
  • nasnetmobile
  • xception

Sources