class_docstr: GNNExplainer model from `GNNExplainer: Generating Explanations for
  Graph Neural Networks <https://arxiv.org/abs/1903.03894>`__
  
  It identifies compact subgraph structures and small subsets of node features that play a
  critical role in GNN-based node classification and graph classification.
  
  To generate an explanation, it learns an edge mask :math:`M` and a feature mask :math:`F`
  by optimizing the following objective function.
  
  .. math::
    l(y, \hat{y}) + \alpha_1 \|M\|_1 + \alpha_2 H(M) + \beta_1 \|F\|_1 + \beta_2 H(F)
  
  where :math:`l` is the loss function, :math:`y` is the original model prediction,
  :math:`\hat{y}` is the model prediction with the edge and feature mask applied, :math:`H` is
  the entropy function.
  
  Parameters
  ----------
  model : nn.Module
      The GNN model to explain.
  
      * The required arguments of its forward function are graph and feat.
        The latter one is for input node features.
      * It should also optionally take an eweight argument for edge weights
        and multiply the messages by it in message passing.
      * The output of its forward function is the logits for the predicted
        node/graph classes.
  
      See also the example in :func:`explain_node` and :func:`explain_graph`.
  num_hops : int
      The number of hops for GNN information aggregation.
  lr : float, optional
      The learning rate to use, default to 0.01.
  num_epochs : int, optional
      The number of epochs to train.
  alpha1 : float, optional
      A higher value will make the explanation edge masks more sparse by decreasing
      the sum of the edge mask.
  alpha2 : float, optional
      A higher value will make the explanation edge masks more sparse by decreasing
      the entropy of the edge mask.
  beta1 : float, optional
      A higher value will make the explanation node feature masks more sparse by
      decreasing the mean of the node feature mask.
  beta2 : float, optional
      A higher value will make the explanation node feature masks more sparse by
      decreasing the entropy of the node feature mask.
  log : bool, optional
      If True, it will log the computation process, default to True.
