text: from typing import Optional, Tuple
  
  from pytorch_lightning import LightningDataModule
  from torch.utils.data import ConcatDataset, DataLoader, Dataset
  import numpy as np
  import scipy.io as sio
  import torch
  from sklearn import preprocessing
  
  def read_matdataset(self, opt):
          matcontent = sio.loadmat(opt.dataroot + "/" + opt.dataset + "/" + opt.image_embedding + ".mat")
          feature = matcontent['features'].T
          label = matcontent['labels'].astype(int).squeeze() - 1
          matcontent = sio.loadmat(opt.dataroot + "/" + opt.dataset + "/" + opt.class_embedding + "_splits.mat")
          trainval_loc = matcontent['trainval_loc'].squeeze() - 1
          train_loc = matcontent['train_loc'].squeeze() - 1
          val_unseen_loc = matcontent['val_loc'].squeeze() - 1
          test_seen_loc = matcontent['test_seen_loc'].squeeze() - 1
          test_unseen_loc = matcontent['test_unseen_loc'].squeeze() - 1    
  
          self.attribute = torch.from_numpy(matcontent['att'].T).float()
          self.attribute /= self.attribute.pow(2).sum(1).sqrt().unsqueeze(1).expand(self.attribute.size(0),self.attribute.size(1))
  
          if not opt.validation:
              if opt.preprocessing:
                  if opt.standardization:
                      print('standardization...')
                      scaler = preprocessing.StandardScaler()
                  else:
                      scaler = preprocessing.MinMaxScaler()
                  
                  _train_feature = scaler.fit_transform(feature[trainval_loc])
                  _test_seen_feature = scaler.transform(feature[test_seen_loc])
                  _test_unseen_feature = scaler.transform(feature[test_unseen_loc])
                  self.train_feature = torch.from_numpy(_train_feature).float()
                  mx = self.train_feature.max()
                  self.train_feature.mul_(1/mx)
                  self.train_label = torch.from_numpy(label[trainval_loc]).long() 
                  self.test_unseen_feature = torch.from_numpy(_test_unseen_feature).float()
                  self.test_unseen_feature.mul_(1/mx)
                  self.test_unseen_label = torch.from_numpy(label[test_unseen_loc]).long() 
                  self.test_seen_feature = torch.from_numpy(_test_seen_feature).float() 
                  self.test_seen_feature.mul_(1/mx)
                  self.test_seen_label = torch.from_numpy(label[test_seen_loc]).long()
  
              else:
                  self.train_feature = torch.from_numpy(feature[trainval_loc]).float()
                  self.train_label = torch.from_numpy(label[trainval_loc]).long() 
                  self.test_unseen_feature = torch.from_numpy(feature[test_unseen_loc]).float()
                  self.test_unseen_label = torch.from_numpy(label[test_unseen_loc]).long() 
                  self.test_seen_feature = torch.from_numpy(feature[test_seen_loc]).float() 
                  self.test_seen_label = torch.from_numpy(label[test_seen_loc]).long()
          else:
              self.train_feature = torch.from_numpy(feature[train_loc]).float()
              self.train_label = torch.from_numpy(label[train_loc]).long()
              self.test_unseen_feature = torch.from_numpy(feature[val_unseen_loc]).float()
              self.test_unseen_label = torch.from_numpy(label[val_unseen_loc]).long() 
      
          self.seenclasses = torch.from_numpy(np.unique(self.train_label.numpy()))
          self.unseenclasses = torch.from_numpy(np.unique(self.test_unseen_label.numpy()))
  
  
          self.ntrain = self.train_feature.size()[0]
          self.ntest_seen = self.test_seen_feature.size()[0]
          self.ntest_unseen = self.test_unseen_feature.size()[0]
          self.ntrain_class = self.seenclasses.size(0)
          self.ntest_class = self.unseenclasses.size(0)
          self.train_class = self.seenclasses.clone()
          self.allclasses = torch.arange(0, self.ntrain_class+self.ntest_class).long()
          self.train_mapped_label = map_label(self.train_label, self.seenclasses) 
  
  class ZSLDataModule(LightningDataModule):
      """
      Example of LightningDataModule for MNIST dataset.
  
      A DataModule implements 5 key methods:
          - prepare_data (things to do on 1 GPU/TPU, not on every GPU/TPU in distributed mode)
          - setup (things to do on every accelerator in distributed mode)
          - train_dataloader (the training dataloader)
          - val_dataloader (the validation dataloader(s))
          - test_dataloader (the test dataloader(s))
  
      This allows you to share a full dataset without explaining how to download,
      split, transform and process the data.
  
      Read the docs:
          https://pytorch-lightning.readthedocs.io/en/latest/extensions/datamodules.html
      """
  
      def __init__(
          self,
          data_dir: str = "",
          dataset: str = 'AWA2',
          image_embedding: str = 'res101',
          class_embedding: str = 'att',
          preprocessing: bool = True,
          standardization: bool = False,
          validation: bool = False,
          num_workers: int = 0,
          pin_memory: bool = False,
          batch_size: int = 64
      ):
          super().__init__()
  
          self.data_dir = data_dir
          self.batch_size = batch_size
          self.num_workers = num_workers
          self.pin_memory = pin_memory
          self.dataset=dataset
          self.image_embedding=image_embedding
          self.class_embedding=class_embedding
          self.preprocessing=preprocessing
          self.standardization=standardization
          self.validation=validation
          
          self.data_train: Optional[Dataset] = None
          self.data_val: Optional[Dataset] = None
          self.data_test: Optional[Dataset] = None
  
      
      def setup(self, stage: Optional[str] = None):
          """Load data. Set variables: self.data_train, self.data_val, self.data_test."""
          trainset = MNIST(self.data_dir, train=True, transform=self.transforms)
          testset = MNIST(self.data_dir, train=False, transform=self.transforms)
          dataset = ConcatDataset(datasets=[trainset, testset])
          self.data_train, self.data_val, self.data_test = random_split(
              dataset, self.train_val_test_split
          )
  
      def train_dataloader(self):
          return DataLoader(
              dataset=self.data_train,
              batch_size=self.batch_size,
              num_workers=self.num_workers,
              pin_memory=self.pin_memory,
              shuffle=True,
          )
  
      def val_dataloader(self):
          return DataLoader(
              dataset=self.data_val,
              batch_size=self.batch_size,
              num_workers=self.num_workers,
              pin_memory=self.pin_memory,
              shuffle=False,
          )
  
      def test_dataloader(self):
          return DataLoader(
              dataset=self.data_test,
              batch_size=self.batch_size,
              num_workers=self.num_workers,
              pin_memory=self.pin_memory,
              shuffle=False,
          )
  