[PYTHON] Déterminer s'il s'agit de mon enfant à partir de l'image du chien Shiba par apprentissage profond (3) Visualisation par Grad-CAM

introduction

――Ceci est le résultat de mon propre dossier d'étude sur l'apprentissage automatique et l'apprentissage profond.

Public cible de cet article / Articles référencés

--Cible: identique à la précédente. Pour plus de détails ** ici **. --Article de référence ** Détection d'anomalies et visualisation des pièces anormales en implémentant vgg16 et Grad-CAM avec ○ keras **

À propos de moi

--Acquis ** JDLA Deep Learning pour Engeneer 2019 # 2 ** en septembre 2019. ――Jusqu'à la fin mars 2020, vous occuperez le bureau d'une société d'intérêt public. À partir d'avril 2020, il a changé de carrière en ingénieur de données. Pour plus de détails ** ici **.

Aperçu de l'analyse précédente (2)

--Les fichiers images (jpg) à analyser ont été augmentés à 120 photos de chiens de compagnie (chiens Shiba) et 120 photos de chiens Shiba (autres que les chiens de compagnie), pour un total de 240 photos, et ils ont été à nouveau classés en 2 par apprentissage profond. .. De plus, grâce à l'apprentissage par transfert et à la mise au point avec le modèle ImageNet (VGG16) et à la vérification avec les données de test, la précision de la classification est passée d'environ 75% à environ 95%.

Aperçu de la procédure de cette époque (3)

** Étape 1 Conversion de données, construction de modèles et apprentissage ** ** Étape 2 Mise en œuvre de Grad-CAM **

-Cet article de @T_Tao ** ([Détection d'anomalies et visualisation des pièces anormales en implémentant vgg16 et Grad-CAM avec keras](https: // qiita. L'implémentation de Grad-CAM a été introduite à l'adresse com / T_Tao / items / 0e869e440067518b6b58)) **.

Je voudrais implémenter le code introduit dans cet article et continuer à utiliser les données de photo de chien Shiba pour la visualisation avec Grad-CAM. Il est très intéressant de voir quelle partie de l'image du chien le deep learning considère comme une caractéristique et de faire la distinction entre les deux. Dans l'implémentation suivante, écrivez essentiellement le code de l'article de référence tel quel. De plus, les données de Google Drive configurées dans l'analyse précédente (2) seront utilisées telles quelles.

Étape 1 Conversion de données, construction de modèles et apprentissage

(1) Montez Google Drive

Montez-le pour pouvoir charger des données dans Colab à partir du dossier contenant l'image du chien Shiba.

#Monture Google Drive
from google.colab import drive
drive.mount('/content/drive')

(2) Importez la bibliothèque requise

Importez avec le code suivant.

#Importer la bibliothèque
from __future__ import print_function
import keras
from keras.applications import VGG16
from keras.models import Sequential, load_model, model_from_json
from keras import models, optimizers, layers
from keras.optimizers import SGD
from keras.layers import Dense, Dropout, Activation, Flatten
from sklearn.model_selection import train_test_split  
from PIL import Image 
from keras.preprocessing import image as images
from keras.preprocessing.image import array_to_img, img_to_array, load_img
from keras import backend as K 
import os
import numpy as np  
import glob  
import pandas as pd
import cv2

(3) Prétraitement des données

Jusqu'à la dernière fois, j'ai utilisé le générateur de données d'image de keras pour convertir les données d'image. Cette fois, au lieu d'utiliser Image Data Generator, utilisez le code suivant pour convertir le fichier image en tenseur.

# cd '/content/drive/'My Drive/'Colab Notebooks'Déplacer vers le dossier de travail dans
%cd '/content/drive/'My Drive/Colab Notebooks/Self_Study/02_mydog_or_otherdogs/

num_classes = 2 #Nombre de cours
folder = ["mydog2", "otherdogs2"] #Nom du dossier dans lequel les données photo sont stockées
image_size = 312 #La taille d'un morceau d'image d'entrée
x = []
y = []

for index, name in enumerate(folder):
    dir = "./original_data/" + name
    files = glob.glob(dir + "/*.jpg ")    
    for i, file in enumerate(files):    
        image = Image.open(file)                       
        image = image.convert("RGB")
        image = image.resize((image_size, image_size))
        data = np.asarray(image) 
        x.append(data)  
        y.append(index) 
#Np si vous souhaitez convertir la liste en un tableau Numpy.array、np.Il existe deux types, un tableau.
#Si vous souhaitez faire une copie du tableau Numpy, np.Utilisez un tableau.
#Si vous souhaitez faire une copie qui reste synchronisée avec le tableau Numpy d'origine, np.Utilisez un tableau.

x = np.array(x)   
y = np.array(y)

(4) Confirmation des données converties

Vérifions comment les données sont converties et stockées en x et y.

#Vérifiez le contenu de x
display(x)

Le contenu de x qui a converti les données d'image est converti dans la liste suivante.

array([[[[114, 109, 116],
         [116, 111, 118],
         [104,  99, 105],
         ...,
         [ 37,  38,  30],
         [ 37,  38,  30],
         [ 36,  37,  29]],

        [[117, 112, 119],
         [120, 115, 121],
         [110, 105, 111],
         ...,
         [ 37,  38,  30],
         [ 37,  38,  30],
         [ 37,  38,  30]],

        [[118, 113, 120],
         [121, 116, 122],
         [114, 109, 115],
         ...,
         [ 37,  38,  30],
         [ 38,  39,  31],
         [ 38,  39,  31]],
(Omis)

       ...,

        [[ 60,  56,  53],
         [ 60,  56,  53],
         [ 61,  57,  54],
         ...,
         [105,  97,  84],
         [105,  97,  84],
         [104,  96,  83]]]], dtype=uint8)

'\n[[[0, 0, 0],\n         [0, 0, 0],\n         [0, 0, 0],\n         ...,\n         [0, 0, 0],\n         [0, 0, 0],\n         [0, 0, 0]],\n'

Vérifiez le contenu de y (étiquette).

#Vérifiez le contenu de y
y

y est généré avec deux types d'étiquettes, "0" et "1".

array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])

