--Une des méthodes de visualisation de la base de jugement de CNN
import cv2
import numpy as np
from keras.models import Model
def ScoreCam(model, img_array, layer_name, max_N=-1):
cls = np.argmax(model.predict(img_array))
act_map_array = Model(inputs=model.input, outputs=model.get_layer(layer_name).output).predict(img_array)
# extract effective maps
if max_N != -1:
act_map_std_list = [np.std(act_map_array[0,:,:,k]) for k in range(act_map_array.shape[3])]
unsorted_max_indices = np.argpartition(-np.array(act_map_std_list), max_N)[:max_N]
max_N_indices = unsorted_max_indices[np.argsort(-np.array(act_map_std_list)[unsorted_max_indices])]
act_map_array = act_map_array[:,:,:,max_N_indices]
input_shape = model.layers[0].output_shape[1:] # get input shape
# 1. upsampled to original input size
act_map_resized_list = [cv2.resize(act_map_array[0,:,:,k], input_shape[:2], interpolation=cv2.INTER_LINEAR) for k in range(act_map_array.shape[3])]
# 2. normalize the raw activation value in each activation map into [0, 1]
act_map_normalized_list = []
for act_map_resized in act_map_resized_list:
if np.max(act_map_resized) - np.min(act_map_resized) != 0:
act_map_normalized = act_map_resized / (np.max(act_map_resized) - np.min(act_map_resized))
else:
act_map_normalized = act_map_resized
act_map_normalized_list.append(act_map_normalized)
# 3. project highlighted area in the activation map to original input space by multiplying the normalized activation map
masked_input_list = []
for act_map_normalized in act_map_normalized_list:
masked_input = np.copy(img_array)
for k in range(3):
masked_input[0,:,:,k] *= act_map_normalized
masked_input_list.append(masked_input)
masked_input_array = np.concatenate(masked_input_list, axis=0)
# 4. feed masked inputs into CNN model and softmax
pred_from_masked_input_array = softmax(model.predict(masked_input_array))
# 5. define weight as the score of target class
weights = pred_from_masked_input_array[:,cls]
# 6. get final class discriminative localization map as linear weighted combination of all activation maps
cam = np.dot(act_map_array[0,:,:,:], weights)
cam = np.maximum(0, cam) # Passing through ReLU
cam /= np.max(cam) # scale 0 to 1.0
return cam
def softmax(x):
f = np.exp(x)/np.sum(np.exp(x), axis = 1, keepdims = True)
return f
Appliquons-le à VGG16 qui a été formé avec ImageNet.
from keras.preprocessing.image import load_img
import matplotlib.pyplot as plt
orig_img = np.array(load_img('./image/hummingbird.jpg'),dtype=np.uint8)
plt.imshow(orig_img)
plt.show()
from keras.applications.vgg16 import VGG16
from gradcamutils import read_and_preprocess_img
import matplotlib.pyplot as plt
model = VGG16(include_top=True, weights='imagenet')
layer_name = 'block5_conv3'
img_array = read_and_preprocess_img('./image/hummingbird.jpg', size=(224,224))
score_cam = ScoreCam(model,img_array,layer_name)
plt.imshow(score_cam)
plt.show()
--model: instance de modèle de keras
--img_array: Données prétraitées de l'image pour laquelle vous souhaitez déterminer la zone de regard. Il doit être sous une forme qui peut exécuter predire
immédiatement, comme model.predict (img_array)
.
--layer_name: Le nom de la couche d'activation immédiatement après la couche de convolution finale. Si la couche d'activation est incluse dans la couche de convolution, le nom de la couche de convolution peut être utilisé. Vous pouvez vérifier le nom du calque avec model.summary ()
.
--max_N: La valeur de réglage pour accélérer que j'ai implémentée sans autorisation. Si «-1», le score-CAM d'origine. La spécification d'un nombre naturel réduit le nombre d'inférences CNN à ce nombre. La valeur recommandée est d'environ 10. Une valeur élevée ne fera qu'augmenter le temps de traitement et n'aura pas beaucoup d'effet sur la carte thermique, mais si elle est trop petite, la carte thermique deviendra étrange.
Affichons la carte thermique obtenue ci-dessus au-dessus de l'image d'origine.
Pour Grad-CAM et Grad-CAM ++, j'ai utilisé le code de gradcam ++ for keras.
Le code d'exécution peut être trouvé sur github.
«L'image affichée comme« accentuée »montre la zone du regard plus clairement en ajoutant un traitement de seuil à la carte thermique. --Score-CAM semble capter la zone du regard de manière uniforme. --Guided Backpropagation est affiché pour le moment, mais comme indiqué dans here, ** ne reflète pas les informations du réseau neuronal. Il y a un doute **. ――Si vous souhaitez extraire les contours que vous regardez, il est toujours préférable d'afficher le dégradé sous forme d'image **, de sorte que l'image du bas est celle qui est superposée en calculant le dégradé sous forme d'image.
Seuls les résultats sont affichés pour les autres images.
Mesurez la vitesse de traitement avec Google cola boratory. Utilisez GPU.
print("Grad-CAM")
%timeit grad_cam = GradCam(model, img_array, layer_name)
print("Grad-CAM++")
%timeit grad_cam_plus_plus = GradCamPlusPlus(model, img_array, layer_name)
print("Score-Cam")
%timeit score_cam = ScoreCam(model, img_array, layer_name)
print("Faster-Score-Cam N=10")
%timeit faster_score_cam = ScoreCam(model, img_array, layer_name, max_N=10)
print("Faster-Score-Cam N=3")
%timeit faster_score_cam = ScoreCam(model, img_array, layer_name, max_N=3)
print("Guided-BP}")
%timeit saliency = GuidedBackPropagation(guided_model, img_array, layer_name)
Grad-CAM
1 loop, best of 3: 196 ms per loop
Grad-CAM++
1 loop, best of 3: 221 ms per loop
Score-Cam
1 loop, best of 3: 5.24 s per loop
Faster-Score-Cam N=10
1 loop, best of 3: 307 ms per loop
Faster-Score-Cam N=3
The slowest run took 4.45 times longer than the fastest. This could mean that an intermediate result is being cached.
1 loop, best of 3: 238 ms per loop
Guided-BP}
1 loop, best of 3: 415 ms per loop
Comme vous pouvez le voir, ** Score-CAM est très lourd **. ** Cela prend plus de 25 fois plus de temps que Grad-CAM **.
Quand j'ai expérimenté la sortie de la couche de convolution finale (512 canaux pour VGG16), j'ai pensé que plusieurs canaux étaient dominants dans la génération de la carte thermique finale. ** De chaque canal, la variable latente Faster-Score-CAM est celui avec le traitement de ** qui utilise préférentiellement celui avec une grande distribution de carte comme image de masque. (Je lui ai donné un nom sans autorisation. Si vous définissez max_N = -1
, ce sera le Score-CAM d'origine)
L'effet est tel que décrit dans ** Comparaison de la vitesse de traitement **, et il est possible d'augmenter la vitesse de ** 10 fois ou plus **. Pourtant, Grad-CAM ++ est plus rapide.
Pour confirmer son caractère pratique, nous appliquerons Score-CAM à notre propre modèle formé avec un ensemble de données ouvert.
Ensemble de données DAGM est utilisé comme ensemble de données, et ResNet (un peu profond avec environ 80 couches) est utilisé comme modèle.
Veuillez réécrire le chemin dagm_path
de l'emplacement où vous avez téléchargé et décompressé DAGM Dataset comme il convient.
from keras.utils import to_categorical
import numpy as np
import glob
from sklearn.model_selection import train_test_split
from gradcamutils import read_and_preprocess_img
num_classes = 2
img_size = (224,224)
dagm_path = "./DAGM"
def get_dagm_data(names):
x = []
y = []
for i, name in enumerate(names):
for path in glob.glob(f"{dagm_path}/{name}/*.png "):
img_array = read_and_preprocess_img(path, size=img_size)
x.append(img_array)
y.append(i)
x = np.concatenate(x, axis=0)
y = np.array(y)
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=111)
y_train = to_categorical(y_train, num_classes)
y_test = to_categorical(y_test, num_classes)
print(x_train.shape[0], 'train samples')
print(x_test.shape[0], 'test samples')
return x_train, x_test, y_train, y_test
x_train, x_test, y_train, y_test = get_dagm_data(["Class1","Class1_def"])
Je le porte sur le côté et utilise celui coupé de ResNet dans les applications de keras. (Ce n'est pas une très bonne écriture, mais ça marche, donc c'est bien)
from keras.applications.resnet50 import ResNet50
from keras.models import Model
from keras.optimizers import Adam
from keras.layers import Dense, Input, Activation, GlobalAveragePooling2D
from keras.callbacks import EarlyStopping, ModelCheckpoint
def build_ResNet():
model = ResNet50(include_top=True, input_tensor=Input(shape=(img_size[0],img_size[1],3)))
x = model.layers[-98].output
x = Activation('relu', name="act_last")(x)
x = GlobalAveragePooling2D()(x)
x = Dense(2, name="dense_out")(x)
outputs = Activation('softmax')(x)
model = Model(model.input, outputs)
# model.summary()
model.compile(loss='binary_crossentropy',
optimizer=Adam(amsgrad=True),
metrics=['accuracy'])
return model
model = build_ResNet()
es_cb = EarlyStopping(monitor='val_loss', patience=5, verbose=1, mode='auto')
chkpt = './resnet_weight_DAGM.h5'
cp_cb = ModelCheckpoint(filepath = chkpt, monitor='val_loss', verbose=1, save_best_only=True, save_weights_only=True, mode='auto')
epochs = 15
batch_size = 32
history = model.fit(x_train, y_train,
batch_size=batch_size,
epochs=epochs,
validation_data=(x_test, y_test),
callbacks=[es_cb,cp_cb],
class_weight={0: 1., 1: 6.},
shuffle=True)
#Poids de charge
model.load_weights('./resnet_weight_DAGM.h5')
import matplotlib.pyplot as plt
import cv2
import numpy as np
from gradcamutils import GradCam, GradCamPlusPlus, ScoreCam, GuidedBackPropagation, superimpose, read_and_preprocess_img
def build_ResNet_and_load():
model = build_ResNet()
model.load_weights('./resnet_weight_DAGM.h5')
return model
img_path = f'{dagm_path}/Class1_def/12.png'
orig_img = np.array(load_img(img_path),dtype=np.uint8)
img_array = read_and_preprocess_img(img_path, size=(224,224))
layer_name = "act_last"
grad_cam=GradCam(model,img_array,layer_name)
grad_cam_superimposed = superimpose(img_path, grad_cam)
grad_cam_emphasized = superimpose(img_path, grad_cam, emphasize=True)
grad_cam_plus_plus=GradCamPlusPlus(model,img_array,layer_name)
grad_cam_plus_plus_superimposed = superimpose(img_path, grad_cam_plus_plus)
grad_cam_plus_plus_emphasized = superimpose(img_path, grad_cam_plus_plus, emphasize=True)
score_cam=ScoreCam(model,img_array,layer_name)
score_cam_superimposed = superimpose(img_path, score_cam)
score_cam_emphasized = superimpose(img_path, score_cam, emphasize=True)
faster_score_cam=ScoreCam(model,img_array,layer_name, max_N=10)
faster_score_cam_superimposed = superimpose(img_path, faster_score_cam)
faster_score_cam_emphasized = superimpose(img_path, faster_score_cam, emphasize=True)
guided_model = build_guided_model(build_ResNet_and_load)
saliency = GuidedBackPropagation(guided_model, img_array, layer_name)
saliency_resized = cv2.resize(saliency, (orig_img.shape[1], orig_img.shape[0]))
grad_cam_resized = cv2.resize(grad_cam, (orig_img.shape[1], orig_img.shape[0]))
guided_grad_cam = saliency_resized * grad_cam_resized[..., np.newaxis]
grad_cam_plus_plus_resized = cv2.resize(grad_cam_plus_plus, (orig_img.shape[1], orig_img.shape[0]))
guided_grad_cam_plus_plus = saliency_resized * grad_cam_plus_plus_resized[..., np.newaxis]
score_cam_resized = cv2.resize(score_cam, (orig_img.shape[1], orig_img.shape[0]))
guided_score_cam = saliency_resized * score_cam_resized[..., np.newaxis]
faster_score_cam_resized = cv2.resize(faster_score_cam, (orig_img.shape[1], orig_img.shape[0]))
guided_faster_score_cam = saliency_resized * faster_score_cam_resized[..., np.newaxis]
img_gray = cv2.imread(img_path, 0)
dx = cv2.Sobel(img_gray, cv2.CV_64F, 1, 0, ksize=3)
dy = cv2.Sobel(img_gray, cv2.CV_64F, 0, 1, ksize=3)
grad = np.sqrt(dx ** 2 + dy ** 2) #Obtenir un dégradé d'image
grad = cv2.dilate(grad,kernel=np.ones((5,5)), iterations=1) #Processus d'engraissement
grad -= np.min(grad)
grad /= np.max(grad) # scale 0. to 1.
grad_times_grad_cam = grad * grad_cam_resized
grad_times_grad_cam_plus_plus = grad * grad_cam_plus_plus_resized
grad_times_score_cam = grad * score_cam_resized
grad_times_faster_score_cam = grad * faster_score_cam_resized
fig, ax = plt.subplots(nrows=4,ncols=5, figsize=(18, 16))
ax[0,0].imshow(orig_img)
ax[0,0].set_title("input image")
ax[0,1].imshow(grad_cam_superimposed)
ax[0,1].set_title("Grad-CAM")
ax[0,2].imshow(grad_cam_plus_plus_superimposed)
ax[0,2].set_title("Grad-CAM++")
ax[0,3].imshow(score_cam_superimposed)
ax[0,3].set_title("Score-CAM")
ax[0,4].imshow(faster_score_cam_superimposed)
ax[0,4].set_title("Faster-Score-CAM")
ax[1,0].imshow(orig_img)
ax[1,0].set_title("input image")
ax[1,1].imshow(grad_cam_emphasized)
ax[1,1].set_title("Grad-CAM emphasized")
ax[1,2].imshow(grad_cam_plus_plus_emphasized)
ax[1,2].set_title("Grad-CAM++ emphasized")
ax[1,3].imshow(score_cam_emphasized)
ax[1,3].set_title("Score-CAM emphasized")
ax[1,4].imshow(faster_score_cam_emphasized)
ax[1,4].set_title("Faster-Score-CAM emphasized")
ax[2,0].imshow(saliency_resized)
ax[2,0].set_title("Guided-BP")
ax[2,1].imshow(guided_grad_cam)
ax[2,1].set_title("Guided-Grad-CAM")
ax[2,2].imshow(guided_grad_cam_plus_plus)
ax[2,2].set_title("Guided-Grad-CAM++")
ax[2,3].imshow(guided_score_cam)
ax[2,3].set_title("Guided-Score-CAM")
ax[2,4].imshow(guided_faster_score_cam)
ax[2,4].set_title("Guided-Faster-Score-CAM")
ax[3,0].imshow(grad, 'gray')
ax[3,0].set_title("grad")
ax[3,1].imshow(grad_times_grad_cam, 'gray')
ax[3,1].set_title("grad * Grad-CAM")
ax[3,2].imshow(grad_times_grad_cam_plus_plus, 'gray')
ax[3,2].set_title("grad * Grad-CAM++")
ax[3,3].imshow(grad_times_score_cam, 'gray')
ax[3,3].set_title("grad * Score-CAM")
ax[3,4].imshow(grad_times_faster_score_cam, 'gray')
ax[3,4].set_title("grad * Faster-Score-CAM")
for i in range(4):
for j in range(5):
ax[i,j].axis('off')
plt.show()
――Il semble que toutes les méthodes permettent de bien détecter la position du scratch. ――Il semble difficile d'afficher quelque chose qui ne souligne que les rayures. (Cela ne peut pas être aidé car les informations de la couche de conversion finale sont supprimées)
Seuls les résultats du traitement des seuils seront affichés pour 5 feuilles dans chaque classe.
Class2
Class3
Class4
Class5
Class6
Il semble que la position de la rayure puisse être représentée presque correctement.
-Dans la ** détection d'anomalies **, il peut être utilisé pour une visualisation approximative des pièces anormales. --Il est très gênant pour Grad-CAM, ++ et Score-CAM de ** limiter les modèles qui peuvent être utilisés **.
Recommended Posts