code: def fit_model(self, ctx, decryptor):
      batch_loader = DataLoader(
          dataset=None,
          ctx=ctx,
          batch_size=self.batch_size,
          mode="hetero",
          role="arbiter",
          sync_arbiter=True,
      )
      logger.info(f"batch_num={batch_loader.batch_num}")
      if self.optimizer.optimizer is None:
          optimizer_ready = False
      else:
          optimizer_ready = True
          # self.start_epoch = self.end_epoch + 1
  
      for i, iter_ctx in ctx.on_iterations.ctxs_range(self.epochs):
          iter_loss = None
          iter_g = None
          self.optimizer.set_iters(i)
          logger.info(f"self.optimizer set epoch {i}")
          for batch_ctx, _ in iter_ctx.on_batches.ctxs_zip(
              batch_loader
          ):
              g_guest_enc = batch_ctx.guest.get("g_enc")
              g_guest = decryptor.decrypt_tensor(g_guest_enc)
              size_list = [g_guest.size()[0]]
              g_total = g_guest.squeeze()
              # get torch tensor
  
              host_g = batch_ctx.hosts.get("g_enc")
              for i, g_host_enc in enumerate(host_g):
                  g = decryptor.decrypt_tensor(g_host_enc)
                  size_list.append(g.size()[0])
                  g_total = torch.hstack((g_total, g.squeeze()))
              if not optimizer_ready:
                  self.optimizer.init_optimizer(
                      model_parameter_length=sum(size_list),
                      dtype=g_total.dtype,
                  )
                  self.lr_scheduler.init_scheduler(
                      self.optimizer.optimizer
                  )
                  optimizer_ready = True
              self.optimizer.step(g_total.unsqueeze(1))
              delta_g = self.optimizer.get_delta_gradients()
              delta_g_list = separate(delta_g, size_list)
  
              delta_g_list_squeezed = []
              batch_ctx.guest.put("g", delta_g_list[0])
              delta_g_list_squeezed.append(delta_g_list[0].squeeze())
              for i, g_host in enumerate(delta_g_list[1:]):
                  batch_ctx.hosts[i].put("g", g_host)
                  delta_g_list_squeezed.append(g_host.squeeze())
              if iter_g is None:
                  iter_g = torch.hstack(delta_g_list_squeezed)
              else:
                  iter_g += torch.hstack(delta_g_list_squeezed)
  
              if len(host_g) == 1:
                  loss = decryptor.decrypt_tensor(
                      batch_ctx.guest.get("loss")
                  )
                  iter_loss = 0 if iter_loss is None else iter_loss
                  iter_loss += loss
              else:
                  logger.info(
                      "Multiple hosts exist, do not compute loss."
                  )
  
          if iter_loss is not None:
              iter_ctx.metrics.log_loss(
                  "linr_loss", iter_loss.tolist()[0]
              )
          if self.early_stop == "weight_diff":
              self.is_converged = self.converge_func.is_converge(iter_g)
          else:
              if iter_loss is None:
                  raise ValueError(
                      "Multiple host situation, loss early stop function is not available."
                      "You should use 'weight_diff' instead"
                  )
              self.is_converged = self.converge_func.is_converge(
                  iter_loss
              )
  
          iter_ctx.hosts.put("converge_flag", self.is_converged)
          iter_ctx.guest.put("converge_flag", self.is_converged)
  
          if self.is_converged:
              self.end_epoch = i
              break
          if i < self.epochs - 1:
              self.lr_scheduler.step()
      if not self.is_converged:
          self.end_epoch = self.epochs
      logger.debug(f"Finish training at {self.end_epoch}th epoch.")
  
imports: import logging
  import torch
  from fate.arch import Context
  from fate.arch.dataframe import DataLoader
  from fate.ml.abc.module import HeteroModule
  from fate.ml.utils._convergence import converge_func_factory
  from fate.ml.utils._optimizer import LRScheduler, Optimizer, separate
label_desc: Use image node_id 2 for calling the HeteroLinREstimatorArbiter obj's underlying member method code with example usage: obj.fit_model(ctx, decryptor) without return types