https://d226lax1qjow5r.cloudfront.net/blog/blogposts/dog-breed-detector-using-machine-learning-dr/Building-a-Dog-Breed-Detector-Using-Machine-Learning.png

Construire un détecteur de race de chien à l'aide de l'apprentissage automatique

Publié le May 10, 2021

Temps de lecture : 10 minutes

Chez Nexmo, nous utilisons Facebook Workplace comme l'un de nos nombreux canaux de communication. Si vous ne l'avez pas utilisé ou si vous n'en avez pas entendu parler, c'est comme Facebook, mais pour les entreprises. Nous avons tous un compte chez Nexmo, et nous pouvons voir et rejoindre différents groupes dans l'ensemble de l'organisation.

Il y a quelques mois, l'un de nos collègues a créé un groupe pour montrer nos animaux de compagnie. C'était une excellente idée et beaucoup de membres de l'équipe postent des photos de leurs animaux. Je consulte le groupe presque tous les jours et c'est un bon moyen d'apprécier les belles choses de la vie (les chiots !).

wp-groupwp-group

Après avoir regardé les photos de chiens, de chats et même de lapins, certaines personnes ont demandé : "Quelle est la race de ce chien ? J'ai alors eu l'idée de créer un algorithme d'apprentissage automatique pour déterminer la race du chien figurant sur la photo.

Dans ce billet, nous allons apprendre à construire un détecteur de race de chien en utilisant Kerasqui est un framework très populaire pour construire des modèles d'apprentissage automatique.

Conditions préalables

Cet article suppose que vous connaissiez un peu de Python et que vous ayez une compréhension de base de l'apprentissage automatique. Vous devriez savoir ce qu'est Keras et comment entraîner un modèle d'apprentissage automatique de base.

Par où commencer ?

Pour résoudre de nombreux problèmes d'apprentissage automatique, il faut des données, et beaucoup de données. Plus précisément, nous avons besoin de photos d'un grand nombre de chiens et de leurs races. Pour ce projet, nous allons utiliser l'ensemble de données du Défi d'identification des races de chiens sur Kaggle. Cet ensemble de données contient plus de 10 000 images de chiens, classées par race.

Construction du modèle

Commençons par construire le modèle. J'utiliserai Google Colab pour construire mon Jupyter Notebooken Python. Un carnet Jupyter est une application web open source qui vous permet d'écrire du code, ainsi que du texte et des images. C'est un excellent moyen de débuter. Google Colab est un service gratuit qui hébergera vos Notebooks Jupyter.

Note : Si vous voulez voir comment le modèle est construit, vous pouvez consulter mon carnet de notes ici.

Avant de construire le modèle, nous devons obtenir les données, qui sont hébergées sur Kaggle. Pour charger les données, nous devons utiliser un package pour télécharger les données sur notre notebook, en utilisant l API Kaggle. Cela nous permettra de télécharger l'ensemble de données pour le concours de races de chiens. Avant de pouvoir télécharger le jeu de données, nous devons créer un Account sur Kaggle, et obtenir votre clé API Kaggle et votre secret.

kaggle-create-api-tokenkaggle-create-api-token

Cliquez sur "Create New API Token" (Créer un nouveau jeton API) et enregistrez le fichier sur votre ordinateur. Pour télécharger les données, nous exécuterons la commande suivante cellule.

# Run this cell and select the kaggle.json file downloaded
# from the Kaggle account settings page.
from google.colab import files
files.upload()
# Let's make sure the kaggle.json file is present.
!ls -lha kaggle.json
# Next, install the Kaggle API client.
!pip install -q kaggle

# The Kaggle API client expects this file to be in ~/.kaggle,
# so move it there.
!mkdir -p ~/.kaggle
!cp kaggle.json ~/.kaggle/

# This permissions change avoids a warning on Kaggle tool startup.
!chmod 600 ~/.kaggle/kaggle.json

#download the dataset for the dog-breed identification challenge https://www.kaggle.com/c/dog-breed-identification
!kaggle competitions download -c dog-breed-identification

#unzip the downloaded files
!unzip labels.csv.zip
!unzip test.zip
!unzip train.zip

Si vous ne comprenez pas chaque ligne de ce code ou de toute autre section de code, ne vous inquiétez pas. Vous pourrez copier et coller la source pour tout exécuter vous-même, sans avoir à vous soucier des détails.

