class_docstr: Multiple Feature Spaces Adaptation Network (MFSAN)
  
  Reference: Zhu, Y., Zhuang, F. and Wang, D., 2019, July. Aligning domain-specific distribution and classifier
      for cross-domain classification from multiple sources. In AAAI.
      https://ojs.aaai.org/index.php/AAAI/article/view/4551
  
  Original implementation: https://github.com/easezyc/deep-transfer-learning/tree/master/MUDA/MFSAN
code: def compute_loss(self, batch, split_name="valid"):
      x, y, domain_labels = batch
      phi_x = self.forward(x)
      tgt_idx = torch.where(domain_labels == self.target_label)[0]
      n_src = len(self.src_domains)
      domain_dist = 0
      loss_cls = 0
      ok_src = []
      for src_domain in self.src_domains:
          src_domain_idx = torch.where(
              domain_labels == self.domain_to_idx[src_domain]
          )[0]
          phi_src = self.domain_net[src_domain].forward(
              phi_x[src_domain_idx]
          )
          phi_tgt = self.domain_net[src_domain].forward(phi_x[tgt_idx])
          kernels = losses.gaussian_kernel(
              phi_src,
              phi_tgt,
              kernel_mul=self._kernel_mul,
              kernel_num=self._kernel_num,
          )
          domain_dist += losses.compute_mmd_loss(kernels, len(phi_src))
          y_src_hat = self.classifiers[src_domain](phi_src)
          loss_cls_, ok_src_ = losses.cross_entropy_logits(
              y_src_hat, y[src_domain_idx]
          )
          loss_cls += loss_cls_
          ok_src.append(ok_src_)
  
      domain_dist += self.cls_discrepancy(phi_x[tgt_idx])
      loss_cls = loss_cls / n_src
      ok_src = torch.cat(ok_src)
  
      y_tgt_hat = self._get_avg_cls_output(phi_x[tgt_idx])
      _, ok_tgt = losses.cross_entropy_logits(y_tgt_hat, y[tgt_idx])
  
      ok_src = ok_src.double().mean()
      ok_tgt = ok_tgt.double().mean()
      task_loss = loss_cls
  
      log_metrics = {
          f"{split_name}_source_acc": ok_src,
          f"{split_name}_target_acc": ok_tgt,
          f"{split_name}_domain_acc": domain_dist,
      }
  
      return task_loss, domain_dist, log_metrics
  
imports: import torch
  import torch.nn
  from sklearn.base import BaseEstimator, ClassifierMixin
  from sklearn.metrics.pairwise import pairwise_kernels
  from sklearn.preprocessing import LabelBinarizer
  from torch.linalg import multi_dot
  from torch.nn.functional import one_hot
  import kale.predict.losses
  from kale.embed.image_cnn import _Bottleneck
  from kale.pipeline.domain_adapter import BaseAdaptTrainer
label_desc: Use image node_id 2 for calling the MFSANTrainer obj's underlying member method code with example usage: obj.compute_loss(batch, split_name) and returns: task_loss,  domain_dist,  log_metrics