原理 | 優(yōu)點 | 缺點 | |
GAP | 將多維特征映射降維為一個固定長度的特征向量 | ①減少了模型的參數(shù)量;②保留更多的空間位置信息;③可并行計算,計算效率高;④具有一定程度的不變性 | ①可能導致信息的損失;②忽略不同尺度的空間信息 |
CAM | 利用最后一個卷積層的特征圖×權重(用GAP代替全連接層,重新訓練,經(jīng)過GAP分類后概率最大的神經(jīng)元的權重) | 效果已經(jīng)很不錯 | 需要修改原模型的結構,導致需要重新訓練該模型,大大限制了使用場景,如果模型已經(jīng)上線了,或著訓練的成本非常高,我們幾乎是不可能為了它重新訓練的。 |
Grad-CAM | 最后一個卷積層的特征圖×權重(通過對特征圖梯度的全局平均來計算權重) | ①解決了CAM的缺點,適用于任何卷積神經(jīng)網(wǎng)絡;②利用特征圖的梯度,可視化結果更準確和精細 | |
Grad-CAM++ | 1. 定位更準確 2. 更適合同類多目標的情況 |
||
目錄
GAP全局平均池化
CAM
Grad-CAM?
Grad-CAM++
GAP全局平均池化
論文:Network In Network
GAP (Global Average Pooling,全局平均池化),在上述論文中提出,用于避免全連接層的過擬合問題。全局平均池化就是對整個特征映射應用平均池化。
圖1:將原本h × w × d的三維特征圖,具體大小為6 × 6 × 3,經(jīng)過GAP池化為1 × 1 × 3 輸出值。也就是每一個channel的h × w 平均池化為一個值。特征圖經(jīng)過 GAP 處理后每一個特征圖包含了不同類別的信息。?
GAP平均池化的操作步驟如下:
- 經(jīng)過卷積操作和激活函數(shù)后,得到最后一個卷積層的特征圖。
- 對每個通道的特征圖進行平均池化,即計算每個通道上所有元素的平均值。這將每個通道的特征圖轉化為一個標量值。
- 將每個通道的標量值組合成一個特征向量。這些標量值的順序與通道的順序相同。
- 最終得到的特征向量可以作為分類器的輸入,用于進行圖像分類。
CAM
論文:Learning Deep Features for Discriminative Localization
原理:利用最后一個卷積層的特征圖與經(jīng)過GAP分類后概率最大的神經(jīng)元權重進行疊加。
圖2:解釋了在CNN中使用全局平均池化(GAP)生成類激活映射(CAM)的過程:
經(jīng)過最后一層卷積操作之后,得到的特征圖包含多個channel,如圖1中的不同顏色的3個channel,也就是在GAP之前所對應的不同的channel特征圖,就表示第k個channel的特征圖。然后經(jīng)過GAP處理后每個channel的特征圖包含了不同類別的信息,就表示分類概率最大的神經(jīng)元(圖2黑色神經(jīng)元)所對應連接的第k個神經(jīng)元的權重。
Grad-CAM?
Grad-CAM: Visual Explanations from Deep Networks via Gradient-based Localization (arxiv.org)
Grad-CAM的前身是?CAM,CAM 的基本的思想是求分類網(wǎng)絡某一類別得分對高維特征圖 (卷積層的輸出) 的偏導數(shù),從而可以得到該高維特征圖每個通道對該類別得分的權值;而高維特征圖的激活信息 (正值) 又代表了卷積神經(jīng)網(wǎng)絡的所感興趣的信息,加權后使用熱力圖呈現(xiàn)得到 CAM。
原理:Grad-CAM的關鍵思想是將輸出類別的梯度(相對于特定卷積層的輸出)與該層的輸出相乘,然后取平均,得到一個“粗糙”的熱力圖。這個熱力圖可以被放大并疊加到原始圖像上,以顯示模型在分類時最關注的區(qū)域。
具體步驟如下:
- 選擇網(wǎng)絡的最后一個卷積層,因為它既包含了高級特征,也保留了空間信息。
- 前向傳播圖像到網(wǎng)絡,得到你想解釋的類別的得分。
- 計算此得分相對于我們選擇的卷積層輸出的梯度。
- 對于該卷積層的每個通道,使用上述梯度的全局平均值對該通道進行加權。
- 結果是一個與卷積層的空間維度相同的加權熱力圖。
因為熱力圖關心的是對分類有正面影響的特征,所以在線性組合的技術上加上了ReLU,以移除負值?。
第 k 個特征圖對應于類別 c 的權重,
表示:第?k 個特征圖,
表示特征圖的像素個數(shù),
表示: 第c類得分的梯度,
表示: 第 k個特征圖中坐標( i , j )位置處的像素值;
Grad-CAM代碼:
import torch
import cv2
import torch.nn.functional as F
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
from PIL import Image
class GradCAM:
def __init__(self, model, target_layer):
self.model = model # 要進行Grad-CAM處理的模型
self.target_layer = target_layer # 要進行特征可視化的目標層
self.feature_maps = None # 存儲特征圖
self.gradients = None # 存儲梯度
# 為目標層添加鉤子,以保存輸出和梯度
target_layer.register_forward_hook(self.save_feature_maps)
target_layer.register_backward_hook(self.save_gradients)
def save_feature_maps(self, module, input, output):
"""保存特征圖"""
self.feature_maps = output.detach()
def save_gradients(self, module, grad_input, grad_output):
"""保存梯度"""
self.gradients = grad_output[0].detach()
def generate_cam(self, image, class_idx=None):
"""生成CAM熱力圖"""
# 將模型設置為評估模式
self.model.eval()
# 正向傳播
output = self.model(image)
if class_idx is None:
class_idx = torch.argmax(output).item()
# 清空所有梯度
self.model.zero_grad()
# 對目標類進行反向傳播
one_hot = torch.zeros((1, output.size()[-1]), dtype=torch.float32)
one_hot[0][class_idx] = 1
output.backward(gradient=one_hot.cuda(), retain_graph=True)
# 獲取平均梯度和特征圖
pooled_gradients = torch.mean(self.gradients, dim=[0, 2, 3])
activation = self.feature_maps.squeeze(0)
for i in range(activation.size(0)):
activation[i, :, :] *= pooled_gradients[i]
# 創(chuàng)建熱力圖
heatmap = torch.mean(activation, dim=0).squeeze().cpu().numpy()
heatmap = np.maximum(heatmap, 0)
heatmap /= torch.max(heatmap)
heatmap = cv2.resize(heatmap, (image.size(3), image.size(2)))
heatmap = np.uint8(255 * heatmap)
heatmap = cv2.applyColorMap(heatmap, cv2.COLORMAP_JET)
# 將熱力圖疊加到原始圖像上
original_image = self.unprocess_image(image.squeeze().cpu().numpy())
superimposed_img = heatmap * 0.4 + original_image
superimposed_img = np.clip(superimposed_img, 0, 255).astype(np.uint8)
return heatmap, superimposed_img
def unprocess_image(self, image):
"""反預處理圖像,將其轉回原始圖像"""
mean = np.array([0.485, 0.456, 0.406])
std = np.array([0.229, 0.224, 0.225])
image = (((image.transpose(1, 2, 0) * std) + mean) * 255).astype(np.uint8)
return image
def visualize_gradcam(model, input_image_path, target_layer):
"""可視化Grad-CAM熱力圖"""
# 加載圖像
img = Image.open(input_image_path)
preprocess = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])
input_tensor = preprocess(img).unsqueeze(0).cuda()
# 創(chuàng)建GradCAM
gradcam = GradCAM(model, target_layer)
heatmap, result = gradcam.generate_cam(input_tensor)
# 顯示圖像和熱力圖
plt.figure(figsize=(10,10))
plt.subplot(1,2,1)
plt.imshow(heatmap)
plt.title('熱力圖')
plt.axis('off')
plt.subplot(1,2,2)
plt.imshow(result)
plt.title('疊加后的圖像')
plt.axis('off')
plt.show()
# 以下是示例代碼,顯示如何使用上述代碼。
# 首先,你需要加載你的模型和權重。
# model = resnet20()
# model.load_state_dict(torch.load("path_to_your_weights.pth"))
# model.to('cuda')
# 然后,調用`visualize_gradcam`函數(shù)來查看結果。
# visualize_gradcam(model, "path_to_your_input_image.jpg", model.layer3[-1])
?Grad-CAM++
Grad-CAM++: Improved Visual Explanations for Deep Convolutional Networks (arxiv.org)?文章來源:http://www.zghlxwxcb.cn/news/detail-742930.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-742930.html
到了這里,關于《python深度學習》筆記(二十):神經(jīng)網(wǎng)絡的解釋方法之CAM、Grad-CAM、Grad-CAM++、LayerCAM的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!