Lorsque vous exécutez cette cellule, elle vous invite à sélectionner un fichier. Trouvez le fichier JSON qui a été téléchargé depuis Kaggle et téléchargez-le dans la cellule. Vous pourrez alors exécuter l'API Kaggle et télécharger l'ensemble de données dans le bloc-notes. Une fois les fichiers téléchargés, nous les décompresserons à l'aide de la commande !unzip. L'élément ! avant la commande vous permet d'exécuter une action de ligne de commande dans Google Colab. La commande !unzip décompresse simplement chaque fichier.

Les fichiers téléchargés depuis Kaggle contiennent les éléments suivants :

  • Images de formation, situé le dossier \train dossier

  • Images de test, situées dans le \test dossier

  • Un fichier CSV appelé labels.csvcontenant le nom de la race et le nom du fichier, qui pointe vers l'image dans le dossier d'entraînement.

Maintenant, nous pouvons charger nos données dans un Dataframe, en utilisant Pandas. A DataFrameest une structure de données simple qui contient des lignes et des colonnes, un peu comme un CSV. Pandas est un paquetage Python qui fournit des structures de données et des outils d'analyse de données performants et faciles à utiliser. Il est utilisé dans de nombreuses Applications d'apprentissage automatique. Si vous utilisez des applications d'apprentissage automatique, l'un des premiers packages que vous utiliserez sera Pandas. Si vous souhaitez en savoir plus sur Pandas, consultez leur propre tutoriel, Tutoriel de 10 minutes sur Pandas.

En utilisant Pandas, nous pouvons importer le csv de l'ensemble de données Kaggle dans un Dataframe Pandas.

Source.

#import the necessary packages
import pandas as pd
import numpy as np

#constants
num_classes = 12 # the number of breeds we want to classify
seed = 42 # makes the random numbers in numpy predictable
im_size = 299 # This size of the images
batch_size = 32

#read the csv into a dataframe, group the breeds by name and append the path the to image in the `filename` column
df = pd.read_csv('labels.csv')
selected_breed_list = list(df.groupby('breed').count().sort_values(by='id', ascending=False).head(num_classes).index)
df = df[df['breed'].isin(selected_breed_list)]
df['filename'] = df.apply(lambda x: ('train/' + x['id'] + '.jpg'), axis=1)

breeds = pd.Series(df['breed'])
print("total number of breeds to classify",len(breeds.unique()))

df.head()

Cette commande prend le fichier csv, le charge dans un DataFrame pd.read_csv('labels.csv')puis trie le DataFrame par race et par ordre alphabétique. Ensuite, nous imprimons les 10 premières lignes en utilisant df.head()

df_headdf_head

Ensuite, nous devons écrire une fonction qui redimensionnera toutes les images à la taille dont nous avons besoin, c'est-à-dire 299x299px. Il sera clair pourquoi nous devons redimensionner l'image plus tard.

Source

from keras.preprocessing import image

def read_img(img_id, train_or_test, size):
    """Read and resize image.
    # Arguments
    img_id: string
    train_or_test: string 'train' or 'test'.
    size: resize the original image.
    # Returns
    Image as numpy array.
    """
    path = train_or_test + "/" + img_id + ".jpg"
    img = image.load_img(path, target_size=size)
return image.img_to_array(img)

La fonction read_img() chargera l'image à la taille voulue (299x299px) et la convertira en un tableau numpy multidimensionnel. Numpy est un autre paquetage Python très fréquemment utilisé dans l'apprentissage automatique. Il permet de travailler plus facilement avec ces types de tableaux en Python.

Ensuite, nous devons convertir les noms des races (basenji, scottish_deerhound) en vecteurs (tableau de nombres à une dimension), car notre modèle d'apprentissage automatique ne peut traiter que des nombres. Pour ce faire, nous utiliserons l'outil Scikit Learn LabelEncoder. Un LabelEncoder prend tous les noms de race et convertit le nom de la race en un nombre entier. Chaque nombre sera différent pour chaque race (0 pour basenji1 pour scottish_deerhound etc.) Scikit-Learn est un autre logiciel libre qui facilite l'apprentissage automatique.

