[PYTHON] Bestimmen Sie anhand des Bildes des Shiba-Hundes anhand des Bildes des Shiba-Hundes, ob es sich um ein Kind handelt

Einführung

――Dies ist das Ergebnis meiner eigenen Studienaufzeichnung über maschinelles Lernen und tiefes Lernen.

Zielgruppe dieses Artikels / Artikel, auf die verwiesen wird

über mich

Überblick über die vorherige Analyse (3)

――Wir haben Grad-CAM implementiert und eine Heatmap mit den Funktionen erstellt, die die Grundlage für die Klassifizierung von Shiba-Hundefotos bilden.

Verfahrensablauf dieser Zeit (4)

** Verfahren 1 Vorbereitung ** ** Schritt 2 Registrieren Sie die Funktionen, die zur Implementierung von Grad-CAM und Guided Grad-CAM erforderlich sind. ** ** Schritt 3 Implementierung der Hauptverarbeitung von Grad-CAM ** ** Schritt 4 Implementierung der Hauptverarbeitung von Guided Grad-CAM **

Schritt 1 Vorbereitung

(1) Hängen Sie Google Drive ein

Mount, damit die Daten aus dem Ordner mit dem Bild des Shiba-Hundes in Colab eingelesen werden können.

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

(2) Importieren Sie die erforderliche Bibliothek

Importieren Sie mit dem folgenden Code.

#Bibliothek importieren
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) Keras-Versionsprüfung

Lassen Sie uns die Version der importierten Keras überprüfen (Stand: 19. Januar 2020, wenn dieser Artikel geschrieben wird). Es ist diese Version.)

print(keras.__version__)

2.2.5

Hier ist ein Punkt zu beachten. Der Inhalt, an dem ich interessiert war, wurde in ** Kommentar ** geschrieben, der dem Originalartikel beigefügt ist, aber die Version von Keras ist ** 2.2. Wenn es nicht weniger als .4 ** ist, scheint es, dass beim Ausführen des Quellmantels ein Fehler auftritt. Als ich es versuchte, bekam ich die folgende Fehlermeldung in der letzten Zeile der Reihe der Quellcodeausführung. Immerhin scheint es, dass Sie die Version von Keras senken müssen, bevor Sie es ausführen.

AttributeError: module 'keras.backend' has no attribute 'image_dim_ordering'

(4) Verringern Sie die Version von Keras

Führen Sie den folgenden Code aus.

#Spezifische Version von Keras(2.2.4)Ändern
#Erfordert nach der Ausführung einen Laufzeitneustart
!pip install keras==2.2.4

Nach der Ausführung wird der folgende Bildschirm angezeigt und die Bibliothek wird auf die angegebene Version geändert. Da der braune Text Sie jedoch warnt, müssen Sie die Laufzeit neu starten, damit diese Änderung wirksam wird. 20200119-01.png

Um die Laufzeit neu zu starten, klicken Sie in der Menüleiste unter "Laufzeit" auf "Laufzeit neu starten". 20200119-02.png

(5) Wiederholen Sie die Schritte (1) bis (3).

Wiederholen Sie nach dem Neustart der Laufzeit alle vorherigen Schritte in Schritt 1, um sicherzustellen, dass die Keras-Version 2.2.4 ist.

print(keras.__version__)

2.2.4

Schritt 2 Registrieren Sie die Funktionen, die zur Implementierung von Grad-CAM und Guided Grad-CAM erforderlich sind

(1) Führen Sie den im Originalartikel veröffentlichten Funktionsdefinitionscode aus

Führen Sie den folgenden Code aus.

def target_category_loss(x, category_index, nb_classes):
    return tf.multiply(x, K.one_hot([category_index], nb_classes))

def target_category_loss_output_shape(input_shape):
    return input_shape

def normalize(x):
    # utility function to normalize a tensor by its L2 norm
    return x / (K.sqrt(K.mean(K.square(x))) + 1e-5)

