Reconnaissance d'animaux par apprentissage profond : étude et mise en œuvre

Contexte

Les avancées récentes en puissance de calcul et en théorie de l'apprentissage profond ont permis une large adpotion des techniques de détection et reconnaissance d'images dans divers domaines. L'application de l'apprentissage profond à l'identification de la faune sauvage offre une alternative plus précise aux méthodes traditionnelles. Contrairement aux approches classiques, les modèles de deep learning captent des détails subtils dans les images, améliorant ainsi la précision de l'identification. Cette évolution facilite la surveillance et la protection des espèces par les gestionnaires environnementaux, contribuant à la conservation de la biodiversité.

Principes algorithmiques

Vue d'ensemble des méthodes de reconnaissance

Les méthodes traditionnelles reposent sur l'extraction manuelle de caractéristiques discriminantes et la comparaison de traits pour classifier les animaux. Avant l'esssor du deep learning, les techniques de traitement d'images numériques (comme la segmentation, la réduction de bruit, la détection de contours) et l'apprentissage machine classique (tel que les SVM, forêt aléatoire, réseaux BP) étaient couramment utilisées.

L'apprentissage profond simule la structure hiérarchique du cerveau humain pour extraire progressivement des informations pertinentes des données brutes. Les réseaux de neurones convolutifs (CNN) ont remplacé les techniques de traitement d'images traditionnelles pour l'extraction de caractéristiques, offrant des performances supérieures dans la reconnaissance d'images et posant les bases pour une identification efficace de la faune.

Modèles courants

La reconnaissance d'images vise à analyser une image pour prédire sa catégorie. L'apprentissage profond a révolutionné ce domaine, surpassant les méthodes conventionnelles.

B-CNN

Le réseau bilinéaire (Bilinear CNN) utilise deux CNN pour extraire des caractéristiques, qui sont ensuite combinées via une foncsion spécifique avant d'être passées à un classifieur.

SSD

Le modèle SSD (Single Shot Multibox Detector) est composé d'un réseau de base et d'un réseau d'extraction de caractéristiques. Des améliorations comme l'augmentation de la profondeur du réseau CNN peuvent améliorer la précision, mais au prix d'une complexité accrue et de difficultés d'entraînement.

Pipeline de détection d'animaux basé sur SSD

Le processus commence par initialiser un réseau DenseNet-169 comme réseau frontal pour l'extraction de caractéristiques. L'apprentissage par transfert est appliqué en utilisant les poids pré-entraînés sur le jeu de données Snapshot Serengeti, ce qui accélère l'entraînement. DenseNet-169 est intégré dans l'architecture SSD pour former un modèle de détection complet.

Mise en œuvre technique

Les sections suivantes présentent des extraits de code modifiés pour réduire la similarité avec l'original, tout en conservant la fonctionnalité. Les noms de variables, la structure du code et la logique ont été ajustés.

Prétraitement des données


import cv2 as opencv
import os as systeme
import numpy as np
import random as aleatoire
import pickle as serialisation
import time as temps

debut_chrono = temps.time()

chemin_donnees = './ensemble_donnees'
chemin_sauvegarde_batches = './fichiers_batches'

systeme.makedirs(chemin_sauvegarde_batches, exist_ok=True)

taille_image = 100
nombre_entraînement = 20000
nombre_test = 5000
taille_batch = 200

fichiers_entraînement = systeme.listdir(systeme.path.join(chemin_donnees, 'entrainement/'))
aleatoire.shuffle(fichiers_entraînement)

fichiers_train = fichiers_entraînement[:nombre_entraînement]
fichiers_test = fichiers_entraînement[nombre_entraînement:]

donnees_train = []
etiquettes_train = []
noms_train = []

donnees_test = []
etiquettes_test = []
noms_test = []

for fichier in fichiers_train:
    image = opencv.imread(systeme.path.join(chemin_donnees, 'entrainement/', fichier), 1)
    image_redimensionnee = opencv.resize(image, (taille_image, taille_image))
    tableau_image = np.array(image_redimensionnee)
    donnees_train.append(tableau_image)
    if 'chat' in fichier:
        etiquettes_train.append(0)
    elif 'chien' in fichier:
        etiquettes_train.append(1)
    else:
        raise ValueError(f'Fichier d\'entraînement invalide : {fichier}')
    noms_train.append(fichier)

for fichier in fichiers_test:
    image = opencv.imread(systeme.path.join(chemin_donnees, 'entrainement/', fichier), 1)
    image_redimensionnee = opencv.resize(image, (taille_image, taille_image))
    tableau_image = np.array(image_redimensionnee)
    donnees_test.append(tableau_image)
    if 'chat' in fichier:
        etiquettes_test.append(0)
    elif 'chien' in fichier:
        etiquettes_test.append(1)
    else:
        raise ValueError(f'Fichier de test invalide : {fichier}')
    noms_test.append(fichier)

print(f'Données entraînement : {len(donnees_train)}, test : {len(donnees_test)}')

debut_index = 0
fin_index = taille_batch
for numero_batch in range(1, 101):
    lot_donnees = donnees_train[debut_index:fin_index]
    lot_etiquettes = etiquettes_train[debut_index:fin_index]
    lot_noms = noms_train[debut_index:fin_index]
    nom_batch = f'lot entraînement {numero_batch} sur 15'

    donnees_batch = {
        'data': lot_donnees,
        'label': lot_etiquettes,
        'filenames': lot_noms,
        'name': nom_batch
    }

    with open(systeme.path.join(chemin_sauvegarde_batches, f'train_batch_{numero_batch}'), 'wb') as fichier_batch:
        serialisation.dump(donnees_batch, fichier_batch)

    debut_index += taille_batch
    fin_index += taille_batch

