Skip to content

datamodule

MEDSDataModule

Bases: LightningDataModule, Module

LightningDataModule for the MEDS pytorch dataset.

TODO: Add documentation

A LightningDataModule implements 7 key methods:

```python def prepare_data(self): # Things to do on 1 GPU/TPU (not on every GPU/TPU in DDP). # Pre-process and loading data, save to disk, etc…

def setup(self, stage): # Things to do on every process in DDP. # Load data, set variables, etc…

def train_dataloader(self): # return train dataloader

def val_dataloader(self): # return validation dataloader

def test_dataloader(self): # return test dataloader

def predict_dataloader(self): # return predict dataloader

def teardown(self, stage): # Called on every process in DDP. # Clean up after fit or test. ```

This allows you to share a full dataset without explaining how to download, split, transform and process the data.

Read the docs: https://lightning.ai/docs/pytorch/latest/data/datamodule.html

Source code in meds_torch/data/datamodule.py
class MEDSDataModule(LightningDataModule, Module):
    """`LightningDataModule` for the MEDS pytorch dataset.

    TODO: Add documentation

    A `LightningDataModule` implements 7 key methods:

    ```python     def prepare_data(self):     # Things to do on 1 GPU/TPU (not on every GPU/TPU in DDP).     #
    Pre-process and loading data, save to disk, etc...

    def setup(self, stage): # Things to do on every process in DDP. # Load data, set variables, etc...

    def train_dataloader(self): # return train dataloader

    def val_dataloader(self): # return validation dataloader

    def test_dataloader(self): # return test dataloader

    def predict_dataloader(self): # return predict dataloader

    def teardown(self, stage):     # Called on every process in DDP.     # Clean up after fit or test. ```

    This allows you to share a full dataset without explaining how to download, split, transform and process
    the data.

    Read the docs:
    https://lightning.ai/docs/pytorch/latest/data/datamodule.html
    """

    def __init__(
        self,
        cfg: DictConfig = None,
    ) -> None:
        """Initialize a `MEDSDataModule`."""
        super().__init__()
        self.cfg = cfg

        # this line allows to access init params with 'self.hparams' attribute
        # also ensures init params will be stored in ckpt
        self.save_hyperparameters(logger=False)

        self.data_train: Dataset | None = None
        self.data_val: Dataset | None = None
        self.data_test: Dataset | None = None

    @property
    def num_classes(self) -> int:
        """Get the number of classes.

        :return: The number of MNIST classes (10).
        """
        return 10

    def prepare_data(self) -> None:
        """Download data if needed. Lightning ensures that `self.prepare_data()` is
        called only within a single process on CPU, so you can safely add your
        downloading logic within. In case of multi-node training, the execution of this
        hook depends upon `self.prepare_data_per_node()`.

        Do not use it to assign state (self.x = y).
        """

    def setup(self, stage: str | None = None) -> None:
        """Load data. Set variables: `self.data_train`, `self.data_val`,
        `self.data_test`.

        This method is called by Lightning before `trainer.fit()`, `trainer.validate()`, `trainer.test()`, and
        `trainer.predict()`, so be careful not to execute things like random split twice! Also, it is called
        after `self.prepare_data()` and there is a barrier in between which ensures that all the processes
        proceed to `self.setup()` once the data is prepared and available for use.

        :param stage: The stage to setup. Either `"fit"`, `"validate"`, `"test"`, or `"predict"`. Defaults to
            ``None``.
        """
        # Divide batch size by the number of devices.
        if self.trainer is not None:
            if self.hparams.cfg.dataloader.batch_size % self.trainer.world_size != 0:
                raise RuntimeError(
                    f"Batch size ({self.hparams.cfg.dataloader.batch_size}) is not divisible by "
                    f"the number of devices ({self.trainer.world_size})."
                )
            self.batch_size_per_device = self.hparams.cfg.dataloader.batch_size // self.trainer.world_size

        # load and split datasets only if not loaded already
        if stage == "test":
            self.data_test = get_dataset(self.cfg, split=self.cfg.split_names.test)
        elif stage == "validate":
            self.data_val = get_dataset(self.cfg, split=self.cfg.split_names.validate)
        else:
            self.data_train = get_dataset(self.cfg, split=self.cfg.split_names.train)
            self.data_val = get_dataset(self.cfg, split=self.cfg.split_names.validate)
            self.data_test = get_dataset(self.cfg, split=self.cfg.split_names.test)

    def train_dataloader(self) -> DataLoader[Any]:
        """Create and return the train dataloader.

        :return: The train dataloader.
        """
        return DataLoader(
            dataset=self.data_train,
            shuffle=True,
            collate_fn=self.data_train.collate,
            drop_last=True,
            **self.cfg.dataloader,
        )

    def val_dataloader(self) -> DataLoader[Any]:
        """Create and return the validation dataloader.

        :return: The validation dataloader.
        """
        return DataLoader(
            dataset=self.data_val,
            shuffle=False,
            collate_fn=self.data_val.collate,
            **self.cfg.dataloader,
        )

    def test_dataloader(self) -> DataLoader[Any]:
        """Create and return the test dataloader.

        :return: The test dataloader.
        """
        return DataLoader(
            dataset=self.data_test,
            shuffle=False,
            collate_fn=self.data_test.collate,
            **self.cfg.dataloader,
        )

    def predict_dataloader(self) -> DataLoader[Any]:
        """Create and return the predict dataloader.

        :return: The predict dataloader.
        """
        if self.cfg.predict_dataset == "train":
            return self.train_dataloader()
        elif self.cfg.predict_dataset == "val":
            return self.val_dataloader()
        elif self.cfg.predict_dataset == "test":
            return self.test_dataloader()
        else:
            raise NotImplementedError(
                f"{self.cfg.predict_dataset} not implemented! Use 'train', 'val', or 'test'."
            )

    def state_dict(self) -> dict[Any, Any]:
        """Called when saving a checkpoint. Implement to generate and save the
        datamodule state.

        :return: A dictionary containing the datamodule state that you want to save.
        """
        return {}

    def load_state_dict(self, state_dict: dict[str, Any]) -> None:
        """Called when loading a checkpoint. Implement to reload datamodule state given
        datamodule `state_dict()`.

        :param state_dict: The datamodule state returned by `self.state_dict()`.
        """