def load_image(path):
    #img_path = sys.argv[1]
    img_path = path
    #Lesen Sie die durch das Argument angegebene Bilddatei
    #Die Größe wird auf die Standardeinstellung von VGG16 von 224 x 224 geändert
    img = image.load_img(img_path, target_size=(224, 224))
    #Konvertieren Sie das gelesene Bild im PIL-Format in ein Array
    x = image.img_to_array(img)
    #3D-Tensor (Reihen), cols, channels)Zu
    #4D Tensor(samples, rows, cols, channels)Umstellung auf
    #Da es nur ein Eingabebild gibt, Samples=1 ist in Ordnung
    x = np.expand_dims(x, axis=0)
    x = preprocess_input(x)
    return x


def register_gradient():
    #Wenn GuidedBackProp nicht registriert ist, registrieren Sie sich
    if "GuidedBackProp" not in ops._gradient_registry._registry:
        #Dekorateur, um selbst gemachten Farbverlauf zu registrieren
        #Diesmal_GuidedBackProp-Funktion"GuidedBackProp"Registrieren Sie sich als
        @ops.RegisterGradient("GuidedBackProp")
        def _GuidedBackProp(op, grad):
            '''Von den Gradienten, die sich rückwärts ausgebreitet haben, vorwärts/Die Rückausbreitung wird durchgeführt, indem nur Zellen mit einem negativen Rückausbreitungswert auf 0 gesetzt werden'''
            dtype = op.inputs[0].dtype
            # grad :Backpropagation-Gradient
            # tf.cast(grad > 0., dtype) :1 für Zellen mit einem Grad von 0 oder mehr,Zellen unter 0 sind 0 Matrizen
            # tf.cast(op.inputs[0] > 0., dtype) :1 für Zellen mit 0 oder mehr Eingängen,Zellen unter 0 sind 0 Matrizen
            return grad * tf.cast(grad > 0., dtype) * \
                tf.cast(op.inputs[0] > 0., dtype)


def compile_saliency_function(model, activation_layer='block5_conv3'):
    '''Erstellen einer Funktion, die den Gradienten der Eingabe in Bezug auf den Maximalwert in Kanalrichtung der angegebenen Ebene berechnet'''
    #Modelleingabe
    input_img = model.input
    #Behalten Sie die Ebene nach der Eingabeebene als Wörterbuch mit Ebenennamen und -instanzen bei
    layer_dict = dict([(layer.name, layer) for layer in model.layers[1:]])
    #Ruft die Ausgabe der Instanz mit dem durch die Argumentform angegebenen Ebenennamen ab=(?, 14, 14, 512)
    layer_output = layer_dict[activation_layer].output
    #Form, die den Maximalwert in Kanalrichtung annimmt=(?, 14, 14)
    max_output = K.max(layer_output, axis=3)
    #Eine Funktion, die den Gradienten der Eingabe in Bezug auf den Maximalwert in Kanalrichtung der angegebenen Ebene berechnet.
    saliency = K.gradients(K.sum(max_output), input_img)[0]
    return K.function([input_img, K.learning_phase()], [saliency])


def modify_backprop(model, name):
    '''Der Gradient der ReLU-Funktion"name"Durch Farbverlauf ersetzen'''

    #ReLU in mit"name"Ersetzt durch
    g = tf.get_default_graph()
    with g.gradient_override_map({'Relu': name}):

        #▽▽▽▽▽ Frage 4:Ist es notwendig, das Relu des Argumentmodells zu ersetzen, obwohl das neue Modell zurückgegeben wird?? ▽▽▽▽▽

        #Extrahieren und ordnen Sie nur die Ebenen an, die aktiviert sind
        # get layers that have an activation
        layer_dict = [layer for layer in model.layers[1:]
                      if hasattr(layer, 'activation')]

        #Keras RelU durch Tensorflow ReLU ersetzt
        # replace relu activation
        for layer in layer_dict:
            if layer.activation == keras.activations.relu:
                layer.activation = tf.nn.relu

        # △△△△△△△△△△△△△△△△△△△△△△△△△△△△△△△△△△△△△△△△△△△△△△△△△△△

        #Instanziieren Sie ein neues Modell
        #Ändern Sie hier, wenn Sie Ihr eigenes Modell verwenden
        # re-instanciate a new model
        new_model = VGG16(weights='imagenet')
    return new_model

