code: def fit(
      self,
      epochs,
      lr,
      validate=True,
      schedule_type="warmup_cosine",
      optimizer_type="lamb",
  ):
      tensorboard_dir = self.output_dir / "tensorboard"
      tensorboard_dir.mkdir(exist_ok=True)
  
      # Train the model
      tb_writer = SummaryWriter(tensorboard_dir)
  
      train_dataloader = self.data.train_dl
      if self.max_steps > 0:
          t_total = self.max_steps
          self.epochs = (
              self.max_steps
              // len(train_dataloader)
              // self.grad_accumulation_steps
              + 1
          )
      else:
          t_total = (
              len(train_dataloader)
              // self.grad_accumulation_steps
              * epochs
          )
  
      # Prepare optimiser
      optimizer = self.get_optimizer(lr, optimizer_type=optimizer_type)
  
      # get the base model if its already wrapped around DataParallel
      if hasattr(self.model, "module"):
          self.model = self.model.module
  
      if self.is_fp16:
          try:
              from apex import amp
          except ImportError:
              raise ImportError(
                  "Please install apex to use fp16 training"
              )
          self.model, optimizer = amp.initialize(
              self.model, optimizer, opt_level=self.fp16_opt_level
          )
  
      # Get scheduler
      scheduler = self.get_scheduler(
          optimizer, t_total=t_total, schedule_type=schedule_type
      )
  
      # Parallelize the model architecture
      if self.multi_gpu == True:
          self.model = torch.nn.DataParallel(self.model)
  
      # Start Training
      self.logger.info("***** Running training *****")
      self.logger.info(
          "  Num examples = %d", len(train_dataloader.dataset)
      )
      self.logger.info("  Num Epochs = %d", epochs)
      self.logger.info(
          "  Total train batch size (w. parallel, distributed & accumulation) = %d",
          self.data.train_batch_size * self.grad_accumulation_steps,
      )
      self.logger.info(
          "  Gradient Accumulation steps = %d",
          self.grad_accumulation_steps,
      )
      self.logger.info("  Total optimization steps = %d", t_total)
  
      global_step = 0
      epoch_step = 0
      tr_loss, logging_loss, epoch_loss = 0.0, 0.0, 0.0
      self.model.zero_grad()
      pbar = master_bar(range(epochs))
  
      for epoch in pbar:
          epoch_step = 0
          epoch_loss = 0.0
          for step, batch in enumerate(
              progress_bar(train_dataloader, parent=pbar)
          ):
              self.model.train()
              batch = tuple(t.to(self.device) for t in batch)
              inputs = {
                  "input_ids": batch[0],
                  "attention_mask": batch[1],
                  "token_type_ids": None
                  if self.model_type == "xlm"
                  else batch[2],
                  "start_positions": batch[3],
                  "end_positions": batch[4],
              }
  
              if self.model_type in ["xlnet", "xlm"]:
                  inputs.update(
                      {"cls_index": batch[5], "p_mask": batch[6]}
                  )
  
              outputs = self.model(**inputs)
              loss = outputs[
                  0
              ]  # model outputs are always tuple in pytorch-transformers (see doc)
  
              if self.n_gpu > 1:
                  loss = (
                      loss.mean()
                  )  # mean() to average on multi-gpu parallel training
              if self.grad_accumulation_steps > 1:
                  loss = loss / self.grad_accumulation_steps
  
              if self.is_fp16:
                  with amp.scale_loss(loss, optimizer) as scaled_loss:
                      scaled_loss.backward()
                  torch.nn.utils.clip_grad_norm_(
                      amp.master_params(optimizer), self.max_grad_norm
                  )
              else:
                  loss.backward()
                  torch.nn.utils.clip_grad_norm_(
                      self.model.parameters(), self.max_grad_norm
                  )
  
              tr_loss += loss.item()
              epoch_loss += loss.item()
              if (step + 1) % self.grad_accumulation_steps == 0:
                  optimizer.step()
                  scheduler.step()
  
                  self.model.zero_grad()
                  global_step += 1
                  epoch_step += 1
  
                  if (
                      self.logging_steps > 0
                      and global_step % self.logging_steps == 0
                  ):
                      if validate:
                          # evaluate model
                          results = self.validate()
                          for key, value in results.items():
                              tb_writer.add_scalar(
                                  "eval_{}".format(key),
                                  value,
                                  global_step,
                              )
                              self.logger.info(
                                  "eval_{} after step {}: {}: ".format(
                                      key, global_step, value
                                  )
                              )
  
                      # Log metrics
                      self.logger.info(
                          "lr after step {}: {}".format(
                              global_step, scheduler.get_lr()[0]
                          )
                      )
                      self.logger.info(
                          "train_loss after step {}: {}".format(
                              global_step,
                              (tr_loss - logging_loss)
                              / self.logging_steps,
                          )
                      )
                      tb_writer.add_scalar(
                          "lr", scheduler.get_lr()[0], global_step
                      )
                      tb_writer.add_scalar(
                          "loss",
                          (tr_loss - logging_loss) / self.logging_steps,
                          global_step,
                      )
  
                      logging_loss = tr_loss
  
          # Evaluate the model after every epoch
          if validate:
              results = self.validate("epoch_{}".format(epoch + 1))
              for key, value in results.items():
                  self.logger.info(
                      "eval_{} after epoch {}: {}: ".format(
                          key, (epoch + 1), value
                      )
                  )
  
          # Log metrics
          self.logger.info(
              "lr after epoch {}: {}".format(
                  (epoch + 1), scheduler.get_lr()[0]
              )
          )
          self.logger.info(
              "train_loss after epoch {}: {}".format(
                  (epoch + 1), epoch_loss / epoch_step
              )
          )
          self.logger.info("\n")
  
      tb_writer.close()
      return global_step, tr_loss / global_step
  
imports: import os
  import torch
  import pandas
  import numpy
  from pathlib import Path
  import collections
  import math
  from io import open
  import json
  from .data_qa import BertQADataBunch
  from .learner_util import Learner
  from fastprogress.fastprogress import master_bar, progress_bar
  from tensorboardX import SummaryWriter
  from transformers.tokenization_bert import BasicTokenizer, whitespace_tokenize
  from transformers import BertConfig, BertForQuestionAnswering, XLNetConfig, XLNetForQuestionAnswering, XLMConfig, XLMForQuestionAnswering, DistilBertConfig, DistilBertForQuestionAnswering
  from .utils_squad_evaluate import EVAL_OPTS, main
label_desc: Use image node_id 3 for calling the BertQALearner obj's underlying member method code with example usage: obj.fit(epochs, lr, validate, schedule_type, optimizer_type) and returns: global_step,  unknown