您現在的位置是:首頁 > 遊戲

圖神經網路的可解釋性方法介紹和GNNExplainer解釋預測的程式碼示例

由 deephub 發表于 遊戲2022-09-16
簡介它學習並返回一個節點特徵掩碼和一個邊緣掩碼,這兩個掩碼在解釋GNN對一個圖的預測時起著至關重要的作用# Import libarariesimport numpy as npimport pandas as pdimport osimpor

define N 4什麼意思

深度學習模型的可解釋性為其預測提供了人類可以理解的推理。如果不解釋預測背後的原因,深度學習演算法就像黑匣子,對於一些場景說是無法被信任的。 不提供預測的原因也會阻止深度學習演算法在涉及跨域公平、隱私和安全的關鍵應用程式中使用。

深度學習模型的可解釋性有助於增加對模型預測的信任, 提高模型對與公平、隱私和其他安全挑戰相關的關鍵決策應用程式的透明度,並且可以讓我們瞭解網路特徵,以便在將模型部署到現實世界之前識別和糾正模型所犯錯誤的系統模式。

圖在現實世界中無處不在,代表社交網路、引用網路、化學分子、金融資料等。圖神經網路 (GNN) 是一個強大的框架,用於對圖相關資料進行機器學習,例如節點分類、圖分類、和連結預測。

圖神經網路的可解釋性方法介紹和GNNExplainer解釋預測的程式碼示例

所以本文探討以下5方面

GNN 需要可解釋性

解釋 GNN 預測的挑戰

不同的 GNN 解釋方

GNNExplainer的直觀解釋

使用 GNNExplainer 解釋節點分類和圖分類的實現

圖卷積神經網路 (GCN)、GraphSAGE 和圖注意網路 (GAT) 等 GNN 透過沿輸入圖的邊緣遞迴傳遞神經訊息,將節點特徵資訊與圖結構相結合。

圖神經網路的可解釋性方法介紹和GNNExplainer解釋預測的程式碼示例

同時結合圖結構和特徵資訊會導致複雜的模型; 因此,解釋 GNN 的預測是具有挑戰性的。

圖資料不如影象和文字直觀,這使得對圖深度學習模型的人類可以理解的解釋具有挑戰性。

影象和文字使用網格狀資料; 但是在拓撲圖中,資訊是使用特徵矩陣和鄰接矩陣來表示的,每個節點都有不同的鄰居。 因此影象和文字的可解釋性方法不適合獲得對圖的高質量解釋。

圖節點和邊對 GNN 的最終預測有顯著貢獻; 因此GNN 的可解釋性需要考慮這些互動。

節點分類任務透過執行來自其鄰居的訊息遍歷來預測節點的類別。 訊息遊走可以更好地瞭解 GNN 做出預測的原因,但這與影象和文字相比更具有挑戰性。

GNN 解釋方法

圖可解釋性需要回答以下問題:

哪些輸入邊更關鍵,對預測貢獻最大?

哪些輸入節點更重要?

哪些節點特徵更重要?

什麼圖模式將最大化某個類的預測?

解釋 GNN 的方法根據它們提供的解釋型別分為兩個分支。 這些圖解釋方法側重於圖模型的不同方面,並提供不同的檢視來理解 GNN 模型。

例項級方法:給定一個輸入圖,例項級方法透過識別用於預測的重要輸入特徵來解釋深度模型。

模型級方法提供了一般見解和高階理解來解釋深度圖模型。 模型級方法專門研究哪些輸入圖模式可以導致 GNN 進行某種預測。

圖神經網路的可解釋性方法介紹和GNNExplainer解釋預測的程式碼示例

上圖為解釋 GNN 的不同方法

例項級方法根據重要性分數的獲得方式進行區分,可以將它們分為四個不同的分支。

Gradients/Feature-based 方法使用梯度或隱藏特徵圖來表示不同輸入特徵的重要性,其中梯度或特徵值越高表示重要性越高。基於梯度/特徵的可解釋性方法廣泛用於影象和文字任務。 SA、Guided back propagation、CAM 和 Grad-CAM 是基於梯度/特徵的可解釋性方法的示例。