def deprocess_image(x):
    '''
    Same normalization as in:
    https://github.com/fchollet/keras/blob/master/examples/conv_filter_visualization.py

    '''
    if np.ndim(x) > 3:
        x = np.squeeze(x)
    # normalize tensor: center on 0., ensure std is 0.1
    x -= x.mean()
    x /= (x.std() + 1e-5)
    x *= 0.1

    # clip to [0, 1]
    x += 0.5
    x = np.clip(x, 0, 1)

    # convert to RGB array
    x *= 255
    if K.image_dim_ordering() == 'th':
        x = x.transpose((1, 2, 0))
    x = np.clip(x, 0, 255).astype('uint8')
    return x

def grad_cam(input_model, image, category_index, layer_name):
    '''
    Parameters
    ----------
    input_model : model
Keras-Modell zu bewerten
    image :Tupel usw.
Bild eingeben(Anzahl der Blätter,Vertikal,Seite,Kanal)
    category_index : int
Eingabebildklassifizierungsklasse
    layer_name : str
Ebenenname der Aktivierungsschicht nach der letzten Conv-Schicht.
Wenn die Aktivierung in der letzten Conv-Ebene angegeben ist, der Layername der Conv-Ebene.
        batch_Wenn in der Conv-Schicht keine Aktivierung angegeben ist, z. B. bei Verwendung der Normalisierung,
Ebenenname der Aktivierungsschicht danach.

    Returns
    ----------
    cam : tuple
        Grad-Cam Bild
    heatmap : tuple
Wärmekartenbild
    '''
    #Anzahl der Klassifizierungsklassen
    nb_classes = 1000

    # ----- 1.Berechnen Sie die Vorhersageklasse des Eingabebildes-----

    #Eingabekategorie_Index ist erwartete Klasse

    # ----- 2.Berechnen Sie den Verlust für die Vorhersageklasse-----

    #Eingabedaten x Kategorie_Definition der Verarbeitung zum Setzen einer anderen 0 als des durch den Index angegebenen Index
    target_layer = lambda x: target_category_loss(x, category_index, nb_classes)

    #Argumenteingabe_Ziel nach der Ausgabeschicht des Modells_Ebene Ebene hinzufügen
    #Wenn Sie das Modell vorhersagen, sind andere Werte als die Vorhersageklasse 0.
    x = input_model.layers[-1].output
    x = Lambda(target_layer, output_shape=target_category_loss_output_shape)(x)
    model = keras.models.Model(input_model.layers[0].input, x)

    #Da die anderen Werte als die Vorhersageklasse 0 sind, wird die Summe genommen und nur der Wert der Vorhersageklasse extrahiert.
    loss = K.sum(model.layers[-1].output)
    #Argumentschicht_Namensschicht(Letzte Conv-Schicht)Holen Sie sich die Ausgabe von
    conv_output = [l for l in model.layers if l.name is layer_name][0].output

    # ----- 3.Backpropagation von der Vorhersageklasse Loss bis zur letzten Conv-Schicht(Steigung)Berechnung-----

    #Definieren Sie eine Funktion zur Berechnung des Gradienten vom Wert der erwarteten Klasse bis zur letzten Conv-Schicht
    #Von der definierten Funktion
    #Eingang: [Bild, das Sie beurteilen möchten.shape=(1, 224, 224, 3)]、
    #Ausgabe: [Ausgabewert der letzten Conv-Schicht.shape=(1, 14, 14, 512),Gradient vom erwarteten Klassenwert bis zur letzten Conv-Ebene.shape=(1, 14, 14, 512)]
    grads = normalize(K.gradients(loss, conv_output)[0])
    gradient_function = K.function([model.layers[0].input], [conv_output, grads])

    #Berechnen Sie mit der definierten Gradientenberechnungsfunktion und formatieren Sie die Abmessungen der Daten
    #Nach dem Formen
    # output.shape=(14, 14, 512), grad_val.shape=(14, 14, 512)
    output, grads_val = gradient_function([image])
    output, grads_val = output[0, :], grads_val[0, :, :, :]

    # ----- 4.Berechnen Sie den durchschnittlichen Gradienten für jeden Kanal in der letzten Conv-Schicht und die Wichtigkeit jedes Kanals(Gewicht)Zu-----

    # weights.shape=(512, )
    # cam.shape=(14, 14)
    #* Frage 1: Muss die Nockeninitialisierung nicht Null sein??
    weights = np.mean(grads_val, axis = (0, 1))
    cam = np.ones(output.shape[0 : 2], dtype = np.float32)
    #cam = np.zeros(output.shape[0 : 2], dtype = np.float32)    #Verwenden Sie dies in meinem eigenen Modell

    # ----- 5.Die Vorwärtsausbreitungsausgabe der letzten Conv-Schicht wird für jeden Kanal gewichtet, addiert und durch ReLU geleitet.-----

    #Die Vorwärtsausbreitungsausgabe der letzten Conv-Schicht wird für jeden Kanal gewichtet und addiert.
    for i, w in enumerate(weights):
        cam += w * output[:, :, i]

    #Passen Sie die Größe an das Eingabebild an(14, 14) → (224, 224)
    cam = cv2.resize(cam, (224, 224))
    #Ersetzen Sie negative Werte durch 0. Die Verarbeitung ist die gleiche wie bei ReLU.
    cam = np.maximum(cam, 0)
    #Wert 0~Normalisiert auf 1.
    #* Frage 2: (cam - np.min(cam))/(np.max(cam) - np.min(cam))Ist es nicht nötig?
    heatmap = cam / np.max(cam)
    #heatmap = (cam - np.min(cam))/(np.max(cam) - np.min(cam))    #Verwenden Sie dies in meinem eigenen Modell

    # ----- 6.Multiplizieren Sie das Eingabebild und die Heatmap-----

    #Setzen Sie den Wert des Eingabebildes auf 0~Normalisiert auf 255. image.shape=(1, 224, 224, 3) → (224, 224, 3)
    #Return to BGR [0..255] from the preprocessed image
    image = image[0, :]
    image -= np.min(image)
    #* Frage 3: np.uint8(image / np.max(image))Muss nicht sein?
    image = np.minimum(image, 255)

    #Setzen Sie den Wert der Heatmap auf 0~Machen Sie es 255 und machen Sie es eine Farbkarte(3 Kanäle)
    cam = cv2.applyColorMap(np.uint8(255*heatmap), cv2.COLORMAP_JET)
    #Hinzufügen von Eingabebild und Heatmap
    cam = np.float32(cam) + np.float32(image)
    #Wert 0~Normalisiert auf 255
    cam = 255 * cam / np.max(cam)
    return np.uint8(cam), heatmap

