The role of the dataloader is to generate batches (and transforms if you’ve passed those) in parallel on the CPU from the dataset and that’s all. It gives you batches of data to iterate over which is roughly equal to number_of_sample/batch_size. How many times you loop over it depends on how you’ve written your training logic. However, in DL when we iterate over all the samples once it is called a single epoch. So if you have n epochs your dataset will be iterated n times using the batches generated by the dataloader.
This is an awesome tutorial on Custom Datasets:

  
      pytorch.org
  
  
    

Writing Custom Datasets, DataLoaders and Transforms — PyTorch Tutorials 1.7.0... 47



  
  
    
    
  
  


To give you some direction, I’ve written some inheritance logic.
from torchvision.datasets import CocoDetection


class CustomDataset(CocoDetection):
    def __init__(self,
                 root,
                 annFile,
                 transform=None,
                 target_transform=None) -> None:
        super().__init__(root, annFile, transform, target_transform)
        self.ids = [
            "A list of all the file names which satisfy your criteria "
        ]
        # You can get the above list by applying your filtering logic to
        # this list :list(self.coco.imgs.keys()) So this would only be have
        # to be done only once.
        # Save it to a text file. This file will now contain the names of
        # images that match your criteria
        # Load that file contents in the init function into self.ids
        # the length would automatically be correct

    def __getitem__(self, index: int):
        img, target = super().__getitem__(index)
        # do whatever you want
        return img, target


Hope this helps!