# Buggy code
def __transfer_data_to_device(self, batch, device, gpu_id=None):
    if device == "tpu" and XLA_AVAILABLE:
        # base case: object can be directly moved using `to`
        if callable(getattr(batch, "to", None)):
            return batch.to(xm.xla_device())

    if device == "gpu":
        # base case: object can be directly moved using `cuda` or `to`
        if callable(getattr(batch, "cuda", None)):
            return batch.cuda(gpu_id)

        if callable(getattr(batch, "to", None)):
            return batch.to(torch.device("cuda", gpu_id))

    # when list
    if isinstance(batch, list):
        for i, x in enumerate(batch):
            batch[i] = self.__transfer_data_to_device(x, device, gpu_id)
        return batch

    # when tuple
    if isinstance(batch, tuple):
        batch = list(batch)
        for i, x in enumerate(batch):
            batch[i] = self.__transfer_data_to_device(x, device, gpu_id)
        return tuple(batch)

    # when dict
    if isinstance(batch, dict):
        for k, v in batch.items():
            batch[k] = self.__transfer_data_to_device(v, device, gpu_id)

        return batch

    # nothing matches, return the value as is without transform
    return batch


# Traceback (AttributeError)
Traceback (most recent call last):
File "demo.py", line 48, in <module>
pl.Trainer(max_epochs=20, gpus=1).fit(module)
File "/home/n/repos/pytorch-lightning/pytorch_lightning/trainer/trainer.py", line 749, in fit
self.single_gpu_train(model)
File "/home/n/repos/pytorch-lightning/pytorch_lightning/trainer/distrib_parts.py", line 491, in single_gpu_train
self.run_pretrain_routine(model)
File "/home/n/repos/pytorch-lightning/pytorch_lightning/trainer/trainer.py", line 910, in run_pretrain_routine
self.train()
File "/home/n/repos/pytorch-lightning/pytorch_lightning/trainer/training_loop.py", line 384, in train
self.run_training_epoch()
File "/home/n/repos/pytorch-lightning/pytorch_lightning/trainer/training_loop.py", line 456, in run_training_epoch
_outputs = self.run_training_batch(batch, batch_idx)
File "/home/n/repos/pytorch-lightning/pytorch_lightning/trainer/training_loop.py", line 633, in run_training_batch
loss, batch_output = optimizer_closure()
File "/home/n/repos/pytorch-lightning/pytorch_lightning/trainer/training_loop.py", line 597, in optimizer_closure
output_dict = self.training_forward(split_batch, batch_idx, opt_idx, self.hiddens)
File "/home/n/repos/pytorch-lightning/pytorch_lightning/trainer/training_loop.py", line 770, in training_forward
output = self.model.training_step(*args)
File "demo.py", line 40, in training_step
yhat = self.forward(batch.x1, batch.x2)
AttributeError: 'tuple' object has no attribute 'x1'

# Fixed code
def __transfer_data_to_device(self, batch, device, gpu_id=None):
    if device == "tpu" and XLA_AVAILABLE:
        # base case: object can be directly moved using `to`
        if callable(getattr(batch, "to", None)):
            return batch.to(xm.xla_device())

    if device == "gpu":
        # base case: object can be directly moved using `cuda` or `to`
        if callable(getattr(batch, "cuda", None)):
            return batch.cuda(gpu_id)

        if callable(getattr(batch, "to", None)):
            return batch.to(torch.device("cuda", gpu_id))

    # when list
    if isinstance(batch, list):
        for i, x in enumerate(batch):
            batch[i] = self.__transfer_data_to_device(x, device, gpu_id)
        return batch

    # when tuple
    if isinstance(batch, tuple):
        # when namedtuple
        if hasattr(batch, "_fields"):
            elem_type = type(batch)
            return elem_type(
                *(self.__transfer_data_to_device(x, device, gpu_id) for x in batch)
            )
        else:
            batch = list(batch)
            for i, x in enumerate(batch):
                batch[i] = self.__transfer_data_to_device(x, device, gpu_id)
            return tuple(batch)

    # when dict
    if isinstance(batch, dict):
        for k, v in batch.items():
            batch[k] = self.__transfer_data_to_device(v, device, gpu_id)

        return batch

    # nothing matches, return the value as is without transform
    return batch