Schritt 3 Implementierung der Hauptverarbeitung von Grad-CAM

(1) Bezeichnung des Eingabebildes

Geben sie den untenstehenden Code ein. Geben Sie ein Bild für das Bild an. (Geben Sie in diesem Beispiel mydog7.jpg an.)

# cd '/content/drive/'My Drive/'Colab Notebooks'Wechseln Sie in den Arbeitsordner in
%cd '/content/drive/'My Drive/Colab Notebooks/Self_Study/02_mydog_or_otherdogs/

#① Eingabebild lesen
#Ändern Sie hier, um das Eingabebild zu konvertieren
# preprocessed_input = load_image(sys.argv[1])
preprocessed_input = load_image("./use_data/train/mydog/mydog07.jpg ")

In diesem Beispiel habe ich dieses Bild angegeben. mydog7.jpg

(2) Laden des VGG16-Modells

--Laden Sie das VGG16-Modell. Dieses Mal werden wir das mit ImageNet trainierte Modell so verwenden, wie es ist. (Ich möchte die Gewichte implementieren, die den Unterschied zwischen meinem Kind und anderen Kindern zu einem anderen Zeitpunkt bestimmen.)

#② Laden Sie das Modell
#Ändern Sie hier, wenn Sie Ihr eigenes Modell verwenden
model = VGG16(weights='imagenet')
model.summary()

