注意
點選此處下載完整示例程式碼
介紹 || 張量 || Autograd || 構建模型 || TensorBoard 支援 || 訓練模型 || 理解模型
使用 Captum 理解模型¶
創建於:2021 年 11 月 30 日 | 最後更新:2024 年 1 月 19 日 | 最後驗證:2024 年 11 月 05 日
可觀看下方影片或在 YouTube 上觀看。在此下載 Notebook 和相應檔案。
Captum (在拉丁語中意為“理解”) 是一個構建於 PyTorch 之上的開源、可擴充套件的模型可解釋性庫。
隨著模型複雜性的增加以及由此導致的透明度不足,模型可解釋性方法變得越來越重要。模型理解既是一個活躍的研究領域,也是機器學習在各行各業實際應用的重點領域。Captum 提供了最先進的演算法,包括整合梯度 (Integrated Gradients),為研究人員和開發者提供一種簡單的方式來理解哪些特徵對模型的輸出有所貢獻。
完整的文件、API 參考和針對特定主題的一系列教程可在 captum.ai 網站上找到。
引言¶
Captum 對模型可解釋性的方法基於歸因。Captum 中提供了三種類型的歸因
特徵歸因 (Feature Attribution) 旨在根據生成特定輸出的輸入特徵來解釋該輸出。解釋一篇影評是積極還是消極,透過影評中的某些詞語來理解,就是特徵歸因的一個例子。
層歸因 (Layer Attribution) 檢查模型隱藏層在接收特定輸入後的活動情況。檢查卷積層對輸入影像的空間對映輸出就是一個層歸因的例子。
神經元歸因 (Neuron Attribution) 類似於層歸因,但側重於單個神經元的活動。
在這個互動式 Notebook 中,我們將研究特徵歸因和層歸因。
這三種歸因型別都關聯了多種歸因演算法。許多歸因演算法分為兩大類
基於梯度的演算法 (Gradient-based algorithms) 計算模型輸出、層輸出或神經元啟用相對於輸入的反向梯度。整合梯度 (Integrated Gradients) (用於特徵)、層梯度 * 啟用 (Layer Gradient * Activation) 和神經元傳導 (Neuron Conductance) 都屬於基於梯度的演算法。
基於擾動的演算法 (Perturbation-based algorithms) 檢查模型、層或神經元輸出對輸入變化的響應。輸入擾動可以是定向的或隨機的。遮擋 (Occlusion)、特徵消融 (Feature Ablation) 和特徵置換 (Feature Permutation) 都屬於基於擾動的演算法。
我們將在下面研究這兩種型別的演算法。
特別是在涉及大型模型時,以易於與被檢查的輸入特徵相關聯的方式視覺化歸因資料可能非常有價值。雖然當然可以使用 Matplotlib、Plotly 或類似工具建立自己的視覺化,但 Captum 提供了針對其歸因的增強工具
captum.attr.visualization模組(下面匯入為viz)提供了用於視覺化影像相關歸因的有用函式。Captum Insights 是構建在 Captum 之上的易於使用的 API,提供了一個視覺化小部件,其中包含適用於影像、文字和任意模型型別的現成視覺化。
這兩種視覺化工具集都將在本 Notebook 中進行演示。前幾個示例將側重於計算機視覺用例,但最後的 Captum Insights 部分將演示多模型、視覺問答模型中的歸因視覺化。
安裝¶
在開始之前,您需要具備一個 Python 環境,其中包含
Python 版本 3.6 或更高
對於 Captum Insights 示例,Flask 1.1 或更高以及 Flask-Compress(推薦最新版本)
PyTorch 版本 1.2 或更高(推薦最新版本)
TorchVision 版本 0.6 或更高(推薦最新版本)
Captum(推薦最新版本)
Matplotlib 版本 3.3.4,因為 Captum 當前使用 Matplotlib 的一個函式,該函式在更高版本中更改了引數名稱
要在 Anaconda 或 pip 虛擬環境中安裝 Captum,請使用適合您環境的相應命令
使用 conda
conda install pytorch torchvision captum flask-compress matplotlib=3.3.4 -c pytorch
使用 pip
pip install torch torchvision captum matplotlib==3.3.4 Flask-Compress
在您設定好的環境中重啟此 Notebook,即可開始!
第一個示例¶
首先,讓我們看一個簡單的視覺示例。我們將從在 ImageNet 資料集上預訓練的 ResNet 模型開始。我們將獲取一個測試輸入,並使用不同的特徵歸因演算法來檢查輸入影像如何影響輸出,並檢視一些測試影像的輸入歸因圖的有用視覺化。
首先,一些匯入
import torch
import torch.nn.functional as F
import torchvision.transforms as transforms
import torchvision.models as models
import captum
from captum.attr import IntegratedGradients, Occlusion, LayerGradCam, LayerAttribution
from captum.attr import visualization as viz
import os, sys
import json
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
現在我們將使用 TorchVision 模型庫下載一個預訓練的 ResNet 模型。由於我們不進行訓練,暫時將其設定為評估模式。
model = models.resnet18(weights='IMAGENET1K_V1')
model = model.eval()
獲取此互動式 Notebook 的位置應該還有一個 img 資料夾,其中包含一個 cat.jpg 檔案。
test_img = Image.open('img/cat.jpg')
test_img_data = np.asarray(test_img)
plt.imshow(test_img_data)
plt.show()
我們的 ResNet 模型在 ImageNet 資料集上訓練,並期望影像具有特定尺寸,通道資料標準化到特定值範圍。我們還將拉入模型識別的類別的可讀標籤列表 - 這也應該在 img 資料夾中。
# model expects 224x224 3-color image
transform = transforms.Compose([
transforms.Resize(224),
transforms.CenterCrop(224),
transforms.ToTensor()
])
# standard ImageNet normalization
transform_normalize = transforms.Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]
)
transformed_img = transform(test_img)
input_img = transform_normalize(transformed_img)
input_img = input_img.unsqueeze(0) # the model requires a dummy batch dimension
labels_path = 'img/imagenet_class_index.json'
with open(labels_path) as json_data:
idx_to_labels = json.load(json_data)
現在,我們可以問一個問題:我們的模型認為這張影像代表什麼?
output = model(input_img)
output = F.softmax(output, dim=1)
prediction_score, pred_label_idx = torch.topk(output, 1)
pred_label_idx.squeeze_()
predicted_label = idx_to_labels[str(pred_label_idx.item())][1]
print('Predicted:', predicted_label, '(', prediction_score.squeeze().item(), ')')
我們已經確認 ResNet 認為我們的貓的影像實際上就是一隻貓。但模型為什麼認為這是一隻貓的影像呢?
要回答這個問題,我們轉向 Captum。
使用整合梯度進行特徵歸因¶
特徵歸因 (Feature attribution) 將特定輸出歸因於輸入的特徵。它使用特定的輸入(此處為我們的測試影像)生成一個圖,顯示每個輸入特徵對特定輸出特徵的相對重要性。
整合梯度 (Integrated Gradients) 是 Captum 中可用的特徵歸因演算法之一。整合梯度透過近似模型輸出相對於輸入的梯度的積分,為每個輸入特徵分配一個重要性分數。
在我們的例子中,我們將選取輸出向量的一個特定元素(即表示模型對其所選類別的置信度的那個元素),並使用整合梯度來理解輸入影像的哪些部分對這個輸出有所貢獻。
一旦獲得整合梯度生成的重要性圖,我們將使用 Captum 中的視覺化工具來提供重要性圖的有用表示。Captum 的 visualize_image_attr() 函式提供了多種選項來自定義歸因資料的顯示。此處,我們傳入一個自定義的 Matplotlib 顏色對映。
執行包含 integrated_gradients.attribute() 呼叫的單元格通常需要一兩分鐘。
# Initialize the attribution algorithm with the model
integrated_gradients = IntegratedGradients(model)
# Ask the algorithm to attribute our output target to
attributions_ig = integrated_gradients.attribute(input_img, target=pred_label_idx, n_steps=200)
# Show the original image for comparison
_ = viz.visualize_image_attr(None, np.transpose(transformed_img.squeeze().cpu().detach().numpy(), (1,2,0)),
method="original_image", title="Original Image")
default_cmap = LinearSegmentedColormap.from_list('custom blue',
[(0, '#ffffff'),
(0.25, '#0000ff'),
(1, '#0000ff')], N=256)
_ = viz.visualize_image_attr(np.transpose(attributions_ig.squeeze().cpu().detach().numpy(), (1,2,0)),
np.transpose(transformed_img.squeeze().cpu().detach().numpy(), (1,2,0)),
method='heat_map',
cmap=default_cmap,
show_colorbar=True,
sign='positive',
title='Integrated Gradients')
在上面的影像中,您應該看到整合梯度在影像中貓的位置周圍給出了最強的訊號。
使用遮擋進行特徵歸因¶
基於梯度的歸因方法透過直接計算輸出相對於輸入的改變來幫助理解模型。基於擾動的歸因方法則更直接地透過引入輸入的變化來衡量對輸出的影響。遮擋 (Occlusion) 就是其中一種方法。它涉及替換輸入影像的某些區域,並檢查對輸出訊號的影響。
下面,我們設定遮擋歸因。與配置卷積神經網路類似,您可以指定目標區域的大小和步長來確定單個測量之間的間距。我們將使用 visualize_image_attr_multiple() 視覺化遮擋歸因的輸出,顯示按區域劃分的正負歸因熱圖,並透過使用正歸因區域掩蓋原始影像。掩蓋影像可以非常直觀地展示模型認為貓照片中哪些區域最像“貓”。
occlusion = Occlusion(model)
attributions_occ = occlusion.attribute(input_img,
target=pred_label_idx,
strides=(3, 8, 8),
sliding_window_shapes=(3,15, 15),
baselines=0)
_ = viz.visualize_image_attr_multiple(np.transpose(attributions_occ.squeeze().cpu().detach().numpy(), (1,2,0)),
np.transpose(transformed_img.squeeze().cpu().detach().numpy(), (1,2,0)),
["original_image", "heat_map", "heat_map", "masked_image"],
["all", "positive", "negative", "positive"],
show_colorbar=True,
titles=["Original", "Positive Attribution", "Negative Attribution", "Masked"],
fig_size=(18, 6)
)
同樣,我們看到包含貓的影像區域被賦予了更大的重要性。
使用 Layer GradCAM 進行層歸因¶
層歸因 (Layer Attribution) 允許您將模型中隱藏層的活動歸因於您的輸入特徵。下面,我們將使用層歸因演算法來檢查模型中一個卷積層的活動。
GradCAM 計算目標輸出相對於給定層的梯度,對每個輸出通道(輸出的維度 2)取平均,然後將每個通道的平均梯度乘以層啟用。結果在所有通道上求和。GradCAM 專為卷積網路設計;由於卷積層的活動通常在空間上對映到輸入,因此 GradCAM 歸因通常會被上取樣並用於掩蓋輸入。
層歸因的設定類似於輸入歸因,不同之處在於除了模型之外,您還必須指定要檢查的模型中的隱藏層。如上所述,當我們呼叫 attribute() 時,我們指定感興趣的目標類別。
layer_gradcam = LayerGradCam(model, model.layer3[1].conv2)
attributions_lgc = layer_gradcam.attribute(input_img, target=pred_label_idx)
_ = viz.visualize_image_attr(attributions_lgc[0].cpu().permute(1,2,0).detach().numpy(),
sign="all",
title="Layer 3 Block 1 Conv 2")
我們將使用 LayerAttribution 基類中的便利方法 interpolate() 對此歸因資料進行上取樣,以便與輸入影像進行比較。
upsamp_attr_lgc = LayerAttribution.interpolate(attributions_lgc, input_img.shape[2:])
print(attributions_lgc.shape)
print(upsamp_attr_lgc.shape)
print(input_img.shape)
_ = viz.visualize_image_attr_multiple(upsamp_attr_lgc[0].cpu().permute(1,2,0).detach().numpy(),
transformed_img.permute(1,2,0).numpy(),
["original_image","blended_heat_map","masked_image"],
["all","positive","positive"],
show_colorbar=True,
titles=["Original", "Positive Attribution", "Masked"],
fig_size=(18, 6))
像這樣的視覺化可以為您提供關於隱藏層如何響應輸入的新穎見解。
使用 Captum Insights 進行視覺化¶
Captum Insights 是一個構建在 Captum 之上的可解釋性視覺化小部件,旨在促進模型理解。Captum Insights 適用於影像、文字和其他特徵,幫助使用者理解特徵歸因。它允許您視覺化多個輸入/輸出對的歸因,併為影像、文字和任意資料提供視覺化工具。
在本 Notebook 的這一部分,我們將使用 Captum Insights 視覺化多個影像分類推理結果。
首先,讓我們收集一些影像,看看模型對它們的看法。為了增加多樣性,我們將選取我們的貓、一個茶壺和一個三葉蟲化石
imgs = ['img/cat.jpg', 'img/teapot.jpg', 'img/trilobite.jpg']
for img in imgs:
img = Image.open(img)
transformed_img = transform(img)
input_img = transform_normalize(transformed_img)
input_img = input_img.unsqueeze(0) # the model requires a dummy batch dimension
output = model(input_img)
output = F.softmax(output, dim=1)
prediction_score, pred_label_idx = torch.topk(output, 1)
pred_label_idx.squeeze_()
predicted_label = idx_to_labels[str(pred_label_idx.item())][1]
print('Predicted:', predicted_label, '/', pred_label_idx.item(), ' (', prediction_score.squeeze().item(), ')')
...看起來我們的模型都正確識別了它們 - 但當然,我們想深入瞭解。為此,我們將使用 Captum Insights 小部件,我們使用一個 AttributionVisualizer 物件進行配置,該物件在下面匯入。AttributionVisualizer 期望批次資料,因此我們將引入 Captum 的 Batch 輔助類。我們將專門檢視影像,因此我們還將匯入 ImageFeature。
我們使用以下引數配置 AttributionVisualizer
一個待檢查的模型陣列(在我們的例子中只有一個)
一個評分函式,允許 Captum Insights 從模型中提取 top-k 預測
一個模型訓練所用的、有序的、可讀的類別列表
一個要查詢的特徵列表 - 在我們的例子中是一個
ImageFeature一個數據集,它是一個可迭代物件,返回輸入和標籤的批次 - 就像您用於訓練的那樣
from captum.insights import AttributionVisualizer, Batch
from captum.insights.attr_vis.features import ImageFeature
# Baseline is all-zeros input - this may differ depending on your data
def baseline_func(input):
return input * 0
# merging our image transforms from above
def full_img_transform(input):
i = Image.open(input)
i = transform(i)
i = transform_normalize(i)
i = i.unsqueeze(0)
return i
input_imgs = torch.cat(list(map(lambda i: full_img_transform(i), imgs)), 0)
visualizer = AttributionVisualizer(
models=[model],
score_func=lambda o: torch.nn.functional.softmax(o, 1),
classes=list(map(lambda k: idx_to_labels[k][1], idx_to_labels.keys())),
features=[
ImageFeature(
"Photo",
baseline_transforms=[baseline_func],
input_transforms=[],
)
],
dataset=[Batch(input_imgs, labels=[282,849,69])]
)
請注意,執行上面的單元格幾乎沒有花時間,這與我們上面執行歸因不同。這是因為 Captum Insights 允許您在視覺化小部件中配置不同的歸因演算法,然後它將計算並顯示歸因。這個過程將需要幾分鐘。
執行下面的單元格將渲染 Captum Insights 小部件。然後,您可以選擇歸因方法及其引數,根據預測類別或預測正確性過濾模型響應,檢視模型的預測及其相關機率,並檢視歸因與原始影像比較的熱圖。
visualizer.render()
指令碼總執行時間: ( 0 分鐘 0.000 秒)