Ensuite, nous diviserons l'ensemble de données en deux, l'un pour la formation et l'autre pour le test. Lorsque nous formons notre modèle, nous utilisons les données de l'ensemble d'entraînement pour former le modèle, puis, lorsque nous devons vérifier son efficacité, nous le testons sur l'ensemble de test.

Source

from sklearn.preprocessing import LabelEncoder
label_enc = LabelEncoder()
np.random.seed(seed=seed)
rnd = np.random.random(len(df))
train_idx = rnd < 0.9 valid_idx = rnd >= 0.9
y_train = label_enc.fit_transform(df["breed"].values)
ytr = y_train[train_idx]
yv = y_train[valid_idx]

Enfin, nous prendrons toutes les images de l'ensemble d'apprentissage et les redimensionnerons à l'aide de la fonction read_img que nous avons créée précédemment. Nous devons ensuite traiter chaque image pour la mettre dans le format correct attendu par notre modèle.

Source

from tqdm import tqdm
from keras.applications import xception

x_train = np.zeros((train_idx.sum(), im_size, im_size, 3), dtype='float32')
x_valid = np.zeros((valid_idx.sum(), im_size, im_size, 3), dtype='float32')
train_i = 0
valid_i = 0
for i, img_id in tqdm(enumerate(df['id'])):
    img = read_img(img_id, 'train', (im_size, im_size))
    x = xception.preprocess_input(np.expand_dims(img.copy(), axis=0))
    if train_idx[i]:
        x_train[train_i] = x
        train_i += 1
    elif valid_idx[i]:
        x_valid[valid_i] = x
        valid_i += 1
print('Train Images shape: {} size: {:,}'.format(x_train.shape, x_train.size))

[00:06, 201.73it/s]Train Images shape: (1218, 299, 299, 3) size: 326,671,254