Das Modell sollte folgendermaßen aussehen:

Model: "vgg16"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         (None, 224, 224, 3)       0         
_________________________________________________________________
block1_conv1 (Conv2D)        (None, 224, 224, 64)      1792      
_________________________________________________________________
block1_conv2 (Conv2D)        (None, 224, 224, 64)      36928     
_________________________________________________________________
block1_pool (MaxPooling2D)   (None, 112, 112, 64)      0         
_________________________________________________________________
block2_conv1 (Conv2D)        (None, 112, 112, 128)     73856     
_________________________________________________________________
block2_conv2 (Conv2D)        (None, 112, 112, 128)     147584    
_________________________________________________________________
block2_pool (MaxPooling2D)   (None, 56, 56, 128)       0         
_________________________________________________________________
block3_conv1 (Conv2D)        (None, 56, 56, 256)       295168    
_________________________________________________________________
block3_conv2 (Conv2D)        (None, 56, 56, 256)       590080    
_________________________________________________________________
block3_conv3 (Conv2D)        (None, 56, 56, 256)       590080    
_________________________________________________________________
block3_pool (MaxPooling2D)   (None, 28, 28, 256)       0         
_________________________________________________________________
block4_conv1 (Conv2D)        (None, 28, 28, 512)       1180160   
_________________________________________________________________
block4_conv2 (Conv2D)        (None, 28, 28, 512)       2359808   
_________________________________________________________________
block4_conv3 (Conv2D)        (None, 28, 28, 512)       2359808   
_________________________________________________________________
block4_pool (MaxPooling2D)   (None, 14, 14, 512)       0         
_________________________________________________________________
block5_conv1 (Conv2D)        (None, 14, 14, 512)       2359808   
_________________________________________________________________
block5_conv2 (Conv2D)        (None, 14, 14, 512)       2359808   
_________________________________________________________________
block5_conv3 (Conv2D)        (None, 14, 14, 512)       2359808   
_________________________________________________________________
block5_pool (MaxPooling2D)   (None, 7, 7, 512)         0         
_________________________________________________________________
flatten (Flatten)            (None, 25088)             0         
_________________________________________________________________
fc1 (Dense)                  (None, 4096)              102764544 
_________________________________________________________________
fc2 (Dense)                  (None, 4096)              16781312  
_________________________________________________________________
predictions (Dense)          (None, 1000)              4097000   
=================================================================
Total params: 138,357,544
Trainable params: 138,357,544
Non-trainable params: 0
_________________________________________________________________

(3) Geben Sie die verbleibenden Schritte ein.

Führen Sie den folgenden Code aus.

#③ Vorhersagewahrscheinlichkeit des Eingabebildes(predictions)Und vorausschauende Klasse(predicted_class)Berechnung von
#Top bei Verwendung anderer Modelle als VGG16_1=~3 Zeilen vom Kommentar aus
predictions = model.predict(preprocessed_input)
top_1 = decode_predictions(predictions)[0][0]
print('Predicted class:')
print('%s (%s) with probability %.2f' % (top_1[1], top_1[0], top_1[2]))

predicted_class = np.argmax(predictions)

# ④ Grad-Nockenberechnung
#Im Fall eines selbst erstellten Modells das Argument"block5_conv3"Geändert zum Ebenennamen der letzten Conv-Ebene des selbst erstellten Modells.
cam, heatmap = grad_cam(model, preprocessed_input, predicted_class, "block5_conv3")

#⑤ Bild speichern
cv2.imwrite("gradcam.jpg ", cam)

Ein Grad-CAM-Heatmap-Bild wird im Ordner ** 02_mydog_or_otherdogs ** generiert. gradcam2.jpg

Schritt 4 Implementierung der Hauptverarbeitung von Guided Grad-CAM

(1) Bezeichnung des Eingabebildes

Führen Sie den folgenden Code aus.

