Hi Mufeili,
See this script:
import dgl
import dgl.nn as dglnn
import torch
import torch.nn.functional as F
from captum.attr import IntegratedGradients
from functools import partial
dataset = dgl.data.MUTAGDataset()
g = dataset[0]
# Some synthetic node features - replace it with your own.
for ntype in g.ntypes:
g.nodes[ntype].data['x'] = torch.randn(g.num_nodes(ntype), 10)
# Your model...
class Model(torch.nn.Module):
def __init__(self, etypes):
super().__init__()
self.conv1 = dglnn.HeteroGraphConv({etype: dglnn.SAGEConv(10, 10, 'mean') for _, etype, _ in etypes})
self.w = dglnn.EdgeWeightNorm()
self.l = torch.nn.Linear(10,1)
def forward(self, blocks, h_dict, eweight = None):
if eweight is None:
h_dict = self.conv1(blocks[0], h_dict)
else:
h_dict = self.conv1(blocks[0], h_dict, mod_kwargs={
c_etype: {'edge_weight': self.w(blocks[0][c_etype], eweight[c_etype])} for c_etype in blocks[0].canonical_etypes
})
return self.l(h_dict['SCHEMA'])
m = Model(g.canonical_etypes)
# Minibatch sampling stuff...
sampler = dgl.dataloading.as_edge_prediction_sampler(
dgl.dataloading.NeighborSampler([2, 2]), negative_sampler=dgl.dataloading.negative_sampler.Uniform(2))
eid = {g.canonical_etypes[0]: torch.arange(g.num_edges(g.canonical_etypes[0]))}
# Let's iterate over and explain one edge at a time.
dl = dgl.dataloading.DataLoader(g, eid, sampler, batch_size=1)
# Define a function that takes in a single tensor as the first argument and also returns a
# single tensor.
def forward_model(x, c_etype, blocks, x_dict, eweight):
eweight = eweight.copy()
eweight[c_etype] = x
return m(blocks, x_dict, eweight=eweight).squeeze()
for input_nodes, pair_graph, neg_pair_graph, blocks in dl:
input_dict = blocks[0].ndata['x']
output = m(blocks, input_dict)
loss = F.mse_loss(
input=output,
target=torch.randn(output.size()).float(),
)
loss.backward()
m.zero_grad()
edge_masks = {}
for c_etype in blocks[0].canonical_etypes:
edge_masks[c_etype] = torch.ones(blocks[0].num_edges(c_etype))
edge_masks[c_etype].requires_grad = True
for c_etype in blocks[0].canonical_etypes:
ig = IntegratedGradients(partial(forward_model,
c_etype=c_etype, blocks=blocks, x_dict=input_dict, eweight = edge_masks))
print(ig.attribute(edge_masks[c_etype], internal_batch_size=edge_masks[c_etype].size(0), n_steps=50))
break
And I got the error:
Traceback (most recent call last):
File “test2.py”, line 75, in
print(ig.attribute(edge_masks[c_etype], internal_batch_size=edge_masks[c_etype].size(0), n_steps=50))
File “/home/hongbo/anaconda3/envs/mumin/lib/python3.7/site-packages/captum/log/init.py”, line 42, in wrapper
return func(*args, **kwargs)
File “/home/hongbo/anaconda3/envs/mumin/lib/python3.7/site-packages/captum/attr/_core/integrated_gradients.py”, line 283, in attribute
method=method,
File “/home/hongbo/anaconda3/envs/mumin/lib/python3.7/site-packages/captum/attr/_utils/batching.py”, line 79, in _batch_attribution
**kwargs, n_steps=batch_steps, step_sizes_and_alphas=(step_sizes, alphas)
File “/home/hongbo/anaconda3/envs/mumin/lib/python3.7/site-packages/captum/attr/_core/integrated_gradients.py”, line 355, in _attribute
additional_forward_args=input_additional_args,
File “/home/hongbo/anaconda3/envs/mumin/lib/python3.7/site-packages/captum/_utils/gradient.py”, line 119, in compute_gradients
grads = torch.autograd.grad(torch.unbind(outputs), inputs)
File “/home/hongbo/anaconda3/envs/mumin/lib/python3.7/site-packages/torch/autograd/init.py”, line 302, in grad
allow_unused, accumulate_grad=False) # Calls into the C++ engine to run the backward pass
RuntimeError: One of the differentiated Tensors appears to not have been used in the graph. Set allow_unused=True if this is the desired behavior.