(5) Divisez les données converties en données de train et données de test

Le tenseur transformé est divisé sur train_test_split de sklearn pour l'entraînement avec le modèle construit plus tard.

#Divisé en données de train et données de test
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=1)
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255

(6) Convertir les étiquettes en représentation one-hot

#a changé l'étiquette y en expression one-hot
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)

print(x_train.shape[0], 'train samples')
print(x_test.shape[0], 'test samples')

Vous devriez voir un résultat similaire au suivant:

192 train samples
48 test samples

(7) Construction et compilation de modèles

Créez le modèle avec le code suivant. Cet optimiseur spécifie SDG (méthode de descente de gradient probabiliste).

vgg_conv = VGG16(weights='imagenet', include_top=False, input_shape=(image_size, image_size, 3))
last = vgg_conv.output

mod = Flatten()(last)
mod = Dense(1024, activation='relu')(mod)
mod = Dropout(0.5)(mod)
preds = Dense(2, activation='sigmoid')(mod)

model = models.Model(vgg_conv.input, preds)
model.summary()

epochs = 100
batch_size = 48

model.compile(loss='binary_crossentropy',
              optimizer=optimizers.SGD(lr=1e-4, momentum=0.9),
              metrics=['accuracy'])

(7) Apprentissage de modèles

Former le modèle.

history = model.fit(x_train, y_train,
                    batch_size=batch_size,
                    epochs=epochs,
                    validation_data=(x_test, y_test),
                    shuffle=True)

Enregistrez le modèle.

model.save('mydog_or_otherdogs3(Grad-Cam).h5')

(8) Évaluation du modèle formé et transition de l'exactitude et de la perte

Affichez le résultat avec le code suivant et tracez un graphique. Le résultat de la validation est également élevé probablement parce que le fichier image utilise toutes les données (240 feuilles).

#affichage du score
scores = model.evaluate(x_test, y_test, verbose=1)
print('Test loss:', scores[0])
print('Test accuracy:', scores[1])

#précision et graphique des pertes
import matplotlib.pyplot as plt 

acc = history.history["acc"]
val_acc = history.history["val_acc"]
loss = history.history["loss"]
val_loss = history.history["val_loss"]
epochs = range(1, len(acc) + 1)

plt.plot(epochs, acc, label = "Training acc" )
plt.plot(epochs, val_acc, label = "Validation acc")
plt.title("Training and Validation accuracy")
plt.legend()
plt.show()

plt.plot(epochs, loss,  label = "Training loss" )
plt.plot(epochs, val_loss, label = "Validation loss")
plt.title("Training and Validation loss")
plt.legend()
plt.show()

Le résultat est le suivant.

Test loss: 0.04847167782029327
Test accuracy: 0.9795918367346939

traial07.png

Étape 2 Mise en œuvre de Grad-CAM

(1) Implémentation de Grad-CAM

Entrez le code ci-dessous. D'après @T_Tao ** Grad-CAM avec un modèle que j'ai fait avec des keras * Cela signifie qu'il est basé sur le code de *.

K.set_learning_phase(1) #set learning phase