#① Implementierung eines Gradienten für die geführte Rückausbreitung
register_gradient()
#(2) Ändern Sie die Gradientenberechnung von ReLU in die Gradientenberechnung von Guided Back Propagation
guided_model = modify_backprop(model, 'GuidedBackProp')
#③ Definition der Funktion für die GaidedBackPropagation-Berechnung
#Wenn Sie Ihre eigene Klasse verwenden, geben Sie zusätzlich zu diesem Argument den Ebenennamen der letzten Conv-Ebene an
saliency_fn = compile_saliency_function(guided_model)
#④ Berechnung der verzögerten Rückausbreitung
saliency = saliency_fn([preprocessed_input, 0])
# ⑤ Guided Grad-CAM-Berechnung
gradcam = saliency[0] * heatmap[..., np.newaxis]
#⑥ Bild speichern
cv2.imwrite("guided_gradcam.jpg ", deprocess_image(gradcam))

Ein Heatmap-Bild von Guided Grad-CAM wird im Ordner ** 02_mydog_or_otherdogs ** generiert. guided_gradcam2.jpg

Es kommt so heraus. Sicherlich habe ich das Gefühl, dass es die Eigenschaften in einer Form erfasst, die für den Menschen leicht zu verstehen ist, anstatt auf die Wärmekarte zu schauen.

Unten werde ich einige von mydog und otherdogs verarbeitete veröffentlichen. mydog gradcam3.jpgguided_gradcam3.jpg gradcam4.jpgguided_gradcam4.jpg gradcam5.jpgguided_gradcam5.jpg gradcam6.jpgguided_gradcam6.jpg otherdogs gradcam11.jpgguided_gradcam11.jpg gradcam12.jpgguided_gradcam12.jpg gradcam13.jpgguided_gradcam13.jpg gradcam14.jpgguided_gradcam14.jpg

Dieses Mal haben wir ein Bild erstellt, das die Funktionen von Grad-CAM und Guided Grad-CAM erfasst. Da der Ausdruck der Merkmalsmenge in jeder Methode, die im Bild angezeigt wird, sehr unterschiedlich ist, ist es bei der Erläuterung des durch Deeplearning erfassten Merkmals besser, so weit wie möglich verschiedene Perspektiven zu verwenden und ein umfassendes Verständnis zu suchen. Es scheint möglich zu sein. Ich möchte weiterhin über verschiedene Methoden berichten.

Recommended Posts

Bestimmen Sie anhand des Bildes des Shiba-Hundes anhand des Bildes des Shiba-Hundes, ob es sich um ein Kind handelt
Bestimmen, ob es mein Kind ist, anhand des Bildes eines Shiba-Hundes durch tiefes Lernen (3) Visualisierung durch Grad-CAM
Nach dem Bild des Shiba-Hundes zu urteilen, indem man tief lernt, ob es mein Kind ist (1)
Beurteilung, ob es sich um mein Kind handelt oder nicht, nach dem Bild des Shiba-Hundes durch tiefes Lernen (2) Datenerhöhung, Transferlernen, Feinabstimmung
Sprachverarbeitung durch tiefes Lernen: Lassen Sie uns anhand der Stimme herausfinden, wer der Sprecher ist
Deep Learning von Grund auf neu Die Theorie und Implementierung des mit Python erlernten Deep Learning Kapitel 3
Paralleles Lernen von Deep Learning durch Keras und Kubernetes
Othello ~ Aus der dritten Zeile von "Implementation Deep Learning" (4) [Ende]
Die Kopiermethode von pandas.DataFrame ist standardmäßig Deep Copy
(Deep Learning) Ich habe Bilder von der Flickr-API gesammelt und versucht, durch Transferlernen mit VGG16 zu unterscheiden
Negative / positive Beurteilung von Sätzen und Visualisierung von Gründen durch Transformer
Negative / positive Beurteilung von Sätzen durch BERT und Visualisierung von Gründen
Ich habe versucht, den Teil, in dem die Pflanze gezeigt wird, mithilfe von Deep Learning aus dem Foto der Veranda zu extrahieren, aber es hat nicht funktioniert, daher werde ich den Inhalt von Versuch und Irrtum zusammenfassen. Teil 2