基於擾動的方法監測不同輸入擾動的輸出變化變化。當保留重要的輸入資訊時,預測應該與原始預測相似。 GNN 可以透過使用不同的掩碼生成演算法來獲得不同型別的掩碼來進行特徵重要性的判斷,如 GNNExplainer、PGExplainer、ZORRO、GraphMask、Causal Screening 和 SubgraphX。

分解方法透過將原始模型預測分解為若干項來衡量輸入特徵的重要性,這些項被視為相應輸入特徵的重要性分數。

代理方法採用簡單且可解釋的代理模型來近似複雜深度模型對輸入示例的相鄰區域的預測。代理方法包括 GraphLime、RelEx 和 PGM Explainer。

GNNExplainer

GNNExplainer 是一種與模型無關的基於擾動的方法,可以為任何基於圖的機器學習任務上的任何基於 GNN 的模型的預測提供可解釋的報告。

GNNExplainer 學習邊和節點特徵的軟掩碼,然後透過掩碼的最佳化來解釋預測。

GNNExplainer 會獲取輸入圖並識別緊湊的子圖結構和在預測中起關鍵作用的一小部分節點特徵。

圖神經網路的可解釋性方法介紹和GNNExplainer解釋預測的程式碼示例

GNNExplainer透過生成傳遞關鍵語義的掩碼來捕獲重要的輸入特徵,從而產生與原始預測相似的預測。它學習邊緣和節點特徵的軟掩碼,透過掩碼最佳化來解釋預測。

以不同方式為輸入圖獲得掩碼可以獲得重要的輸入特徵。 還根據預測任務的型別生成不同的掩碼,例如節點掩碼、邊掩碼和節點特徵掩碼。

圖神經網路的可解釋性方法介紹和GNNExplainer解釋預測的程式碼示例

生成的掩碼與輸入圖相結合,透過逐元素乘法獲得包含重要輸入資訊的新圖。 最後,將新圖輸入經過訓練的 GNN 以評估掩碼並更新掩碼生成演算法。

GNNExplainer 示例

explain_node() 學習並返回一個節點特徵掩碼和一個邊緣掩碼,它們在解釋 GNN 對節點分類所做的預測中起著至關重要的作用。

#Import Library

import numpy as np

import pandas as pd

import os

import torch_geometric。transforms as T

from torch_geometric。datasets import Planetoid

import matplotlib。pyplot as plt

import torch

import torch。nn。functional as F

from torch_geometric。nn import GCNConv, GNNExplainer

import torch_geometric

from torch_geometric。loader import NeighborLoader

from torch_geometric。utils import to_networkx

#Load the Planetoid dataset

dataset = Planetoid(root=‘。’, name=“Pubmed”)

data = dataset[0]

#Set the device dynamically

device = torch。device(‘cuda’ if torch。cuda。is_available() else ‘cpu’)

# Create batches with neighbor sampling

