docstr: Inference with the GraphSAGE model on
  full neighbors (i.e. without neighbor sampling).
  g : the entire graph.
  batch_size : the node number of each inference output
  device : the inference device
docstr_tok: ["Inference","with","the","GraphSAGE","model","on","full","neighbors","(","i.e",".","without","neighbor","sampling",")",".","g",":","the","entire","graph",".","batch_size",":","the","node","number","of","each","inference","output","device",":","the","inference","device"]
code: def inference(self, g, batch_size, device):
      """
      Inference with the GraphSAGE model on
      full neighbors (i.e. without neighbor sampling).
      g : the entire graph.
      batch_size : the node number of each inference output
      device : the inference device
      """
      # During inference with sampling,
      # multi-layer blocks are very inefficient because
      # lots of computations in the first few layers are repeated.
      # Therefore, we compute the representation of all nodes layer by layer.
      # The nodes on each layer are of course splitted in batches.
  
      all_node_ids = torch.arange(0, g.num_nodes()).to(device)
      feat = g.get_node_storage(key="feat", ntype="_N").fetch(
          all_node_ids, device=device
      )
      sampler = dgl.dataloading.MultiLayerFullNeighborSampler(
          1, prefetch_node_feats=["feat"]
      )
      dataloader = dgl.dataloading.DataLoader(
          g,
          torch.arange(g.num_nodes(), dtype=torch.int32).to(g.device),
          sampler,
          device=device,
          batch_size=batch_size,
          shuffle=False,
          drop_last=False,
          num_workers=0,
      )
      buffer_device = torch.device("cpu")
      pin_memory = buffer_device != device
  
      for l_id, layer in enumerate(self.layers):
          y = torch.empty(
              g.num_nodes(),
              self.hid_size
              if l_id != len(self.layers) - 1
              else self.out_size,
              device=buffer_device,
              pin_memory=pin_memory,
          )
          feat = feat.to(device)
          for input_nodes, output_nodes, blocks in tqdm.tqdm(
              dataloader
          ):
              x = feat[input_nodes]
              h = layer(blocks[0], x)  # len(blocks) = 1
              if l_id != len(self.layers) - 1:
                  h = F.relu(h)
                  h = self.dropout(h)
              # by design, our output nodes are contiguous
              y[output_nodes[0] : output_nodes[-1] + 1] = h.to(
                  buffer_device
              )
          feat = y
      return y
  
file_path: cugraph/python/cugraph-dgl/examples/multi_trainer_MG_example/model.py
imports: import time
  import dgl
  import torch
  import torch.nn
  import torch.nn.functional
  import torchmetrics.functional
  from cugraph_dgl.nn import SAGEConv
  import tqdm
label_desc: Use image node_id 3 for calling the Sage obj's underlying member method code with example usage: obj.inference(g, batch_size, device) and returns: y