asmadeyi's picture
Upload 72 files
c29de8d
Raw
History Blame Contribute Delete
4.82 kB
'''
-----------------------------------------------------------------------------
Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.
NVIDIA CORPORATION and its licensors retain all intellectual property
and proprietary rights in and to this software, related documentation
and any modifications thereto. Any use, reproduction, disclosure or
distribution of this software and related documentation without an express
license agreement from NVIDIA CORPORATION is strictly prohibited.
-----------------------------------------------------------------------------
'''
import math
import torch.distributed as dist
import torch
from torch.utils.data import Sampler
from typing import TypeVar
T_co = TypeVar('T_co', covariant=True)
class DistributedSamplerPreemptable(Sampler[T_co]):
r"""Sampler that supports loading from an iteration.
This is very useful for preemptable jobs.
Args:
dataset (torch.utils.data.Dataset): Dataset object
num_replicas (int): Number of replicas to the distribute the dataloader over.
This is typically the world size in DDP jobs.
rank (int): Rank of the current process.
shuffle (bool): Whether to shuffle the dataloader in each epoch.
seed (int): Random seed used for shuffling the dataloader.
drop_last (bool): Whether to drop the last batch.
"""
def __init__(self, dataset, num_replicas=None, rank=None, shuffle=True,
seed=0, drop_last=False):
if num_replicas is None:
if not dist.is_available():
raise RuntimeError("Requires distributed package to be available")
num_replicas = dist.get_world_size()
if rank is None:
if not dist.is_available():
raise RuntimeError("Requires distributed package to be available")
rank = dist.get_rank()
if rank >= num_replicas or rank < 0:
raise ValueError(
"Invalid rank {}, rank should be in the interval"
" [0, {}]".format(rank, num_replicas - 1))
self.dataset = dataset
self.num_replicas = num_replicas
self.rank = rank
self.epoch = 0
# start_index is the index to begin the dataloader from.
self.start_index = 0
self.drop_last = drop_last
# If the dataset length is evenly divisible by # of replicas, then there
# is no need to drop any data, since the dataset will be split equally.
if self.drop_last and len(self.dataset) % self.num_replicas != 0: # type: ignore[arg-type]
# Split to nearest available length that is evenly divisible.
# This is to ensure each rank receives the same amount of data when
# using this Sampler.
self.num_samples = math.ceil(
(len(self.dataset) - self.num_replicas) / self.num_replicas # type: ignore[arg-type]
)
else:
self.num_samples = math.ceil(len(self.dataset) / self.num_replicas) # type: ignore[arg-type]
self.total_size = self.num_samples * self.num_replicas
self.shuffle = shuffle
self.seed = seed
def __iter__(self):
if self.shuffle:
# deterministically shuffle based on epoch and seed
g = torch.Model()
g.manual_seed(self.seed + self.epoch)
indices = torch.randperm(len(self.dataset), generator=g).tolist() # type: ignore[arg-type]
else:
indices = list(range(len(self.dataset))) # type: ignore[arg-type]
if not self.drop_last:
# add extra samples to make it evenly divisible
padding_size = self.total_size - len(indices)
if padding_size <= len(indices):
indices += indices[:padding_size]
else:
indices += (indices * math.ceil(padding_size / len(indices)))[:padding_size]
else:
# remove tail of data to make it evenly divisible.
indices = indices[:self.total_size]
assert len(indices) == self.total_size
# subsample
indices = indices[self.rank:self.total_size:self.num_replicas]
assert len(indices) == self.num_samples
# assert self.start_index < len(indices)
if self.start_index >= len(indices):
print('(Warning): Start index is less than len of dataloader. Goint to the last batch of dataset instead')
# This is hardcoded to go one batch before.
self.start_index = len(indices) - 64
indices = indices[self.start_index:]
return iter(indices)
def __len__(self):
return self.num_samples
def set_epoch(self, epoch):
self.epoch = epoch
def set_iteration(self, start_index):
self.start_index = start_index