class_docstr: ## Configurations
class_docstr_tok: ["#","#","Configurations"]
docstr: ### Training loop
  
  We do full batch training since the dataset is small.
  If we were to sample and train we will have to sample a set of
  nodes for each training step along with the edges that span
  across those selected nodes.
docstr_tok: ["#","#","#","Training","loop","We","do","full","batch","training","since","the","dataset","is","small",".","If","we","were","to","sample","and","train","we","will","have","to","sample","a","set","of","nodes","for","each","training","step","along","with","the","edges","that","span","across","those","selected","nodes","."]
code: def run(self):
      """
      ### Training loop
  
      We do full batch training since the dataset is small.
      If we were to sample and train we will have to sample a set of
      nodes for each training step along with the edges that span
      across those selected nodes.
      """
      # Move the feature vectors to the device
      features = self.dataset.features.to(self.device)
      # Move the labels to the device
      labels = self.dataset.labels.to(self.device)
      # Move the adjacency matrix to the device
      edges_adj = self.dataset.adj_mat.to(self.device)
      # Add an empty third dimension for the heads
      edges_adj = edges_adj.unsqueeze(-1)
  
      # Random indexes
      idx_rand = torch.randperm(len(labels))
      # Nodes for training
      idx_train = idx_rand[: self.training_samples]
      # Nodes for validation
      idx_valid = idx_rand[self.training_samples :]
  
      # Training loop
      for epoch in monit.loop(self.epochs):
          # Set the model to training mode
          self.model.train()
          # Make all the gradients zero
          self.optimizer.zero_grad()
          # Evaluate the model
          output = self.model(features, edges_adj)
          # Get the loss for training nodes
          loss = self.loss_func(output[idx_train], labels[idx_train])
          # Calculate gradients
          loss.backward()
          # Take optimization step
          self.optimizer.step()
          # Log the loss
          tracker.add("loss.train", loss)
          # Log the accuracy
          tracker.add(
              "accuracy.train",
              accuracy(output[idx_train], labels[idx_train]),
          )
  
          # Set mode to evaluation mode for validation
          self.model.eval()
  
          # No need to compute gradients
          with torch.no_grad():
              # Evaluate the model again
              output = self.model(features, edges_adj)
              # Calculate the loss for validation nodes
              loss = self.loss_func(
                  output[idx_valid], labels[idx_valid]
              )
              # Log the loss
              tracker.add("loss.valid", loss)
              # Log the accuracy
              tracker.add(
                  "accuracy.valid",
                  accuracy(output[idx_valid], labels[idx_valid]),
              )
  
          # Save logs
          tracker.save()
  
imports: from typing import Dict
  import numpy
  import torch
  from torch import nn
  from labml import lab, monit, tracker, experiment
  from labml.configs import BaseConfigs, option, calculate
  from labml.utils import download
  from labml_helpers.device import DeviceConfigs
  from labml_helpers.module import Module
  from labml_nn.graphs.gat import GraphAttentionLayer
  from labml_nn.optimizers.configs import OptimizerConfigs
label_desc: Use image node_id 1 for calling the Configs obj's underlying member method code with example usage: obj.run() without return types