code: def train(
      args, graph, features, train_set, valid_set, num_classes, model
  ):
      optimizer = torch.optim.Adam(model.parameters(), lr=args.lr)
      dataloader = create_dataloader(
          graph=graph,
          features=features,
          itemset=train_set,
          batch_size=args.batch_size,
          fanout=args.fanout,
          device=args.device,
          num_workers=args.num_workers,
          job="train",
      )
  
      for epoch in range(args.epochs):
          t0 = time.time()
          model.train()
          total_loss = 0
          for step, data in tqdm(enumerate(dataloader), "Training"):
              # The input features from the source nodes in the first layer's
              # computation graph.
              x = data.node_features["feat"]
  
              # The ground truth labels from the destination nodes
              # in the last layer's computation graph.
              y = data.labels
  
              y_hat = model(data.blocks, x)
  
              # Compute loss.
              loss = F.cross_entropy(y_hat, y)
  
              optimizer.zero_grad()
              loss.backward()
              optimizer.step()
  
              total_loss += loss.item()
  
          t1 = time.time()
          # Evaluate the model.
          acc = evaluate(
              args, model, graph, features, valid_set, num_classes
          )
          print(
              f"Epoch {epoch:05d} | Loss {total_loss / (step + 1):.4f} | "
              f"Accuracy {acc.item():.4f} | Time {t1 - t0:.4f}"
          )
  
imports: import argparse
  import time
  import dgl.graphbolt
  import dgl.nn
  import torch
  import torch.nn
  import torch.nn.functional
  import torchmetrics.functional
  from tqdm import tqdm
label_desc: Use image node_id 4 for calling a global function with example usage: train(args, graph, features, train_set, valid_set, num_classes, model) without return types