class_docstr: Encoder based on dgl.nn.NNConv & GRU & dgl.nn.set2set pooling
  Parameters
  -----------
  in_dim: int
      Input feature size.
  hid_dim: int
      Hidden feature size.
  
  Functions
  -----------
  forward(graph, nfeat, efeat):
      graph: DGLGraph
      nfeat: Tensor
          [N * D1], node features
      efeat: Tensor
          [E * D2], edge features
code: def __init__(self, in_dim, hid_dim):
      super(NNConvEncoder, self).__init__()
  
      self.lin0 = Linear(in_dim, hid_dim)
  
      # mlp for edge convolution in NNConv
      block = Sequential(
          Linear(5, 128), ReLU(), Linear(128, hid_dim * hid_dim)
      )
  
      self.conv = NNConv(
          hid_dim,
          hid_dim,
          edge_func=block,
          aggregator_type="mean",
          residual=False,
      )
      self.gru = GRU(hid_dim, hid_dim)
  
      # set2set pooling
      self.set2set = Set2Set(hid_dim, n_iters=3, n_layers=1)
  
imports: import torch
  import torch.nn
  import torch.nn.functional
  from dgl.nn import GINConv, NNConv, Set2Set
  from dgl.nn.pytorch.glob import SumPooling
  from torch.nn import BatchNorm1d, GRU, Linear, ModuleList, ReLU, Sequential
  from utils import global_global_loss_, local_global_loss_
label_desc: Use image node_id 1 to create a new NNConvEncoder object from inherited base classes: nn with example: obj = NNConvEncoder(in_dim, hid_dim)