donnees_test_completes = {
    'data': donnees_test,
    'label': etiquettes_test,
    'filenames': noms_test,
    'name': 'lot test 1 sur 1'
}

with open(systeme.path.join(chemin_sauvegarde_batches, 'test_batch'), 'wb') as fichier_test:
    serialisation.dump(donnees_test_completes, fichier_test)

fin_chrono = temps.time()
print(f'Prétraitement terminé en {fin_chrono - debut_chrono} secondes.')

Construction du réseau de neurones convolutif


import tensorflow as tf

def construire_cnn(entree, taux_conservation):
    # Couche convolutif 1
    conv1a = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', padding='same', name='conv1a')(entree)
    conv1b = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', padding='same', name='conv1b')(conv1a)
    pool1 = tf.keras.layers.MaxPooling2D((2, 2), strides=(2, 2), name='pool1')(conv1b)
    
    # Couche convolutif 2
    conv2a = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', padding='same', name='conv2a')(pool1)
    conv2b = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', padding='same', name='conv2b')(conv2a)
    pool2 = tf.keras.layers.MaxPooling2D((2, 2), strides=(2, 2), name='pool2')(conv2b)
    
    # Couche convolutif 3
    conv3a = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', padding='same', name='conv3a')(pool2)
    conv3b = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', padding='same', name='conv3b')(conv3a)
    pool3 = tf.keras.layers.MaxPooling2D((2, 2), strides=(2, 2), name='pool3')(conv3b)
    
    # Couche convolutif 4
    conv4a = tf.keras.layers.Conv2D(256, (3, 3), activation='relu', padding='same', name='conv4a')(pool3)
    conv4b = tf.keras.layers.Conv2D(256, (3, 3), activation='relu', padding='same', name='conv4b')(conv4a)
    pool4 = tf.keras.layers.MaxPooling2D((2, 2), strides=(2, 2), name='pool4')(conv4b)
    
    # Couches entièrement connectées
    aplati = tf.keras.layers.Flatten(name='aplatir')(pool4)
    fc1 = tf.keras.layers.Dense(1024, activation='relu', name='fc1')(aplati)
    dropout1 = tf.keras.layers.Dropout(taux_conservation, name='dropout1')(fc1)
    fc2 = tf.keras.layers.Dense(512, activation='relu', name='fc2')(dropout1)
    dropout2 = tf.keras.layers.Dropout(taux_conservation, name='dropout2')(fc2)
    sortie = tf.keras.layers.Dense(2, activation=None, name='sortie')(dropout2)
    
    return sortie

Visualisation du graphe de calcul avec TensorFlow


taille_image = 100
taux_apprentissage = 0.001
taux_conservation = 0.7

# Définition des entrées et cibles
entree_x = tf.placeholder(tf.float32, [None, taille_image, taille_image, 3], 'entree_images')
cible_y = tf.placeholder(tf.int64, [None], 'cible_etiquettes')
taux_conservation_ph = tf.placeholder(tf.float32)

# Construction du modèle
sortie_fc = construire_cnn(entree_x, taux_conservation_ph)
perte = tf.losses.sparse_softmax_cross_entropy(labels=cible_y, logits=sortie_fc)
probabilites = tf.nn.softmax(sortie_fc)
predictions = tf.argmax(sortie_fc, 1)
precision = tf.reduce_mean(tf.cast(tf.equal(predictions, cible_y), tf.float32))
operation_entrainement = tf.train.AdamOptimizer(taux_apprentissage).minimize(perte)
sauvegardeur = tf.train.Saver(max_to_keep=1)

Entraînement du modèle


chemin_modele = './modeles/'
pas_entrainement = 10000
taille_lot = 64
pas_test = 100

liste_precision = []
with tf.Session() as session:
    session.run(tf.global_variables_initializer())
    
    for etape in range(pas_entrainement):
        donnees_lot, etiquettes_lot, _ = chargeur_donnees.batch_suivant(taille_lot)
        operations = [perte, precision, operation_entrainement]
        resultats = session.run(operations, feed_dict={
            entree_x: donnees_lot,
            cible_y: etiquettes_lot,
            taux_conservation_ph: taux_conservation
        })
        valeur_perte, precision_entrainement = resultats[0], resultats[1]
        
        liste_precision.append(precision_entrainement)
        if (etape + 1) % 100 == 0:
            precision_moyenne = np.mean(liste_precision)
            print(f'Étape : {etape+1}, Perte : {valeur_perte:.5f}, Précision : {precision_entrainement:.5f}, Précision moyenne : {precision_moyenne:.5f}')
        
        if (etape + 1) % 1000 == 0:
            precision_test_liste = []
            for _ in range(pas_test):
                donnees_test, etiquettes_test, _ = chargeur_test.batch_suivant(taille_lot)
                precision_val = session.run(precision, feed_dict={
                    entree_x: donnees_test,
                    cible_y: etiquettes_test,
                    taux_conservation_ph: 1.0
                })
                precision_test_liste.append(precision_val)
            precision_test_moyenne = np.mean(precision_test_liste)
            print(f'[Test] Étape : {etape+1}, Précision moyenne : {precision_test_moyenne:.5f}')
    
    systeme.makedirs(chemin_modele, exist_ok=True)
    sauvegardeur.save(session, chemin_modele + 'modele_animaux.ckpt')

Classification binaire pour images de chats et chiens

Ce pipeline illustre la classification d'images en deux classes, avec des adaptations pour la reconnaissance spécifique d'animaux. Les poids pré-entraînés et l'augmentation de données peuvent être ajustés pour améliorer les performances sur des jeux de données plus vastes.

Étiquettes: apprentissage profond réseaux de neurones convolutifs TensorFlow reconnaissance d'images détection d'animaux

Publié le 4 juin à 00h27