num_classes: int property

Get the number of classes.

:return: The number of MNIST classes (10).

__init__(cfg=None)

Initialize a MEDSDataModule.

Source code in meds_torch/data/datamodule.py
def __init__(
    self,
    cfg: DictConfig = None,
) -> None:
    """Initialize a `MEDSDataModule`."""
    super().__init__()
    self.cfg = cfg

    # this line allows to access init params with 'self.hparams' attribute
    # also ensures init params will be stored in ckpt
    self.save_hyperparameters(logger=False)

    self.data_train: Dataset | None = None
    self.data_val: Dataset | None = None
    self.data_test: Dataset | None = None

load_state_dict(state_dict)

Called when loading a checkpoint. Implement to reload datamodule state given datamodule state_dict().

:param state_dict: The datamodule state returned by self.state_dict().

Source code in meds_torch/data/datamodule.py
def load_state_dict(self, state_dict: dict[str, Any]) -> None:
    """Called when loading a checkpoint. Implement to reload datamodule state given
    datamodule `state_dict()`.

    :param state_dict: The datamodule state returned by `self.state_dict()`.
    """

predict_dataloader()

Create and return the predict dataloader.

:return: The predict dataloader.

Source code in meds_torch/data/datamodule.py
def predict_dataloader(self) -> DataLoader[Any]:
    """Create and return the predict dataloader.

    :return: The predict dataloader.
    """
    if self.cfg.predict_dataset == "train":
        return self.train_dataloader()
    elif self.cfg.predict_dataset == "val":
        return self.val_dataloader()
    elif self.cfg.predict_dataset == "test":
        return self.test_dataloader()
    else:
        raise NotImplementedError(
            f"{self.cfg.predict_dataset} not implemented! Use 'train', 'val', or 'test'."
        )

prepare_data()

Download data if needed. Lightning ensures that self.prepare_data() is called only within a single process on CPU, so you can safely add your downloading logic within. In case of multi-node training, the execution of this hook depends upon self.prepare_data_per_node().

Do not use it to assign state (self.x = y).