class_docstr_tok: ["GNNExplainer","model","from","`","GNNExplainer",":","Generating","Explanations","for","Graph","Neural","Networks","<","https",":","\/\/arxiv.org\/abs\/1903.03894",">","`","__","It","identifies","compact","subgraph","structures","and","small","subsets","of","node","features","that","play","a","critical","role","in","GNN-based","node","classification","and","graph","classification",".","To","generate","an","explanation",",","it","learns","an","edge","mask",":","math",":","`","M","`","and","a","feature","mask",":","math",":","`","F","`","by","optimizing","the","following","objective","function",".","..","math",":",":","l","(","y",",","\\hat","{","y","}",")","+","\\alpha_1","\\|M\\|_1","+","\\alpha_2","H","(","M",")","+","\\beta_1","\\|F\\|_1","+","\\beta_2","H","(","F",")","where",":","math",":","`","l","`","is","the","loss","function",",",":","math",":","`","y","`","is","the","original","model","prediction",",",":","math",":","`","\\hat","{","y","}","`","is","the","model","prediction","with","the","edge","and","feature","mask","applied",",",":","math",":","`","H","`","is","the","entropy","function",".","Parameters","--","--","--","--","--","model",":","nn.Module","The","GNN","model","to","explain",".","*","The","required","arguments","of","its","forward","function","are","graph","and","feat",".","The","latter","one","is","for","input","node","features",".","*","It","should","also","optionally","take","an","eweight","argument","for","edge","weights","and","multiply","the","messages","by","it","in","message","passing",".","*","The","output","of","its","forward","function","is","the","logits","for","the","predicted","node\/graph","classes",".","See","also","the","example","in",":","func",":","`","explain_node","`","and",":","func",":","`","explain_graph","`",".","num_hops",":","int","The","number","of","hops","for","GNN","information","aggregation",".","lr",":","float",",","optional","The","learning","rate","to","use",",","default","to","0.01.","num_epochs",":","int",",","optional","The","number","of","epochs","to","train",".","alpha1",":","float",",","optional","A","higher","value","will","make","the","explanation","edge","masks","more","sparse","by","decreasing","the","sum","of","the","edge","mask",".","alpha2",":","float",",","optional","A","higher","value","will","make","the","explanation","edge","masks","more","sparse","by","decreasing","the","entropy","of","the","edge","mask",".","beta1",":","float",",","optional","A","higher","value","will","make","the","explanation","node","feature","masks","more","sparse","by","decreasing","the","mean","of","the","node","feature","mask",".","beta2",":","float",",","optional","A","higher","value","will","make","the","explanation","node","feature","masks","more","sparse","by","decreasing","the","entropy","of","the","node","feature","mask",".","log",":","bool",",","optional","If","True",",","it","will","log","the","computation","process",",","default","to","True","."]
docstr: Learn and return a node feature mask and subgraph that play a
  crucial role to explain the prediction made by the GNN for node
  :attr:`node_id`.
  
  Parameters
  ----------
  node_id : int
      The node to explain.
  graph : DGLGraph
      A homogeneous graph.
  feat : Tensor
      The input feature of shape :math:`(N, D)`. :math:`N` is the
      number of nodes, and :math:`D` is the feature size.
  kwargs : dict
      Additional arguments passed to the GNN model. Tensors whose
      first dimension is the number of nodes or edges will be
      assumed to be node/edge features.
  
  Returns
  -------
  new_node_id : Tensor
      The new ID of the input center node.
  sg : DGLGraph
      The subgraph induced on the k-hop in-neighborhood of the input center node.
  feat_mask : Tensor
      Learned node feature importance mask of shape :math:`(D)`, where :math:`D` is the
      feature size. The values are within range :math:`(0, 1)`.
      The higher, the more important.
  edge_mask : Tensor
      Learned importance mask of the edges in the subgraph, which is a tensor
      of shape :math:`(E)`, where :math:`E` is the number of edges in the
      subgraph. The values are within range :math:`(0, 1)`.
      The higher, the more important.
  
  Examples
  --------
  
  >>> import dgl
  >>> import dgl.function as fn
  >>> import torch
  >>> import torch.nn as nn
  >>> from dgl.data import CoraGraphDataset
  >>> from dgl.nn import GNNExplainer
  
  >>> # Load dataset
  >>> data = CoraGraphDataset()
  >>> g = data[0]
  >>> features = g.ndata['feat']
  >>> labels = g.ndata['label']
  >>> train_mask = g.ndata['train_mask']
  
  >>> # Define a model
  >>> class Model(nn.Module):
  ...     def __init__(self, in_feats, out_feats):
  ...         super(Model, self).__init__()
  ...         self.linear = nn.Linear(in_feats, out_feats)
  ...
  ...     def forward(self, graph, feat, eweight=None):
  ...         with graph.local_scope():
  ...             feat = self.linear(feat)
  ...             graph.ndata['h'] = feat
  ...             if eweight is None:
  ...                 graph.update_all(fn.copy_u('h', 'm'), fn.sum('m', 'h'))
  ...             else:
  ...                 graph.edata['w'] = eweight
  ...                 graph.update_all(fn.u_mul_e('h', 'w', 'm'), fn.sum('m', 'h'))
  ...             return graph.ndata['h']
  
  >>> # Train the model
  >>> model = Model(features.shape[1], data.num_classes)
  >>> criterion = nn.CrossEntropyLoss()
  >>> optimizer = torch.optim.Adam(model.parameters(), lr=1e-2)
  >>> for epoch in range(10):
  ...     logits = model(g, features)
  ...     loss = criterion(logits[train_mask], labels[train_mask])
  ...     optimizer.zero_grad()
  ...     loss.backward()
  ...     optimizer.step()
  
  >>> # Explain the prediction for node 10
  >>> explainer = GNNExplainer(model, num_hops=1)
  >>> new_center, sg, feat_mask, edge_mask = explainer.explain_node(10, g, features)
  >>> new_center
  tensor([1])
  >>> sg.num_edges()
  12
  >>> # Old IDs of the nodes in the subgraph
  >>> sg.ndata[dgl.NID]
  tensor([ 9, 10, 11, 12])
  >>> # Old IDs of the edges in the subgraph
  >>> sg.edata[dgl.EID]
  tensor([51, 53, 56, 48, 52, 57, 47, 50, 55, 46, 49, 54])
  >>> feat_mask
  tensor([0.2638, 0.2738, 0.3039,  ..., 0.2794, 0.2643, 0.2733])
  >>> edge_mask
  tensor([0.0937, 0.1496, 0.8287, 0.8132, 0.8825, 0.8515, 0.8146, 0.0915, 0.1145,
          0.9011, 0.1311, 0.8437])