Dans cette fonction, nous parcourons en boucle chaque élément de notre DataFrame (for i, img_id in tqdm(enumerate(df['id'])): et appelons la fonction read_image qui prend l'identifiant img_idqui est l'ID de l'image, en corrélation avec le nom de fichier de l'image dans le dossier \train et la redimensionne à 299x299px. Nous appelons ensuite la fonction xception.preprocess_input fonction.

Avant de voir ce que fait cette fonction, nous devons comprendre ce qu'est un xception est.

La formation de modèles à partir de zéro nécessite beaucoup plus d'images que celles dont nous disposons (10 000), ainsi que beaucoup de temps de calcul et de ressources. Afin d'accélérer ce processus, nous pouvons utiliser une technique appelée Apprentissage par transfert. Cela signifie que nous pouvons utiliser un modèle qui a été pré-entraîné sur un autre ensemble de données, tel que le jeu de données Imagenet. Xception est l'un de ces modèles pré-entraînés. Nous pouvons compter sur le modèle pré-entraîné pour extraire les caractéristiques de l'image. Nous nous contenterons ensuite d'entraîner le modèle pour notre cas d'utilisation spécifique : la détermination de la race.

J'ai expérimenté plusieurs modèles, et j'ai trouvé que Xception donne les meilleurs résultats pour la détection des races, avec le jeu de données d'images que nous utilisons.

Pour d'autres ensembles de données, il peut y avoir d'autres modèles plus adaptés à vos besoins pour de meilleurs résultats. Veillez donc à effectuer quelques tests avant de décider du modèle à utiliser. Pour en savoir plus sur ce modèle et d'autres modèles pré-entraînés, consultez le site suivant (https://www.pyimagesearch.com/2017/03/20/imagenet-vggnet-resnet-inception-xception-keras/). Cet article explique également ce qu'est ImageNet et comment il est lié à ces modèles préformés.

OK, revenons maintenant à ce que fait la fonction xception.preprocess_input() fait. Elle prend l'image, qui est maintenant un tableau numpy, et la convertit dans un format attendu par le modèle Xception, dans lequel toutes les valeurs du tableau sont comprises entre -1 et 1, ce qui est connu sous le nom de normalisation.

Nous pouvons maintenant construire notre modèle.

Comme nous utilisons Xception comme modèle de base, notre modèle personnalisé est très simple. Pour notre propre modèle, nous chargerons la sortie de Xception, c'est-à-dire toutes les couches qui ont déjà été entraînées sur les images d'Imagenet, puis nous construirons un modèle séquentiel.

Extrait du blog Keras : "Le modèle séquentiel est un empilement linéaire de couches". Démarrer avec le modèle séquentiel de Keras

Cela signifie que nous pouvons superposer d'autres couches au modèle Xception. Cela permettra à notre modèle de s'entraîner sur nos images de chiens.

Voici notre modèle. Source d'information

from keras.layers import GlobalAveragePooling2D, Dense, BatchNormalization, Dropout
from keras.optimizers import Adam, SGD, RMSprop
from keras.models import Model, Input

# create the base pre-trained model
base_model = xception.Xception(weights='imagenet', include_top=False)
# first: train only the top layers (which were randomly initialized)
# i.e. freeze all convolutional Xception layers
for layer in base_model.layers:
    layer.trainable = False

# add a global spatial average pooling layer
x = base_model.output
x = BatchNormalization()(x)
x = GlobalAveragePooling2D()(x)
# let's add a fully-connected layer
x = Dropout(0.5)(x)
x = Dense(1024, activation='relu')(x)
x = Dropout(0.5)(x)
# and a logistic layer and set it to the number of breeds we want to classify,
predictions = Dense(num_classes, activation='softmax')(x)

# this is the model we will train
model = Model(inputs=base_model.input, outputs=predictions)

Nous prenons d'abord notre base_modelqui est Xception, puis, Freeze les couches. Cela signifie que nous ne ferons pas d'apprentissage sur ces couches, puisqu'elles ont déjà été entraînées. Ensuite, nous prenons la sortie de la couche base_model et ajoutons les couches suivantes :

  • BatchNormalization - applique une transformation qui maintient l'activation moyenne proche de 0 et l'écart-type de l'activation proche de 1.

  • GlobalAveragePooling2D réduit le nombre de paramètres à apprendre.

  • Désactivation - désactive les entrées de manière aléatoire afin d'éviter l'ajustement excessif.

  • Couche dense couche dense - qui relie tous les neurones du réseau.

  • suivi d'un autre Dropout couche.

  • Enfin, nous créons un autre Dense et nous la définissons en fonction du nombre de races pour lesquelles nous nous entraînons.

La sélection de ces couches est basée sur des essais et des erreurs. Il n'existe pas de méthode connue pour déterminer une bonne structure de réseau lors de la construction de votre modèle.. Lorsque vous construisez vos propres modèles, Stack Overflow est votre meilleur ami.

Formation du modèle

Nous allons maintenant commencer à construire le modèle et à l'entraîner. La façon dont j'entraîne le modèle est très basique. Vous verrez ce type de code lorsque vous examinerez d'autres modèles dans Keras. Source

import datetime
from keras.callbacks import EarlyStopping, ModelCheckpoint

epochs = 1
learning_rate = 0.001

# checkpoints
early_stopping = EarlyStopping(monitor='val_acc', patience=5)
STAMP = "{}_dog_breed_model".format(datetime.date.today().strftime("%Y-%m-%d"))

bst_model_path = "{}.h5".format(STAMP)
model_checkpoint = ModelCheckpoint(bst_model_path,
save_best_only=True,
save_weights_only=False,
verbose=1)
# compile the model
optimizer = RMSprop(lr=learning_rate, rho=0.9)
model.compile(optimizer=optimizer,
loss='sparse_categorical_crossentropy',
metrics=["accuracy"])

hist = model.fit_generator(train_generator,
steps_per_epoch=train_idx.sum() // batch_size,
epochs=epochs, callbacks=[early_stopping, model_checkpoint],
validation_data=valid_generator,
validation_steps=valid_idx.sum() // batch_size)

model.save(bst_model_path)

Nous ajoutons d'abord quelques fonctions de rappelqui sont des fonctions que nous exécutons après chaque cycle de formation, également connu sous le nom de epoch. Nous avons 2 fonctions de rappel :

  • early_stopping avec le patience avec le paramètre 5 : ceci arrêtera l'apprentissage si le modèle ne s'améliore pas après 5 époques.

  • model_checkpoint: Cette fonction permet d'enregistrer le modèle dans un fichier en vue d'une utilisation ultérieure.

Ensuite, nous réglons l'optimiseur sur RMSprop. Un optimiseur est la façon dont le modèle "apprend". Pour chaque époque, le modèle calcule la fonction de perte, qui indique la qualité du modèle par rapport à l'ensemble de test. L'objectif est de rendre cette perte aussi faible que possible, ce qui est appelé Descente en gradient. Keras prend en charge de nombreux optimiseurs, et lors de mes expériences, RMSProp, qui effectue une descente de gradient, a semblé donner les meilleurs résultats.

Ensuite, nous construirons le modèle à l'aide de la fonction model.compile qui accepte l'optimiseur, la fonction de perte que nous voulons calculer (sparse_categorical_crossentropy), et le réglage du paramètre metrics à accuracyqui nous indiquera la précision du modèle après chaque période.

Ensuite, nous effectuerons notre formation en appelant model.fit_generator(). Les paramètres de cette fonction sont : ImageDataGenerator's pour notre ensemble d'apprentissage et notre ensemble de test, le nombre d'étapes que nous allons exécuter, le nombre d'époques, ce sur quoi valider, et le nombre d'étapes pour valider. Pour l'instant, nous allons entraîner ce modèle pendant 10 époques, juste pour voir comment nous nous en sommes sortis.

Epoch 1/10
38/38 [==============================] - 40s 1s/step - loss: 0.5477 - acc: 0.8281 - val_loss: 0.0555 - val_acc: 0.9766

*skipping output for readability*

Epoch 10/10
38/38 [==============================] - 33s 857ms/step - loss: 0.2426 - acc: 0.9358 - val_loss: 0.0457 - val_acc: 0.9905

Nous disposons donc d'un modèle qui est précis à 99 % lorsqu'il s'agit de prédire 12 races !

Nous pouvons maintenant tester notre modèle sur des images de chiens et voir si nous sommes capables d'obtenir la bonne race à partir de cette image. Nous allons écrire une fonction qui prend une image sur internet, la formate selon les attentes du modèle (image de 299x299px) et fait la prédiction en utilisant model.predict(). Cette fonction prend une image, sous la forme d'un tableau numpy, et renvoie la sortie sous la forme d'une liste de probabilités pour chaque race. Nous utilisons np.argmax() pour trouver l'indice de la probabilité la plus élevée dans le résultat de la fonction model.predict(). Pour retourner le nom de la race, nous utilisons la fonction labels.csv que nous avons chargé à partir de l'ensemble de données Kaggle qui contient les 12 noms de races. Nous trions ensuite la liste par ordre alphabétique et renvoyons le nom de la race.

from keras.models import load_model
from keras.preprocessing import image
import matplotlib.pyplot as plt
import numpy as np
import os

def predict_from_image(img_path):
    img = image.load_img(img_path, target_size=(299, 299))
    img_tensor = image.img_to_array(img) # (height, width, channels)
    img_tensor = np.expand_dims(img_tensor, axis=0) # (1, height, width, channels), add a dimension because the model expects this shape: (batch_size, height, width, channels)
    img_tensor /= 255.

    pred = model.predict(img_tensor)
    predicted_class = sorted_breeds_list[np.argmax(pred)]

return predicted_class

Testons maintenant cette fonction, pour être sûrs qu'elle fonctionne. Nous allons télécharger une photo d'un chien de chasse écossaisen utilisant wgetqui est un utilitaire de ligne de commande permettant de télécharger des fichiers, et nous verrons comment le modèle se comporte.

predictpredict

C'est bien ! Le modèle prédit que le chien sur la photo est un Deerhound écossais, ce qui est le cas !

Conclusion

Dans cet article, nous avons appris à construire notre propre modèle d'apprentissage automatique à l'aide de Keras, à entraîner notre modèle à l'aide de l'apprentissage par transfert et à faire des prédictions à l'aide de notre modèle.

Dans un prochain article, nous verrons comment déployer ce modèle sur un serveur en tant qu'API simple pour que d'autres puissent l'utiliser. Ensuite, nous construirons un robot Workplace qui permettra à n'importe quel membre de notre groupe Workplace de demander quelle race de chien se trouve sur une photo tirée d'un post Workplace.

Partager:

https://a.storyblok.com/f/270183/150x150/a3d03a85fd/placeholder.svg
Tony HungAnciens de Vonage

Développeur IOS devenu passionné de science des données et d'apprentissage automatique. Je veux que les gens comprennent ce qu'est l'apprentissage automatique et comment nous pouvons l'utiliser dans nos Applications.