基于WIN10的64位系統(tǒng)演示
一、寫在前面
類激活映射(Class Activation Mapping,CAM)和梯度權(quán)重類激活映射(Gradient-weighted Class Activation Mapping,Grad-CAM)是兩種可視化深度學(xué)習(xí)模型決策過程的技術(shù)。他們都是為了理解模型的決策過程,特別是對于圖像分類任務(wù),它們可以生成一種熱力圖,這種圖可以突出顯示模型在做出預(yù)測時關(guān)注的圖像區(qū)域。
CAM:CAM是一種可視化卷積神經(jīng)網(wǎng)絡(luò)(Convolutional Neural Networks, CNN)決策依據(jù)的技術(shù)。對于圖像分類任務(wù),它可以生成一種熱力圖,突出顯示模型在做出預(yù)測時關(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í)模型,包括圖像分類、圖像生成、強化學(xué)習(xí)等各種模型。這使得Grad-CAM在可視化模型決策過程方面更加靈活和強大。
這一期主要介紹Grad-CAM,用的模型是Mobilenet_v2,以為夠快?。?/span>
二、Grad-CAM可視化實戰(zhàn)
繼續(xù)使用胸片的數(shù)據(jù)集:肺結(jié)核病人和健康人的胸片的識別。其中,肺結(jié)核病人700張,健康人900張,分別存入單獨的文件夾中。
(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] #如果有多個GPU,僅使用第0個GPU
tf.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'] # 用來正常顯示中文標簽
plt.rcParams['axes.unicode_minus'] = False # 用來正常顯示負號
################################導(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 = 100
train_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.AUTOTUNE
def 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])
# 顯示標簽
plt.xlabel(class_names[labels[i]-1])
plt.show()
######################################數(shù)據(jù)增強函數(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)練模型對輸入的預(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, LearningRateScheduler
NO_EPOCHS = 50
PATIENCE = 10
VERBOSE = 1
# 設(shè)置動態(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ù)測類別
preds = model.predict(x)
pred_class = np.argmax(preds[0])
# 使用 GradientTape 計算 Grad-CAM
with 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))
# 我們把梯度在每個特征圖上進行平均
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 # 防止除以0
heatmap /= 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') # 不顯示坐標軸
plt.show()
print(pred_class)
# 繪制熱力圖
grad_cam(image_path, 0, model)
這個代碼需要調(diào)整的參數(shù)就只有“l(fā)ayer_name”,也就是使用哪一層的信息來可視化。當然,首先我們得先知道每一層的名稱:
#查看 Keras 模型每一層的名稱
for layer in model.layers:
print(layer.name)
輸出如下:
然后,用哪一層呢?
其實吧,選擇哪一層用于Grad-CAM的計算并沒有一條明確的規(guī)則,這完全取決于你的模型結(jié)構(gòu)以及你的具體需求。
一般來說,Convolutional Neural Networks(CNN,卷積神經(jīng)網(wǎng)絡(luò))的前面幾層往往捕捉到的是圖像的低級特征,比如邊緣、色彩和紋理等,而后面的層則可以捕捉到更為高級的特征,比如物體的部分或者整體。所以,如果你想要看到模型在判斷圖像時,主要關(guān)注了圖像中的哪些部分或者物體,你可能需要選擇離輸出層更近一些的卷積層。
但是這也不是絕對的。在實際應(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 文章來源:http://www.zghlxwxcb.cn/news/detail-637633.html
提取碼:x3jf 文章來源地址http://www.zghlxwxcb.cn/news/detail-637633.html
到了這里,關(guān)于第56步 深度學(xué)習(xí)圖像識別:CNN梯度權(quán)重類激活映射(TensorFlow)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!