docstr_tok: ["Learn","and","return","a","node","feature","mask","and","subgraph","that","play","a","crucial","role","to","explain","the","prediction","made","by","the","GNN","for","node",":","attr",":","`","node_id","`",".","Parameters","--","--","--","--","--","node_id",":","int","The","node","to","explain",".","graph",":","DGLGraph","A","homogeneous","graph",".","feat",":","Tensor","The","input","feature","of","shape",":","math",":","`","(","N",",","D",")","`",".",":","math",":","`","N","`","is","the","number","of","nodes",",","and",":","math",":","`","D","`","is","the","feature","size",".","kwargs",":","dict","Additional","arguments","passed","to","the","GNN","model",".","Tensors","whose","first","dimension","is","the","number","of","nodes","or","edges","will","be","assumed","to","be","node\/edge","features",".","Returns","--","--","--","-","new_node_id",":","Tensor","The","new","ID","of","the","input","center","node",".","sg",":","DGLGraph","The","subgraph","induced","on","the","k-hop","in-neighborhood","of","the","input","center","node",".","feat_mask",":","Tensor","Learned","node","feature","importance","mask","of","shape",":","math",":","`","(","D",")","`",",","where",":","math",":","`","D","`","is","the","feature","size",".","The","values","are","within","range",":","math",":","`","(","0",",","1",")","`",".","The","higher",",","the","more","important",".","edge_mask",":","Tensor","Learned","importance","mask","of","the","edges","in","the","subgraph",",","which","is","a","tensor","of","shape",":","math",":","`","(","E",")","`",",","where",":","math",":","`","E","`","is","the","number","of","edges","in","the","subgraph",".","The","values","are","within","range",":","math",":","`","(","0",",","1",")","`",".","The","higher",",","the","more","important",".","Examples","--","--","--","--",">",">",">","import","dgl",">",">",">","import","dgl.function","as","fn",">",">",">","import","torch",">",">",">","import","torch.nn","as","nn",">",">",">","from","dgl.data","import","CoraGraphDataset",">",">",">","from","dgl.nn","import","GNNExplainer",">",">",">","#","Load","dataset",">",">",">","data","=","CoraGraphDataset","(",")",">",">",">","g","=","data","[","0","]",">",">",">","features","=","g.ndata","[","'feat","'","]",">",">",">","labels","=","g.ndata","[","'label","'","]",">",">",">","train_mask","=","g.ndata","[","'train_mask","'","]",">",">",">","#","Define","a","model",">",">",">","class","Model","(","nn.Module",")",":","...","def","__init__","(","self",",","in_feats",",","out_feats",")",":","...","super","(","Model",",","self",")",".__init__","(",")","...","self.linear","=","nn.Linear","(","in_feats",",","out_feats",")","...","...","def","forward","(","self",",","graph",",","feat",",","eweight=None",")",":","...","with","graph.local_scope","(",")",":","...","feat","=","self.linear","(","feat",")","...","graph.ndata","[","'","h","'","]","=","feat","...","if","eweight","is","None",":","...","graph.update_all","(","fn.copy_u","(","'","h","'",",","'m","'",")",",","fn.sum","(","'m","'",",","'","h","'",")",")","...","else",":","...","graph.edata","[","'","w","'","]","=","eweight","...","graph.update_all","(","fn.u_mul_e","(","'","h","'",",","'","w","'",",","'m","'",")",",","fn.sum","(","'m","'",",","'","h","'",")",")","...","return","graph.ndata","[","'","h","'","]",">",">",">","#","Train","the","model",">",">",">","model","=","Model","(","features.shape","[","1","]",",","data.num_classes",")",">",">",">","criterion","=","nn.CrossEntropyLoss","(",")",">",">",">","optimizer","=","torch.optim.Adam","(","model.parameters","(",")",",","lr=1e-2",")",">",">",">","for","epoch","in","range","(","10",")",":","...","logits","=","model","(","g",",","features",")","...","loss","=","criterion","(","logits","[","train_mask","]",",","labels","[","train_mask","]",")","...","optimizer.zero_grad","(",")","...","loss.backward","(",")","...","optimizer.step","(",")",">",">",">","#","Explain","the","prediction","for","node","10",">",">",">","explainer","=","GNNExplainer","(","model",",","num_hops=1",")",">",">",">","new_center",",","sg",",","feat_mask",",","edge_mask","=","explainer.explain_node","(","10",",","g",",","features",")",">",">",">","new_center","tensor","(","[","1","]",")",">",">",">","sg.num_edges","(",")","12",">",">",">","#","Old","IDs","of","the","nodes","in","the","subgraph",">",">",">","sg.ndata","[","dgl.NID","]","tensor","(","[","9",",","10",",","11",",","12","]",")",">",">",">","#","Old","IDs","of","the","edges","in","the","subgraph",">",">",">","sg.edata","[","dgl.EID","]","tensor","(","[","51",",","53",",","56",",","48",",","52",",","57",",","47",",","50",",","55",",","46",",","49",",","54","]",")",">",">",">","feat_mask","tensor","(","[","0.2638",",","0.2738",",","0.3039",",","...",",","0.2794",",","0.2643",",","0.2733","]",")",">",">",">","edge_mask","tensor","(","[","0.0937",",","0.1496",",","0.8287",",","0.8132",",","0.8825",",","0.8515",",","0.8146",",","0.0915",",","0.1145",",","0.9011",",","0.1311",",","0.8437","]",")"]
code: def explain_node(self, node_id, graph, feat, **kwargs):
      r"""Learn and return a node feature mask and subgraph that play a
      crucial role to explain the prediction made by the GNN for node
      :attr:`node_id`.
  
      Parameters
      ----------
      node_id : int
          The node to explain.
      graph : DGLGraph
          A homogeneous graph.
      feat : Tensor
          The input feature of shape :math:`(N, D)`. :math:`N` is the
          number of nodes, and :math:`D` is the feature size.
      kwargs : dict
          Additional arguments passed to the GNN model. Tensors whose
          first dimension is the number of nodes or edges will be
          assumed to be node/edge features.
  
      Returns
      -------
      new_node_id : Tensor
          The new ID of the input center node.
      sg : DGLGraph
          The subgraph induced on the k-hop in-neighborhood of the input center node.
      feat_mask : Tensor
          Learned node feature importance mask of shape :math:`(D)`, where :math:`D` is the
          feature size. The values are within range :math:`(0, 1)`.
          The higher, the more important.
      edge_mask : Tensor
          Learned importance mask of the edges in the subgraph, which is a tensor
          of shape :math:`(E)`, where :math:`E` is the number of edges in the
          subgraph. The values are within range :math:`(0, 1)`.
          The higher, the more important.
  
      Examples
      --------
  
      >>> import dgl
      >>> import dgl.function as fn
      >>> import torch
      >>> import torch.nn as nn
      >>> from dgl.data import CoraGraphDataset
      >>> from dgl.nn import GNNExplainer
  
      >>> # Load dataset
      >>> data = CoraGraphDataset()
      >>> g = data[0]
      >>> features = g.ndata['feat']
      >>> labels = g.ndata['label']
      >>> train_mask = g.ndata['train_mask']
  
      >>> # Define a model
      >>> class Model(nn.Module):
      ...     def __init__(self, in_feats, out_feats):
      ...         super(Model, self).__init__()
      ...         self.linear = nn.Linear(in_feats, out_feats)
      ...
      ...     def forward(self, graph, feat, eweight=None):
      ...         with graph.local_scope():
      ...             feat = self.linear(feat)
      ...             graph.ndata['h'] = feat
      ...             if eweight is None:
      ...                 graph.update_all(fn.copy_u('h', 'm'), fn.sum('m', 'h'))
      ...             else:
      ...                 graph.edata['w'] = eweight
      ...                 graph.update_all(fn.u_mul_e('h', 'w', 'm'), fn.sum('m', 'h'))
      ...             return graph.ndata['h']
  
      >>> # Train the model
      >>> model = Model(features.shape[1], data.num_classes)
      >>> criterion = nn.CrossEntropyLoss()
      >>> optimizer = torch.optim.Adam(model.parameters(), lr=1e-2)
      >>> for epoch in range(10):
      ...     logits = model(g, features)
      ...     loss = criterion(logits[train_mask], labels[train_mask])
      ...     optimizer.zero_grad()
      ...     loss.backward()
      ...     optimizer.step()
  
      >>> # Explain the prediction for node 10
      >>> explainer = GNNExplainer(model, num_hops=1)
      >>> new_center, sg, feat_mask, edge_mask = explainer.explain_node(10, g, features)
      >>> new_center
      tensor([1])
      >>> sg.num_edges()
      12
      >>> # Old IDs of the nodes in the subgraph
      >>> sg.ndata[dgl.NID]
      tensor([ 9, 10, 11, 12])
      >>> # Old IDs of the edges in the subgraph
      >>> sg.edata[dgl.EID]
      tensor([51, 53, 56, 48, 52, 57, 47, 50, 55, 46, 49, 54])
      >>> feat_mask
      tensor([0.2638, 0.2738, 0.3039,  ..., 0.2794, 0.2643, 0.2733])
      >>> edge_mask
      tensor([0.0937, 0.1496, 0.8287, 0.8132, 0.8825, 0.8515, 0.8146, 0.0915, 0.1145,
              0.9011, 0.1311, 0.8437])
      """
      self.model = self.model.to(graph.device)
      self.model.eval()
      num_nodes = graph.num_nodes()
      num_edges = graph.num_edges()
  
      # Extract node-centered k-hop subgraph and
      # its associated node and edge features.
      sg, inverse_indices = khop_in_subgraph(
          graph, node_id, self.num_hops
      )
      sg_nodes = sg.ndata[NID].long()
      sg_edges = sg.edata[EID].long()
      feat = feat[sg_nodes]
      for key, item in kwargs.items():
          if torch.is_tensor(item) and item.size(0) == num_nodes:
              item = item[sg_nodes]
          elif torch.is_tensor(item) and item.size(0) == num_edges:
              item = item[sg_edges]
          kwargs[key] = item
  
      # Get the initial prediction.
      with torch.no_grad():
          logits = self.model(graph=sg, feat=feat, **kwargs)
          pred_label = logits.argmax(dim=-1)
  
      feat_mask, edge_mask = self._init_masks(sg, feat)
  
      params = [feat_mask, edge_mask]
      optimizer = torch.optim.Adam(params, lr=self.lr)
  
      if self.log:
          pbar = tqdm(total=self.num_epochs)
          pbar.set_description(f"Explain node {node_id}")
  
      for _ in range(self.num_epochs):
          optimizer.zero_grad()
          h = feat * feat_mask.sigmoid()
          logits = self.model(
              graph=sg, feat=h, eweight=edge_mask.sigmoid(), **kwargs
          )
          log_probs = logits.log_softmax(dim=-1)
          loss = -log_probs[
              inverse_indices, pred_label[inverse_indices]
          ]
          loss = self._loss_regularize(loss, feat_mask, edge_mask)
          loss.backward()
          optimizer.step()
  
          if self.log:
              pbar.update(1)
  
      if self.log:
          pbar.close()
  
      feat_mask = feat_mask.detach().sigmoid().squeeze()
      edge_mask = edge_mask.detach().sigmoid()
  
      return inverse_indices, sg, feat_mask, edge_mask
  
file_path: dgl/python/dgl/nn/pytorch/explain/gnnexplainer.py
imports: from math import sqrt
  import torch
  from torch import nn
  from tqdm import tqdm
  from ....base import EID, NID
  from ....subgraph import khop_in_subgraph
label_desc: Use image node_id 4 for calling the GNNExplainer obj's underlying member method code with example usage: obj.explain_node(node_id, graph, feat) and returns: inverse_indices,  sg,  feat_mask,  edge_mask