docs: def tensors_to(tensors, *args, **kwargs):
      """ Apply ``torch.Tensor.to`` to tensors in a generic data structure.
  
      Inspired by:
      https://github.com/pytorch/pytorch/blob/master/torch/utils/data/_utils/collate.py#L31
  
      Args:
          tensors (tensor, dict, list, namedtuple or tuple): Data structure with tensor values to
              move.
          *args: Arguments passed to ``torch.Tensor.to``.
          **kwargs: Keyword arguments passed to ``torch.Tensor.to``.
  
      Example use case:
          This is useful as a complementary function to ``collate_tensors``. Following collating,
          it's important to move your tensors to the appropriate device.
  
      Returns:
          The inputted ``tensors`` with ``torch.Tensor.to`` applied.
  
      Example:
  
          >>> import torch
          >>> batch = [
          ...   { 'column_a': torch.randn(5), 'column_b': torch.randn(5) },
          ...   { 'column_a': torch.randn(5), 'column_b': torch.randn(5) },
          ... ]
          >>> tensors_to(batch, torch.device('cpu'))  # doctest: +ELLIPSIS
          [{'column_a': tensor(...}]
      """
      if torch.is_tensor(tensors):
          return tensors.to(*args, **kwargs)
      elif isinstance(tensors, dict):
          return {k: tensors_to(v, *args, **kwargs) for k, v in tensors.items()}
      elif hasattr(tensors, '_asdict') and isinstance(tensors, tuple):  # Handle ``namedtuple``
          return tensors.__class__(**tensors_to(tensors._asdict(), *args, **kwargs))
      elif isinstance(tensors, list):
          return [tensors_to(t, *args, **kwargs) for t in tensors]
      elif isinstance(tensors, tuple):
          return tuple([tensors_to(t, *args, **kwargs) for t in tensors])
      else:
          return tensors