Source code in meds_torch/data/datamodule.py
def prepare_data(self) -> None:
    """Download data if needed. Lightning ensures that `self.prepare_data()` is
    called only within a single process on CPU, so you can safely add your
    downloading logic within. In case of multi-node training, the execution of this
    hook depends upon `self.prepare_data_per_node()`.

    Do not use it to assign state (self.x = y).
    """

setup(stage=None)

Load data. Set variables: self.data_train, self.data_val, self.data_test.

This method is called by Lightning before trainer.fit(), trainer.validate(), trainer.test(), and trainer.predict(), so be careful not to execute things like random split twice! Also, it is called after self.prepare_data() and there is a barrier in between which ensures that all the processes proceed to self.setup() once the data is prepared and available for use.

:param stage: The stage to setup. Either "fit", "validate", "test", or "predict". Defaults to None.

Source code in meds_torch/data/datamodule.py
def setup(self, stage: str | None = None) -> None:
    """Load data. Set variables: `self.data_train`, `self.data_val`,
    `self.data_test`.

    This method is called by Lightning before `trainer.fit()`, `trainer.validate()`, `trainer.test()`, and
    `trainer.predict()`, so be careful not to execute things like random split twice! Also, it is called
    after `self.prepare_data()` and there is a barrier in between which ensures that all the processes
    proceed to `self.setup()` once the data is prepared and available for use.

    :param stage: The stage to setup. Either `"fit"`, `"validate"`, `"test"`, or `"predict"`. Defaults to
        ``None``.
    """
    # Divide batch size by the number of devices.
    if self.trainer is not None:
        if self.hparams.cfg.dataloader.batch_size % self.trainer.world_size != 0:
            raise RuntimeError(
                f"Batch size ({self.hparams.cfg.dataloader.batch_size}) is not divisible by "
                f"the number of devices ({self.trainer.world_size})."
            )
        self.batch_size_per_device = self.hparams.cfg.dataloader.batch_size // self.trainer.world_size

    # load and split datasets only if not loaded already
    if stage == "test":
        self.data_test = get_dataset(self.cfg, split=self.cfg.split_names.test)
    elif stage == "validate":
        self.data_val = get_dataset(self.cfg, split=self.cfg.split_names.validate)
    else:
        self.data_train = get_dataset(self.cfg, split=self.cfg.split_names.train)
        self.data_val = get_dataset(self.cfg, split=self.cfg.split_names.validate)
        self.data_test = get_dataset(self.cfg, split=self.cfg.split_names.test)

state_dict()

Called when saving a checkpoint. Implement to generate and save the datamodule state.

:return: A dictionary containing the datamodule state that you want to save.

Source code in meds_torch/data/datamodule.py
def state_dict(self) -> dict[Any, Any]:
    """Called when saving a checkpoint. Implement to generate and save the
    datamodule state.

    :return: A dictionary containing the datamodule state that you want to save.
    """
    return {}

test_dataloader()

Create and return the test dataloader.

:return: The test dataloader.

Source code in meds_torch/data/datamodule.py
def test_dataloader(self) -> DataLoader[Any]:
    """Create and return the test dataloader.

    :return: The test dataloader.
    """
    return DataLoader(
        dataset=self.data_test,
        shuffle=False,
        collate_fn=self.data_test.collate,
        **self.cfg.dataloader,
    )

train_dataloader()

Create and return the train dataloader.

:return: The train dataloader.

Source code in meds_torch/data/datamodule.py
def train_dataloader(self) -> DataLoader[Any]:
    """Create and return the train dataloader.

    :return: The train dataloader.
    """
    return DataLoader(
        dataset=self.data_train,
        shuffle=True,
        collate_fn=self.data_train.collate,
        drop_last=True,
        **self.cfg.dataloader,
    )

val_dataloader()

Create and return the validation dataloader.

:return: The validation dataloader.

Source code in meds_torch/data/datamodule.py
def val_dataloader(self) -> DataLoader[Any]:
    """Create and return the validation dataloader.

    :return: The validation dataloader.
    """
    return DataLoader(
        dataset=self.data_val,
        shuffle=False,
        collate_fn=self.data_val.collate,
        **self.cfg.dataloader,
    )