def Grad_Cam(input_model, pic_array, layer_name):

    #Prétraitement
    pic = np.expand_dims(pic_array, axis=0)
    pic = pic.astype('float32')
    preprocessed_input = pic / 255.0

    #Calcul de la classe de prédiction
    predictions = input_model.predict(preprocessed_input)
    class_idx = np.argmax(predictions[0])
    class_output = input_model.output[:, class_idx]

    #Obtenir un dégradé
    conv_output = input_model.get_layer(layer_name).output   # layer_Sortie de la couche de nom
    grads = K.gradients(class_output, conv_output)[0]  # gradients(loss, variables)Renvoie le gradient par rapport à la perte de variables
    gradient_function = K.function([input_model.input], [conv_output, grads])  # input_model.Lorsque vous entrez l'entrée, conv_Fonction de sortie de sortie et de grades

    output, grads_val = gradient_function([preprocessed_input])
    output, grads_val = output[0], grads_val[0]

    #Faire la moyenne des poids et multiplier par la sortie de la couche
    weights = np.mean(grads_val, axis=(0, 1))
    cam = np.dot(output, weights)

    #Image et combiner comme une carte thermique
    cam = cv2.resize(cam, (312, 312), cv2.INTER_LINEAR) 
    cam = np.maximum(cam, 0) 
    cam = cam / cam.max()

    jetcam = cv2.applyColorMap(np.uint8(255 * cam), cv2.COLORMAP_JET)  #Images monochromes pseudo-couleurs
    jetcam = cv2.cvtColor(jetcam, cv2.COLOR_BGR2RGB)  #Convertir la couleur en RVB
    jetcam = (np.float32(jetcam) + pic / 2)   #Combiné avec l'image d'origine
    return jetcam

Appliquons le résultat à quelques photos de chiens Shiba. Tout d'abord, de mon chien.

# cd '/content/drive/'My Drive/'Colab Notebooks'Déplacer vers le dossier spécifié dans
%cd '/content/drive/'My Drive/Colab Notebooks/

pic_array = img_to_array(load_img('/content/drive/My Drive/Colab Notebooks/Self_Study/02_mydog_or_otherdogs/original_data/mydog2/mydog1.jpg', target_size=(312, 312)))
pic = pic_array.reshape((1,) + pic_array.shape)
array_to_img(pic_array)

gradcam01b.png

Superposer la carte de chaleur

picture = Grad_Cam(model, pic_array, 'block5_conv3')
picture = picture[0,:,:,]
array_to_img(picture)

gradcam01.png

C'est comme ça? En visualisant avec la carte thermique de Grad-CAM, il a été dessiné de quel côté l'apprentissage profond est considéré comme une fonctionnalité. La partie où la couleur est la plus rouge est la partie qui contribue grandement à la perte de la classe de prédiction (la partie avec un grand dégradé), mais après tout c'est la partie qui frappe de sous les yeux au nez du visage, etc. Je me suis demandé si je regardais la partie où l'individualité apparaissait sur le visage. Ce qui était un peu surprenant, c'est que la couleur de la carte thermique est plus foncée entre les yeux et les oreilles (là?).

J'ai également appliqué environ 2 photos de nos Mirin et environ 3 photos d'autres chiens Shiba et les ai arrangées. gradcam02b.pnggradcam02.png gradcam03b.pnggradcam03.png

gradcam11b.pnggradcam11.png gradcam12b.pnggradcam12.png gradcam13b.pnggradcam13.png

Certaines images regardent des parties similaires (des yeux au nez), tandis que d'autres regardent des parties complètement différentes, ce qui est assez intéressant. Je pense qu'il semble y avoir une tendance générale pour les fonctionnalités, mais cela peut être un peu difficile à expliquer avec cette seule carte de chaleur.

Cette fois, nous avons créé une carte thermique à l'aide de Grad-CAM. Il semble y avoir diverses autres méthodes pour visualiser les parties de fonctionnalités, telles que Grad-CAM ++ et Guided-Grad-CAM, donc j'aimerais essayer diverses méthodes à partir de la prochaine fois.

Recommended Posts

Déterminer s'il s'agit de mon enfant à partir de l'image du chien Shiba par apprentissage profond (3) Visualisation par Grad-CAM
À en juger par l'image du chien Shiba en apprenant en profondeur si c'est mon enfant (1)
Juger si c'est mon enfant à partir de l'image du chien Shiba par apprentissage en profondeur (2) Augmentation des données / transfert d'apprentissage / réglage fin
Traitement de la voix par apprentissage profond: identifions qui est l'acteur vocal à partir de la voix
Othello ~ De la troisième ligne de "Implementation Deep Learning" (4) [Fin]
La méthode de copie de pandas.DataFrame est une copie profonde par défaut
Évaluer la précision du modèle d'apprentissage par test croisé de scikit learn
Découvrez le nom de la méthode qui l'a appelée à partir de la méthode qui est python