美國(guó)十大購(gòu)物網(wǎng)站免費(fèi)注冊(cè)個(gè)人網(wǎng)站不花錢
基于WIN10的64位系統(tǒng)演示
一、寫在前面
類激活映射(Class Activation Mapping,CAM)和梯度權(quán)重類激活映射(Gradient-weighted Class Activation Mapping,Grad-CAM)是兩種可視化深度學(xué)習(xí)模型決策過程的技術(shù)。他們都是為了理解模型的決策過程,特別是對(duì)于圖像分類任務(wù),它們可以生成一種熱力圖,這種圖可以突出顯示模型在做出預(yù)測(cè)時(shí)關(guān)注的圖像區(qū)域。
CAM:CAM是一種可視化卷積神經(jīng)網(wǎng)絡(luò)(Convolutional Neural Networks, CNN)決策依據(jù)的技術(shù)。對(duì)于圖像分類任務(wù),它可以生成一種熱力圖,突出顯示模型在做出預(yù)測(cè)時(shí)關(guān)注的圖像區(qū)域。CAM需要模型在全局平均池化(Global Average Pooling, GAP)層和最終的全連接層(Fully Connected, FC)之間沒有其他隱藏層,這是其使用的限制。
Grad-CAM:Grad-CAM是為了克服CAM的限制而提出的一種方法,它使用的是類別得分關(guān)于特定層輸出的梯度信息。這種方法不僅可以應(yīng)用于卷積層,還可以應(yīng)用于任何層的輸出。因此,Grad-CAM可以用于多種類型的深度學(xué)習(xí)模型,包括圖像分類、圖像生成、強(qiáng)化學(xué)習(xí)等各種模型。這使得Grad-CAM在可視化模型決策過程方面更加靈活和強(qiáng)大。
這一期主要介紹Grad-CAM,用的模型是Mobilenet_v2,以為夠快!!
二、Grad-CAM可視化實(shí)戰(zhàn)
繼續(xù)使用胸片的數(shù)據(jù)集:肺結(jié)核病人和健康人的胸片的識(shí)別。其中,肺結(jié)核病人700張,健康人900張,分別存入單獨(dú)的文件夾中。
(a)Mobilenet_v2建模
######################################導(dǎo)入包###################################
from tensorflow import keras
import tensorflow as tf
from tensorflow.python.keras.layers import Dense, Flatten, Conv2D, MaxPool2D, Dropout, Activation, Reshape, Softmax, GlobalAveragePooling2D, BatchNormalization
from tensorflow.python.keras.layers.convolutional import Convolution2D, MaxPooling2D
from tensorflow.python.keras import Sequential
from tensorflow.python.keras import Model
from tensorflow.python.keras.optimizers import adam_v2
import numpy as np
import matplotlib.pyplot as plt
from tensorflow.python.keras.preprocessing.image import ImageDataGenerator, image_dataset_from_directory
from tensorflow.python.keras.layers.preprocessing.image_preprocessing import RandomFlip, RandomRotation, RandomContrast, RandomZoom, RandomTranslation
import os,PIL,pathlib
import warnings
#設(shè)置GPU
gpus = tf.config.list_physical_devices("GPU")if gpus:gpu0 = gpus[0] #如果有多個(gè)GPU,僅使用第0個(gè)GPUtf.config.experimental.set_memory_growth(gpu0, True) #設(shè)置GPU顯存用量按需使用tf.config.set_visible_devices([gpu0],"GPU")warnings.filterwarnings("ignore") #忽略警告信息
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用來正常顯示中文標(biāo)簽
plt.rcParams['axes.unicode_minus'] = False # 用來正常顯示負(fù)號(hào)################################導(dǎo)入數(shù)據(jù)集#####################################
#1.導(dǎo)入數(shù)據(jù)
data_dir = "./MTB"
data_dir = pathlib.Path(data_dir)
image_count = len(list(data_dir.glob('*/*')))
print("圖片總數(shù)為:",image_count)batch_size = 32
img_height = 100
img_width = 100train_ds = image_dataset_from_directory(data_dir,validation_split=0.2,subset="training",seed=12,image_size=(img_height, img_width),batch_size=batch_size)val_ds = image_dataset_from_directory(data_dir,validation_split=0.2,subset="validation",seed=12,image_size=(img_height, img_width),batch_size=batch_size)class_names = train_ds.class_names
print(class_names)
print(train_ds)#2.檢查數(shù)據(jù)
for image_batch, labels_batch in train_ds:print(image_batch.shape)print(labels_batch.shape)break#3.配置數(shù)據(jù)
AUTOTUNE = tf.data.AUTOTUNEdef train_preprocessing(image,label):return (image/255.0,label)train_ds = (train_ds.cache().shuffle(800).map(train_preprocessing) .prefetch(buffer_size=AUTOTUNE)
)val_ds = (val_ds.cache().map(train_preprocessing) .prefetch(buffer_size=AUTOTUNE)
)#4. 數(shù)據(jù)可視化
plt.figure(figsize=(10, 8)) # 圖形的寬為10高為5
plt.suptitle("數(shù)據(jù)展示")class_names = ["Tuberculosis","Normal"]for images, labels in train_ds.take(1):for i in range(15):plt.subplot(4, 5, i + 1)plt.xticks([])plt.yticks([])plt.grid(False)# 顯示圖片plt.imshow(images[i])# 顯示標(biāo)簽plt.xlabel(class_names[labels[i]-1])plt.show()######################################數(shù)據(jù)增強(qiáng)函數(shù)################################data_augmentation = Sequential([RandomFlip("horizontal_and_vertical"),RandomRotation(0.2),RandomContrast(1.0),RandomZoom(0.5,0.2),RandomTranslation(0.3,0.5),
])def prepare(ds):ds = ds.map(lambda x, y: (data_augmentation(x, training=True), y), num_parallel_calls=AUTOTUNE)return ds
train_ds = prepare(train_ds)################################導(dǎo)入mobilenet_v2################################
#獲取預(yù)訓(xùn)練模型對(duì)輸入的預(yù)處理方法
from tensorflow.python.keras.applications import mobilenet_v2
from tensorflow.python.keras import Input, regularizers
IMG_SIZE = (img_height, img_width, 3)# 創(chuàng)建輸入張量
inputs = Input(shape=IMG_SIZE)
# 定義基礎(chǔ)模型,并將 inputs 傳入
base_model = mobilenet_v2.MobileNetV2(input_tensor=inputs,include_top=False, weights='imagenet')#從基礎(chǔ)模型中獲取輸出
x = base_model.output
#全局池化
x = GlobalAveragePooling2D()(x)
#BatchNormalization
x = BatchNormalization()(x)
#Dropout
x = Dropout(0.8)(x)
#Dense
x = Dense(128, kernel_regularizer=regularizers.l2(0.1))(x) # 全連接層減少到128,添加 L2 正則化
#BatchNormalization
x = BatchNormalization()(x)
#激活函數(shù)
x = Activation('relu')(x)
#輸出層
outputs = Dense(2, kernel_regularizer=regularizers.l2(0.1))(x) # 添加 L2 正則化
#BatchNormalization
outputs = BatchNormalization()(outputs)
#激活函數(shù)
outputs = Activation('sigmoid')(outputs)
#整體封裝
model = Model(inputs, outputs)
#打印模型結(jié)構(gòu)
print(model.summary())#############################編譯模型#########################################
#定義優(yōu)化器
from tensorflow.python.keras.optimizers import adam_v2, rmsprop_v2
optimizer = adam_v2.Adam()#編譯模型
model.compile(optimizer=optimizer,loss='sparse_categorical_crossentropy',metrics=['accuracy'])#訓(xùn)練模型
from tensorflow.python.keras.callbacks import ModelCheckpoint, Callback, EarlyStopping, ReduceLROnPlateau, LearningRateSchedulerNO_EPOCHS = 50
PATIENCE = 10
VERBOSE = 1# 設(shè)置動(dòng)態(tài)學(xué)習(xí)率
annealer = LearningRateScheduler(lambda x: 1e-5 * 0.99 ** (x+NO_EPOCHS))# 設(shè)置早停
earlystopper = EarlyStopping(monitor='loss', patience=PATIENCE, verbose=VERBOSE)#
checkpointer = ModelCheckpoint('mtb_jet_best_model_mobilenetv3samll.h5',monitor='val_accuracy',verbose=VERBOSE,save_best_only=True,save_weights_only=True)train_model = model.fit(train_ds,epochs=NO_EPOCHS,verbose=1,validation_data=val_ds,callbacks=[earlystopper, checkpointer, annealer])#保存模型
model.save('mtb_jet_best_model_mobilenet.h5')
print("The trained model has been saved.")
(b)Grad-CAM
import numpy as np
from PIL import Image, ImageOps
from tensorflow.python.keras.preprocessing import image
from tensorflow.python.keras.applications.mobilenet_v2 import preprocess_input
from tensorflow.python.keras.models import load_model
import tensorflow as tf
from tensorflow.python.keras import Model
import matplotlib.pyplot as plt# 你的模型路徑
model_path = 'mtb_jet_best_model_mobilenet.h5'# 你的圖像路徑
image_path = './MTB/Tuberculosis/Tuberculosis-666.png'# 加載你的模型
model = load_model(model_path)def grad_cam(img_path, cls, model, layer_name='block_7_project'):# 加載圖像并預(yù)處理img = image.load_img(img_path, target_size=(100, 100))x = image.img_to_array(img)x = np.expand_dims(x, axis=0)x = preprocess_input(x)# 獲取預(yù)測(cè)類別preds = model.predict(x)pred_class = np.argmax(preds[0])# 使用 GradientTape 計(jì)算 Grad-CAMwith tf.GradientTape() as tape:last_conv_layer = model.get_layer(layer_name)iterate = Model([model.inputs], [model.output, last_conv_layer.output])model_out, last_conv_layer = iterate(x)class_out = model_out[:, pred_class]# 得到的梯度grads = tape.gradient(class_out, last_conv_layer)pooled_grads = tf.reduce_mean(grads, axis=(0, 1, 2))# 我們把梯度在每個(gè)特征圖上進(jìn)行平均heatmap = tf.reduce_mean(tf.multiply(pooled_grads, last_conv_layer), axis=-1)# 調(diào)整 heatmap 的形狀和數(shù)值范圍heatmap = tf.squeeze(heatmap) # 去掉尺寸為1的維度heatmap = np.maximum(heatmap, 0) # 去掉小于0的值max_heat = np.max(heatmap)if max_heat == 0:max_heat = 1e-10 # 防止除以0heatmap /= max_heat # 歸一化到0-1之間heatmap = np.uint8(255 * heatmap) # 轉(zhuǎn)換到0-255之間并轉(zhuǎn)為uint8類型# 加載原始圖像img = Image.open(img_path)# 將熱力圖轉(zhuǎn)換為 PIL 圖像并調(diào)整其尺寸heatmap = Image.fromarray(heatmap)heatmap = heatmap.resize((img.height, img.width))# 將單通道熱力圖轉(zhuǎn)換為彩色(RGB)圖像heatmap = ImageOps.colorize(heatmap, 'blue', 'red')# 將彩色熱力圖轉(zhuǎn)換為帶透明度的(RGBA)圖像heatmap = heatmap.convert('RGBA')heatmap_with_alpha = Image.new('RGBA', heatmap.size)for x in range(heatmap.width):for y in range(heatmap.height):r, g, b, a = heatmap.getpixel((x, y))heatmap_with_alpha.putpixel((x, y), (r, g, b, int(a * 0.5)))# 將原始圖像轉(zhuǎn)換為 RGBA 圖像img = img.convert('RGBA')# 疊加圖像overlay = Image.alpha_composite(img, heatmap_with_alpha)# 將疊加后的圖像轉(zhuǎn)換為numpy數(shù)組overlay = np.array(overlay)# 使用matplotlib顯示圖像plt.imshow(overlay)plt.axis('off') # 不顯示坐標(biāo)軸plt.show()print(pred_class)# 繪制熱力圖
grad_cam(image_path, 0, model)
這個(gè)代碼需要調(diào)整的參數(shù)就只有“l(fā)ayer_name”,也就是使用哪一層的信息來可視化。當(dāng)然,首先我們得先知道每一層的名稱:
#查看 Keras 模型每一層的名稱
for layer in model.layers:print(layer.name)
輸出如下:
然后,用哪一層呢?
其實(shí)吧,選擇哪一層用于Grad-CAM的計(jì)算并沒有一條明確的規(guī)則,這完全取決于你的模型結(jié)構(gòu)以及你的具體需求。
一般來說,Convolutional Neural Networks(CNN,卷積神經(jīng)網(wǎng)絡(luò))的前面幾層往往捕捉到的是圖像的低級(jí)特征,比如邊緣、色彩和紋理等,而后面的層則可以捕捉到更為高級(jí)的特征,比如物體的部分或者整體。所以,如果你想要看到模型在判斷圖像時(shí),主要關(guān)注了圖像中的哪些部分或者物體,你可能需要選擇離輸出層更近一些的卷積層。
但是這也不是絕對(duì)的。在實(shí)際應(yīng)用中,你可能需要嘗試不同的層,看看哪一層生成的Grad-CAM熱力圖最能滿足你的需求。
比如我試了試:'block_1_project':
?'block_7_project':
?'block_10_project':
?'block_2_add':
?綜上,似乎一切隨緣,太抽象了!!!
三、寫在最后
略~
四、數(shù)據(jù)
鏈接:https://pan.baidu.com/s/15vSVhz1rQBtqNkNp2GQyVw?pwd=x3jf
提取碼:x3jf