train_loader = NeighborLoader(

data,

num_neighbors=[5, 10],

batch_size=16,

input_nodes=data。train_mask,

# Define the GCN model

class Net(torch。nn。Module):

def __init__(self):

super()。__init__()

self。conv1 = GCNConv(dataset。num_features, 16, normalize=False)

self。conv2 = GCNConv(16, dataset。num_classes, normalize=False)

self。optimizer = torch。optim。Adam(self。parameters(), lr=0。02, weight_decay=5e-4)

def forward(self, x, edge_index):

x = F。relu(self。conv1(x, edge_index))

x = F。dropout(x, training=self。training)

x = self。conv2(x, edge_index)

return F。log_softmax(x, dim=1)

model = Net()。to(device)

def accuracy(pred_y, y):

“”“Calculate accuracy。”“”

return ((pred_y == y)。sum() / len(y))。item()

# define the function to Train the model

def train_nn(model, x,edge_index,epochs):

criterion = torch。nn。CrossEntropyLoss()

optimizer = model。optimizer

model。train()

for epoch in range(epochs+1):

total_loss = 0

acc = 0

val_loss = 0

val_acc = 0

# Train on batches

for batch in train_loader:

optimizer。zero_grad()

out = model(batch。x, batch。edge_index)

loss = criterion(out[batch。train_mask], batch。y[batch。train_mask])

total_loss += loss

acc += accuracy(out[batch。train_mask]。argmax(dim=1),

batch。y[batch。train_mask])

loss。backward()

optimizer。step()

# Validation

val_loss += criterion(out[batch。val_mask], batch。y[batch。val_mask])

val_acc += accuracy(out[batch。val_mask]。argmax(dim=1),

batch。y[batch。val_mask])

# Print metrics every 10 epochs

if(epoch % 10 == 0):

print(f‘Epoch {epoch:>3} | Train Loss: {total_loss/len(train_loader):。3f} ’

f‘| Train Acc: {acc/len(train_loader)*100:>6。2f}% | Val Loss: ’

f‘{val_loss/len(train_loader):。2f} | Val Acc: ’

f‘{val_acc/len(train_loader)*100:。2f}%’)

# define the function to Test the model

def test(model, data):

“”“Evaluate the model on test set and print the accuracy score。”“”

model。eval()

out = model(data。x, data。edge_index)

acc = accuracy(out。argmax(dim=1)[data。test_mask], data。y[data。test_mask])

return acc

# Train the Model

train_nn(model, data。x, data。edge_index, 200)

# Test

print(f‘\nGCN test accuracy: {test(model, data)*100:。2f}%\n’)

# Explain the GCN for node

node_idx = 20

x, edge_index = data。x, data。edge_index

# Pass the model to explain to GNNExplainer

explainer = GNNExplainer(model, epochs=100,return_type=‘log_prob’)

#returns a node feature mask and an edge mask that play a crucial role to explain the prediction made by the GNN for node 20

node_feat_mask, edge_mask = explainer。explain_node(node_idx, x, edge_index)

ax, G = explainer。visualize_subgraph(node_idx, edge_index, edge_mask, y=data。y)

plt。show()

print(“Ground Truth label for node: ”,node_idx, “ is ”, data。y。numpy()[node_idx])

out = torch。softmax(model(data。x, data。edge_index), dim=1)。argmax(dim=1)

print(“Prediction for node ”,node_idx, “is ” ,out[node_idx]。cpu()。detach()。numpy()。squeeze())

圖神經網路的可解釋性方法介紹和GNNExplainer解釋預測的程式碼示例

上圖中所有顏色相似的節點都屬於同一個類。視覺化有助於解釋哪些節點對預測貢獻最大。

Explain_graph()用於圖分類;它學習並返回一個節點特徵掩碼和一個邊緣掩碼,這兩個掩碼在解釋GNN對一個圖的預測時起著至關重要的作用

# Import libararies

import numpy as np

import pandas as pd

import os

import torch_geometric。transforms as T

from torch_geometric。datasets import Planetoid

import matplotlib。pyplot as plt

import torch

import torch。nn。functional as F

from torch_geometric。nn import GraphConv

import torch_geometric

from torch。nn import Parameter

from torch_geometric。nn。conv import MessagePassing

import urllib。request

import tarfile

from torch。nn import Linear

import torch。nn。functional as F

from torch_geometric。nn import GCNConv, GNNExplainer

from torch_geometric。nn import global_mean_pool

from torch_geometric。datasets import TUDataset

from torch_geometric。loader import DataLoader

# Load the dataset

dataset = TUDataset(root=‘data/TUDataset’, name=‘MUTAG’)

# print details about the graph

print(f‘Dataset: {dataset}:’)

print(“Number of Graphs: ”,len(dataset))

print(“Number of Freatures: ”, dataset。num_features)

print(“Number of Classes: ”, dataset。num_classes)

data= dataset[0]

print(data)

print(“No。 of nodes: ”, data。num_nodes)

print(“No。 of Edges: ”, data。num_edges)

print(f‘Average node degree: {data。num_edges / data。num_nodes:。2f}’)

print(f‘Has isolated nodes: {data。has_isolated_nodes()}’)

print(f‘Has self-loops: {data。has_self_loops()}’)

print(f‘Is undirected: {data。is_undirected()}’)

# Create train and test dataset

torch。manual_seed(12345)

dataset = dataset。shuffle()

train_dataset = dataset[:50]

test_dataset = dataset[50:]

print(f‘Number of training graphs: {len(train_dataset)}’)

print(f‘Number of test graphs: {len(test_dataset)}’)

‘’‘graphs in graph classification datasets are usually small,

a good idea is to batch the graphs before inputting

them into a Graph Neural Network to guarantee full GPU utilization__

_In pytorch Geometric adjacency matrices are stacked in a diagonal fashion

(creating a giant graph that holds multiple isolated subgraphs), a

nd node and target features are simply concatenated in the node dimension:

’‘’

train_loader = DataLoader(train_dataset, batch_size=64, shuffle= True)

test_loader= DataLoader(test_dataset, batch_size=64, shuffle= False)

for step, data in enumerate(train_loader):

print(f‘Step {step + 1}:’)

print(‘=======’)

print(f‘Number of graphs in the current batch: {data。num_graphs}’)

print(data)

print()

# Build the model

class GNN(torch。nn。Module):

def __init__(self, hidden_channels):

super(GNN, self)。__init__()

torch。manual_seed(12345)

self。conv1 = GraphConv(dataset。num_node_features, hidden_channels)

self。conv2 = GraphConv(hidden_channels, hidden_channels)

self。conv3 = GraphConv(hidden_channels, hidden_channels )

self。lin = Linear(hidden_channels, dataset。num_classes)

def forward(self, x, edge_index, batch):

x = self。conv1(x, edge_index)

x = x。relu()

x = self。conv2(x, edge_index)

x = x。relu()

x = self。conv3(x, edge_index)

x = global_mean_pool(x, batch)

x = F。dropout(x, p=0。5, training=self。training)

x = self。lin(x)

return x

model = GNN(hidden_channels=64)

print(model)

# set the optimizer

optimizer = torch。optim。Adam(model。parameters(), lr=0。02)

# set the loss function

criterion = torch。nn。CrossEntropyLoss()

# Creating the function to train the model

def train():

model。train()

for data in train_loader: # Iterate in batches over the training dataset。

out = model(data。x, data。edge_index, data。batch) # Perform a single forward pass。

loss = criterion(out, data。y) # Compute the loss。

loss。backward() # Derive gradients。

optimizer。step() # Update parameters based on gradients。

optimizer。zero_grad() # Clear gradients。

# function to test the model

def test(loader):

model。eval()

correct = 0

for data in loader: # Iterate in batches over the training/test dataset。

out = model(data。x, data。edge_index, data。batch)

pred = out。argmax(dim=1) # Use the class with highest probability。

correct += int((pred == data。y)。sum()) # Check against ground-truth labels。

return correct / len(loader。dataset) # Derive ratio of correct predictions。

# Train the model for 150 epochs

for epoch in range(1, 160):

train()

train_acc = test(train_loader)

test_acc = test(test_loader)

if(epoch % 10 == 0):

‘’‘print(f’Epoch {epoch:>3} | Train Loss: {total_loss/len(train_loader):。3f} ‘

f’| Train Acc: {acc/len(train_loader)*100:>6。2f}% | Val Loss: ‘

f’{val_loss/len(train_loader):。2f} | Val Acc: ‘

f’{val_acc/len(train_loader)*100:。2f}%‘)

’‘’

print(f‘Epoch: {epoch:03d}, Train Acc: {train_acc:。4f}, Test Acc: {test_acc:。4f}’)

#Explain the Graph

explainer = GNNExplainer(model, epochs=100,return_type=‘log_prob’)

data = dataset[0]

node_feat_mask, edge_mask = explainer。explain_graph(data。x, data。edge_index)

ax, G = explainer。visualize_subgraph(-1,data。edge_index, edge_mask, data。y)

plt。show()

當可視化visualize_subgraph時,需要將node_idx設定為-1,因為這意味著一個圖分類任務;否則會報錯。

圖神經網路的可解釋性方法介紹和GNNExplainer解釋預測的程式碼示例

本文使用的是pytorch-geometric實現的GNNExplainer作為示例,有興趣瞭解的話可以檢視其官方文件

https://avoid。overfit。cn/post/3a01457fe6094941a2bca2961f742dce

作者:Renu Khandelwal

推薦文章