――Dies ist das Ergebnis meiner eigenen Studienaufzeichnung über maschinelles Lernen und tiefes Lernen.
――Wir haben Grad-CAM implementiert und eine Heatmap mit den Funktionen erstellt, die die Grundlage für die Klassifizierung von Shiba-Hundefotos bilden.
** 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 **
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')
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
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'
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.
Um die Laufzeit neu zu starten, klicken Sie in der Menüleiste unter "Laufzeit" auf "Laufzeit neu starten".
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
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
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.
--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
_________________________________________________________________
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.
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.
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 otherdogs
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