class_docstr: Implementation of the messy collaboration framework from the paper
  "Improving Generalization Robustness with Noisy Collaboration in Knowledge Distillation"
  https://arxiv.org/abs/1910.05057
  
  :param teacher_model (torch.nn.Module): Teacher model
  :param student_model (torch.nn.Module): Student model
  :param train_loader (torch.utils.data.DataLoader): Dataloader for training
  :param val_loader (torch.utils.data.DataLoader): Dataloader for validation/testing
  :param optimizer_teacher (torch.optim.*): Optimizer used for training teacher
  :param optimizer_student (torch.optim.*): Optimizer used for training student
  :param noise_rate (float): Fraction of samples in a batch whose labels are perturbed
  :param method (string): Decides whether perturbation is done in teacher training('T'), student training('S') or both('TS).
  :param loss_fn (torch.nn.Module):  Calculates loss during distillation
  :param temp (float): Temperature parameter for distillation
  :param distil_weight (float): Weight paramter for distillation loss
  :param device (str): Device used for training; 'cpu' for cpu and 'cuda' for gpu
  :param log (bool): True if logging required
  :param logdir (str): Directory for storing logs
class_docstr_tok: ["Implementation","of","the","messy","collaboration","framework","from","the","paper","''","Improving","Generalization","Robustness","with","Noisy","Collaboration","in","Knowledge","Distillation","''","https",":","\/\/arxiv.org\/abs\/1910.05057",":","param","teacher_model","(","torch.nn.Module",")",":","Teacher","model",":","param","student_model","(","torch.nn.Module",")",":","Student","model",":","param","train_loader","(","torch.utils.data.DataLoader",")",":","Dataloader","for","training",":","param","val_loader","(","torch.utils.data.DataLoader",")",":","Dataloader","for","validation\/testing",":","param","optimizer_teacher","(","torch.optim",".","*",")",":","Optimizer","used","for","training","teacher",":","param","optimizer_student","(","torch.optim",".","*",")",":","Optimizer","used","for","training","student",":","param","noise_rate","(","float",")",":","Fraction","of","samples","in","a","batch","whose","labels","are","perturbed",":","param","method","(","string",")",":","Decides","whether","perturbation","is","done","in","teacher","training","(","'T","'",")",",","student","training","(","'S","'",")","or","both","(","'TS",")",".",":","param","loss_fn","(","torch.nn.Module",")",":","Calculates","loss","during","distillation",":","param","temp","(","float",")",":","Temperature","parameter","for","distillation",":","param","distil_weight","(","float",")",":","Weight","paramter","for","distillation","loss",":","param","device","(","str",")",":","Device","used","for","training",";","'cpu","'","for","cpu","and","'cuda","'","for","gpu",":","param","log","(","bool",")",":","True","if","logging","required",":","param","logdir","(","str",")",":","Directory","for","storing","logs"]
docstr: Function to train student model - for internal use only.
  
  :param epochs (int): Number of epochs you want to train the teacher
  :param plot_losses (bool): True if you want to plot the losses
  :param save_model (bool): True if you want to save the student model
  :param save_model_pth (str): Path where you want to save the student model
docstr_tok: ["Function","to","train","student","model","-","for","internal","use","only",".",":","param","epochs","(","int",")",":","Number","of","epochs","you","want","to","train","the","teacher",":","param","plot_losses","(","bool",")",":","True","if","you","want","to","plot","the","losses",":","param","save_model","(","bool",")",":","True","if","you","want","to","save","the","student","model",":","param","save_model_pth","(","str",")",":","Path","where","you","want","to","save","the","student","model"]
code: def _train_student(
      self,
      epochs=10,
      plot_losses=True,
      save_model=True,
      save_model_pth="./models/student.pth",
  ):
      """
      Function to train student model - for internal use only.
  
      :param epochs (int): Number of epochs you want to train the teacher
      :param plot_losses (bool): True if you want to plot the losses
      :param save_model (bool): True if you want to save the student model
      :param save_model_pth (str): Path where you want to save the student model
      """
      self.teacher_model.eval()
      self.student_model.train()
      loss_arr = []
      length_of_dataset = len(self.train_loader.dataset)
      best_acc = 0.0
      self.best_student_model_weights = deepcopy(
          self.student_model.state_dict()
      )
  
      print("\nTraining student...")
  
      for ep in range(epochs):
          epoch_loss = 0.0
          correct = 0
  
          for data, label in self.train_loader:
              data = data.to(self.device)
              label = label.to(self.device)
              if self.method in ("S", "TS"):
                  for i in range(int(self.noise_rate * data.shape[0])):
                      perturbation = random.randint(
                          0, data.shape[1] - 1
                      )
                      if perturbation != label[i]:
                          label[i] = perturbation
                      else:
                          try:
                              perturbation + 1 < data.shape[1]
                              label[i] = perturbation + 1
                          except:
                              label[i] = perturbation - 1
  
              student_out = self.student_model(data)
              teacher_out = self.teacher_model(data)
  
              loss = self.calculate_kd_loss(
                  student_out, teacher_out, label
              )
  
              if isinstance(student_out, tuple):
                  student_out = student_out[0]
  
              pred = student_out.argmax(dim=1, keepdim=True)
              correct += pred.eq(label.view_as(pred)).sum().item()
  
              self.optimizer_student.zero_grad()
              loss.backward()
              self.optimizer_student.step()
  
              epoch_loss += loss
  
          epoch_acc = correct / length_of_dataset
  
          epoch_val_acc = self.evaluate(teacher=False)
  
          if epoch_val_acc > best_acc:
              best_acc = epoch_val_acc
              self.best_student_model_weights = deepcopy(
                  self.student_model.state_dict()
              )
  
          if self.log:
              self.writer.add_scalar(
                  "Training loss/Student", epoch_loss, epochs
              )
              self.writer.add_scalar(
                  "Training accuracy/Student", epoch_acc, epochs
              )
              self.writer.add_scalar(
                  "Validation accuracy/Student", epoch_val_acc, epochs
              )
  
          loss_arr.append(epoch_loss)
          print(
              f"Epoch: {ep+1}, Loss: {epoch_loss}, Accuracy: {epoch_acc}"
          )
  
      self.student_model.load_state_dict(
          self.best_student_model_weights
      )
      if save_model:
          torch.save(self.student_model.state_dict(), save_model_pth)
      if plot_losses:
          plt.plot(loss_arr)
  
file_path: KD_Lib/KD_Lib/KD/vision/noisy/messy_collab.py
imports: import os
  import random
  from copy import deepcopy
  import matplotlib.pyplot
  import torch
  import torch.nn
  import torch.nn.functional
  from KD_Lib.KD.common import BaseClass
label_desc: Use image node_id 3 for calling the MessyCollab obj's underlying member method code with example usage: obj._train_student(epochs, plot_losses, save_model, save_model_pth) without return types