[SGD] Dataset API (#7839)

This commit is contained in:
Alex Wu
2020-06-01 15:48:15 -07:00
committed by GitHub
parent 21d5b49c56
commit dcf58a43dc
11 changed files with 344 additions and 67 deletions
+19 -4
View File
@@ -138,7 +138,9 @@ class TorchRunner:
def setup_components(self):
"""Runs the creator functions without any distributed coordination."""
logger.debug("Loading data.")
self._initialize_dataloaders()
if self.data_creator and callable(self.data_creator):
self._initialize_dataloaders()
logger.debug("Creating model")
self.models = self.model_creator(self.config)
if not isinstance(self.models, Iterable):
@@ -181,7 +183,11 @@ class TorchRunner:
"""Finds a free port on the current node."""
return utils.find_free_port()
def train_epoch(self, num_steps=None, profile=False, info=None):
def train_epoch(self,
num_steps=None,
profile=False,
info=None,
iterator=None):
"""Runs a training epoch and updates the model parameters."""
logger.debug("Begin Training Step {}".format(self.epochs + 1))
info = info or {}
@@ -193,9 +199,18 @@ class TorchRunner:
SCHEDULER_STEP: self.scheduler_step_freq
})
with self.timers.record("train_epoch"):
iterator = self.train_loader
if iterator is None:
iterator = iter(self.train_loader)
else:
# Dataset will provide us with a list of tuples but we
# need two lists.
def format_batch(batch):
features, targets = zip(*batch)
return torch.cat(features), torch.cat(targets)
iterator = map(format_batch, iterator)
if num_steps:
iterator = itertools.islice(iter(self.train_loader), num_steps)
iterator = itertools.islice(iterator, num_steps)
train_stats = self.training_operator.train_epoch(iterator, info)
self.epochs += 1
+32 -12
View File
@@ -17,6 +17,7 @@ from ray.util.sgd.torch.distributed_torch_runner import (
from ray.util.sgd.utils import check_for_failure, NUM_SAMPLES, BATCH_SIZE
from ray.util.sgd.torch.torch_runner import TorchRunner
from ray.util.sgd.torch.constants import VALID_SCHEDULER_STEP
from ray.util.sgd.data import Dataset
logger = logging.getLogger(__name__)
RESIZE_COOLDOWN_S = 10
@@ -194,11 +195,9 @@ class TorchTrainer:
"For more information, see "
"https://github.com/pytorch/examples/issues/467."))
if not (callable(model_creator) and callable(optimizer_creator)
and callable(data_creator)):
if not (callable(model_creator) and callable(optimizer_creator)):
raise ValueError(
"Must provide a callable model_creator, optimizer_creator, "
"and data_creator.")
"Must provide a callable model_creator and optimizer_creator.")
if num_replicas is not None:
raise DeprecationWarning(
@@ -379,7 +378,8 @@ class TorchTrainer:
profile=False,
reduce_results=True,
max_retries=3,
info=None):
info=None,
dataset=None):
"""Runs a training epoch.
Calls `operator.train_epoch()` on N parallel workers simultaneously
@@ -405,6 +405,8 @@ class TorchTrainer:
in case of shared cluster usage. Defaults to 3.
info (dict): Optional dictionary passed to the training
operator for ``train_epoch`` and ``train_batch``.
dataset (Dataset): Optional dataset to train with. If specified,
the dataloader passed in via data_creator will be ignored.
Returns:
(dict | list) A dictionary of metrics for training.
@@ -414,11 +416,14 @@ class TorchTrainer:
length will be equal to ``num_workers``.
"""
assert max_retries >= 0, "`max_retries` must be non-negative."
assert isinstance(dataset, Dataset) is not None \
or self.data_creator, \
"Must specify either a data creator or a dataset"
if self._should_resize():
logger.info("Resize opportunity detected. Attempting to scale up.")
self._resize_workers()
success, worker_stats = self._train_epoch(
num_steps=num_steps, profile=profile, info=info)
num_steps=num_steps, profile=profile, info=info, dataset=dataset)
# Fault handling
for i in range(max_retries):
if success:
@@ -429,7 +434,10 @@ class TorchTrainer:
logger.info("Retrying training step with %d workers." %
(len(self.remote_workers) + 1))
success, worker_stats = self._train_epoch(
num_steps=num_steps, profile=profile, info=info)
num_steps=num_steps,
profile=profile,
info=info,
dataset=dataset)
if not success:
raise RuntimeError("Training run failed.")
@@ -452,14 +460,26 @@ class TorchTrainer:
stats[stat_key] = worker_stats[0][stat_key]
return stats
def _train_epoch(self, num_steps=None, profile=False, info=None):
def _train_epoch(self,
num_steps=None,
profile=False,
info=None,
dataset=None):
params = dict(num_steps=num_steps, profile=profile, info=info)
remote_worker_stats = [
w.train_epoch.remote(**params) for w in self.remote_workers
]
remote_worker_stats = []
if dataset:
dataset.set_num_shards(self.max_replicas)
for i, w in enumerate(self.remote_workers):
params = dict(num_steps=num_steps, profile=profile, info=info)
if dataset:
params["iterator"] = dataset.get_shard(i)
stats = w.train_epoch.remote(**params)
remote_worker_stats.append(stats)
try:
if dataset:
params["iterator"] = dataset.get_shard(
len(self.remote_workers))
local_worker_stats = self.local_worker.train_epoch(**params)
except RuntimeError as err:
if "gloo" in err.args[0] and "Timed out" in err.args[0]: