Hydra: [Bug] hydra-joblib-launcher crashes when PyTorch DataLoader num_workers>0

Created on 14 Sep 2020  ยท  13Comments  ยท  Source: facebookresearch/hydra

๐Ÿ› Bug

Description

As title. The hydra-joblib-launcher fails when setting num_workers>0 in PyTorch's DataLoader. If num_workers==0 everything works just fine. However, the speedup achieved with joblib multiprocessing barely (if at all) compensates for increased runtime of individual jobs.

Checklist

  • [x] I checked on the latest version of Hydra
  • [x] I created a minimal repro

To reproduce

```python title="MNIST_hydra.py"
import os
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import DataLoader
from torchvision.datasets import MNIST
from torchvision import transforms
import hydra

class Model(nn.Module):
def __init__(self):
super().__init__()
self.l1 = torch.nn.Linear(28 * 28, 10)

def forward(self, x):
    return torch.relu(self.l1(x.view(x.size(0), -1)))

@hydra.main()
def main(cfg):
data_set = MNIST(os.getcwd(), download=True, train=True, transform=transforms.ToTensor())
data_loader = DataLoader(data_set, batch_size=64, num_workers=cfg.num_workers)
model = Model().cuda()
optim = torch.optim.Adam(model.parameters(), lr=cfg.lr)

for batch in data_loader:
    x, y = batch
    y_hat = model(x.cuda())
    loss = F.cross_entropy(y_hat, y.cuda())
print(f'completed {cfg.lr}')

if __name__ == '__main__':
main()

This works:
```commandline
python MNIST_hydra.py +num_workers=0 +lr=1e-4,1e-3,1e-2 hydra/launcher=joblib -m

This fails:

python MNIST_hydra.py +num_workers=1 +lr=1e-4,1e-3,1e-2 hydra/launcher=joblib -m

with error: (note that there are several times more cores available than required for this config.)

* Stack trace/error message *

/usr/lib/python3.7/multiprocessing/semaphore_tracker.py:144: UserWarning: semaphore_tracker: There appear to be 1 leaked semaphores to clean up at shutdown
  len(cache))
/usr/lib/python3.7/multiprocessing/semaphore_tracker.py:144: UserWarning: semaphore_tracker: There appear to be 1 leaked semaphores to clean up at shutdown
  len(cache))
/usr/lib/python3.7/multiprocessing/semaphore_tracker.py:144: UserWarning: semaphore_tracker: There appear to be 1 leaked semaphores to clean up at shutdown
  len(cache))
joblib.externals.loky.process_executor._RemoteTraceback:
"""
Traceback (most recent call last):
  File "/opt/.virtualenvs/l2r/lib/python3.7/site-packages/joblib/externals/loky/process_executor.py", line 431, in _process_worker
    r = call_item()
  File "/opt/.virtualenvs/l2r/lib/python3.7/site-packages/joblib/externals/loky/process_executor.py", line 285, in __call__
    return self.fn(*self.args, **self.kwargs)
  File "/opt/.virtualenvs/l2r/lib/python3.7/site-packages/joblib/_parallel_backends.py", line 595, in __call__
    return self.func(*args, **kwargs)
  File "/opt/.virtualenvs/l2r/lib/python3.7/site-packages/joblib/parallel.py", line 253, in __call__
    for func, args, kwargs in self.items]
  File "/opt/.virtualenvs/l2r/lib/python3.7/site-packages/joblib/parallel.py", line 253, in <listcomp>
    for func, args, kwargs in self.items]
  File "/opt/.virtualenvs/l2r/lib/python3.7/site-packages/hydra_plugins/hydra_joblib_launcher/_core.py", line 47, in execute_job
    job_subdir_key="hydra.sweep.subdir",
  File "/opt/.virtualenvs/l2r/lib/python3.7/site-packages/hydra/core/utils.py", line 125, in run_job
    ret.return_value = task_function(task_cfg)
  File "MNIST_hydra.py", line 28, in main
    for batch in data_loader:
  File "/opt/.virtualenvs/l2r/lib/python3.7/site-packages/torch/utils/data/dataloader.py", line 291, in __iter__
    return _MultiProcessingDataLoaderIter(self)
  File "/opt/.virtualenvs/l2r/lib/python3.7/site-packages/torch/utils/data/dataloader.py", line 737, in __init__
    w.start()
  File "/usr/lib/python3.7/multiprocessing/process.py", line 112, in start
    self._popen = self._Popen(self)
  File "/usr/lib/python3.7/multiprocessing/context.py", line 223, in _Popen
    return _default_context.get_context().Process._Popen(process_obj)
  File "/opt/.virtualenvs/l2r/lib/python3.7/site-packages/joblib/externals/loky/backend/process.py", line 39, in _Popen
    return Popen(process_obj)
  File "/opt/.virtualenvs/l2r/lib/python3.7/site-packages/joblib/externals/loky/backend/popen_loky_posix.py", line 52, in __init__
    self._launch(process_obj)
  File "/opt/.virtualenvs/l2r/lib/python3.7/site-packages/joblib/externals/loky/backend/popen_loky_posix.py", line 157, in _launch
    pid = fork_exec(cmd_python, self._fds, env=process_obj.env)
AttributeError: 'Process' object has no attribute 'env'
"""

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/opt/.virtualenvs/l2r/lib/python3.7/site-packages/hydra/_internal/utils.py", line 207, in run_and_report
    return func()
  File "/opt/.virtualenvs/l2r/lib/python3.7/site-packages/hydra/_internal/utils.py", line 367, in <lambda>
    overrides=args.overrides,
  File "/opt/.virtualenvs/l2r/lib/python3.7/site-packages/hydra/_internal/hydra.py", line 136, in multirun
    return sweeper.sweep(arguments=task_overrides)
  File "/opt/.virtualenvs/l2r/lib/python3.7/site-packages/hydra/_internal/core_plugins/basic_sweeper.py", line 154, in sweep
    results = self.launcher.launch(batch, initial_job_idx=initial_job_idx)
  File "/opt/.virtualenvs/l2r/lib/python3.7/site-packages/hydra_plugins/hydra_joblib_launcher/joblib_launcher.py", line 46, in launch
    launcher=self, job_overrides=job_overrides, initial_job_idx=initial_job_idx
  File "/opt/.virtualenvs/l2r/lib/python3.7/site-packages/hydra_plugins/hydra_joblib_launcher/_core.py", line 98, in launch
    for idx, overrides in enumerate(job_overrides)
  File "/opt/.virtualenvs/l2r/lib/python3.7/site-packages/joblib/parallel.py", line 1042, in __call__
    self.retrieve()
  File "/opt/.virtualenvs/l2r/lib/python3.7/site-packages/joblib/parallel.py", line 921, in retrieve
    self._output.extend(job.get(timeout=self.timeout))
  File "/opt/.virtualenvs/l2r/lib/python3.7/site-packages/joblib/_parallel_backends.py", line 542, in wrap_future_result
    return future.result(timeout=timeout)
  File "/usr/lib/python3.7/concurrent/futures/_base.py", line 435, in result
    return self.__get_result()
  File "/usr/lib/python3.7/concurrent/futures/_base.py", line 384, in __get_result
    raise self._exception
AttributeError: 'Process' object has no attribute 'env'
/opt/.virtualenvs/l2r/lib/python3.7/site-packages/joblib/externals/loky/backend/resource_tracker.py:320: UserWarning: resource_tracker: There appear to be 22 leaked semlock objects to clean up at shutdown
  (len(rtype_registry), rtype))

Expected Behavior

Being able to use hydra-joblib-launcher and num_workers>0 in PyTorch at the same time.

System information

  • Hydra Version : 1.0.0
  • hydra-joblib-launcher: 1.1.0
  • Python version : 3.6.9 and 3.7.5
  • Virtual environment type and version : virtualenv==15.1.0
  • Operating system : Ubuntu 18.04

Additional context

Add any other context about the problem here.

bug invalid

Most helpful comment

Alright. I finally managed to get it to work by forcing the PyTorch DataLoader to use multiprocessing_context='fork'. Apparently the loky backend in joblib clashes with the other forking methods. Below is minimum working example using hydra, hydra-joblib-launcher, and DataLoader(..., num_workers>0, multiprocessing_context='fork').

import os
import torch, torch.nn as nn, torch.nn.functional as F
from torch.utils.data import DataLoader
from torchvision import transforms, datasets
import hydra

class Model(nn.Module):
    def __init__(self):
        super().__init__()
        self.l1 = torch.nn.Linear(28 * 28, 10)

    def forward(self, x):
        return torch.relu(self.l1(x.view(x.size(0), -1)))

@hydra.main()
def main(cfg):
    data_set = datasets.MNIST(os.getcwd(), download=True, train=True, transform=transforms.ToTensor())
    data_loader = DataLoader(data_set, batch_size=64, num_workers=cfg.num_workers, multiprocessing_context='fork')
    model = Model()
    optim = torch.optim.Adam(model.parameters(), lr=cfg.lr)

    for batch in data_loader:
        x, y = batch
        y_hat = model(x)
        loss = F.cross_entropy(y_hat, y)
    print(f'completed {cfg.lr}')

if __name__ == '__main__':
    main()

and

python MNIST_hydra.py hydra/launcher=joblib +lr=1e-4,1e-3,1e-2 +num_workers=1 -m

It is worth noting that if one of the jobs in the sweep fails, the entire sweep will terminate once the currently running jobs are finished.

All 13 comments

This is likely an issue between joblib and PyTorch.
Try to reproduce with joblib and PyTorch. If you can, please file an issue against joblib and / or PyTorch.

In the mean time, you might want to try out the submitit plugin, which also has a local mode (similar to joblib). Maybe it will work better.

@jan-matthis, fyi.

I get the same error using PyTorch/joblib, so not hydra related indeed. Will give the submitit plugin a go later and see if that works. Will report back after as a reference to others before closing the issue.

Thanks for checking.
It would be great if you can file an issue against joblib to see what they think (please tag me if you do).

@omry joblib issue at https://github.com/joblib/joblib/issues/1104

Submitit seems to be a workaround, but I can't get submitit_local to work properly. With joblib I can fix the number of jobs running simultaneously to say 3 by setting n_jobs=3. I expected to be able to do the same thing with submitit using the following config:

# @package hydra.launcher
_target_: hydra_plugins.hydra_submitit_launcher.submitit_launcher.LocalLauncher
tasks_per_node: 3
nodes: 1

However, it seems that this just creates n==3 jobs on the same pid but still runs the entire sweep at once. This obviously fails due to resource constraints.


Click for output tree created using submitit

The sweep contains 19 configurations and for each of them there are 3 jobs created. See below where

$ tree
.
โ”œโ”€โ”€ 0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152210.gerard.14558.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152211.gerard.14559.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152212.gerard.14557.0
โ”‚ย ย  โ””โ”€โ”€ my_app.log
โ”œโ”€โ”€ 1
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152212.gerard.14561.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152213.gerard.14563.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152214.gerard.14562.0
โ”‚ย ย  โ””โ”€โ”€ my_app.log
โ”œโ”€โ”€ 10
โ”‚ย ย  โ”œโ”€โ”€ checkpoints
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152222.gerard.14805.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152223.gerard.14799.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152223.gerard.14802.0
โ”‚ย ย  โ”œโ”€โ”€ hparams.yaml
โ”‚ย ย  โ””โ”€โ”€ my_app.log
โ”œโ”€โ”€ 11
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152223.gerard.14853.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152223.gerard.14854.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152224.gerard.14852.0
โ”‚ย ย  โ””โ”€โ”€ my_app.log
โ”œโ”€โ”€ 12
โ”‚ย ย  โ”œโ”€โ”€ checkpoints
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152224.gerard.14898.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152224.gerard.14899.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152224.gerard.14901.0
โ”‚ย ย  โ”œโ”€โ”€ hparams.yaml
โ”‚ย ย  โ””โ”€โ”€ my_app.log
โ”œโ”€โ”€ 13
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152224.gerard.14918.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152224.gerard.14919.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152224.gerard.14920.0
โ”‚ย ย  โ””โ”€โ”€ my_app.log
โ”œโ”€โ”€ 14
โ”‚ย ย  โ”œโ”€โ”€ checkpoints
โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ epoch=29.ckpt
โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ epoch=34.ckpt
โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ epoch=39.ckpt
โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ epoch=44.ckpt
โ”‚ย ย  โ”‚ย ย  โ””โ”€โ”€ epoch=49.ckpt
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152224.gerard.14989.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152224.gerard.14991.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152225.gerard.14990.0
โ”‚ย ย  โ”œโ”€โ”€ hparams.yaml
โ”‚ย ย  โ””โ”€โ”€ my_app.log
โ”œโ”€โ”€ 15
โ”‚ย ย  โ”œโ”€โ”€ checkpoints
โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ epoch=29.ckpt
โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ epoch=34.ckpt
โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ epoch=39.ckpt
โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ epoch=44.ckpt
โ”‚ย ย  โ”‚ย ย  โ””โ”€โ”€ epoch=49.ckpt
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152224.gerard.15036.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152225.gerard.15038.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152226.gerard.15037.0
โ”‚ย ย  โ”œโ”€โ”€ hparams.yaml
โ”‚ย ย  โ””โ”€โ”€ my_app.log
โ”œโ”€โ”€ 16
โ”‚ย ย  โ”œโ”€โ”€ checkpoints
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152225.gerard.15096.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152226.gerard.15094.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152226.gerard.15095.0
โ”‚ย ย  โ”œโ”€โ”€ hparams.yaml
โ”‚ย ย  โ””โ”€โ”€ my_app.log
โ”œโ”€โ”€ 17
โ”‚ย ย  โ”œโ”€โ”€ checkpoints
โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ epoch=29.ckpt
โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ epoch=34.ckpt
โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ epoch=39.ckpt
โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ epoch=44.ckpt
โ”‚ย ย  โ”‚ย ย  โ””โ”€โ”€ epoch=49.ckpt
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152225.gerard.15122.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152226.gerard.15121.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152226.gerard.15123.0
โ”‚ย ย  โ”œโ”€โ”€ hparams.yaml
โ”‚ย ย  โ””โ”€โ”€ my_app.log
โ”œโ”€โ”€ 18
โ”‚ย ย  โ”œโ”€โ”€ checkpoints
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152226.gerard.15178.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152226.gerard.15179.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152226.gerard.15180.0
โ”‚ย ย  โ”œโ”€โ”€ hparams.yaml
โ”‚ย ย  โ””โ”€โ”€ my_app.log
โ”œโ”€โ”€ 2
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152216.gerard.14582.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152216.gerard.14605.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152216.gerard.14606.0
โ”‚ย ย  โ””โ”€โ”€ my_app.log
โ”œโ”€โ”€ 3
โ”‚ย ย  โ”œโ”€โ”€ checkpoints
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152217.gerard.14624.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152217.gerard.14626.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152218.gerard.14623.0
โ”‚ย ย  โ”œโ”€โ”€ hparams.yaml
โ”‚ย ย  โ””โ”€โ”€ my_app.log
โ”œโ”€โ”€ 4
โ”‚ย ย  โ”œโ”€โ”€ checkpoints
โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ epoch=29.ckpt
โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ epoch=34.ckpt
โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ epoch=39.ckpt
โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ epoch=44.ckpt
โ”‚ย ย  โ”‚ย ย  โ””โ”€โ”€ epoch=49.ckpt
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152219.gerard.14679.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152219.gerard.14681.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152219.gerard.14682.0
โ”‚ย ย  โ”œโ”€โ”€ hparams.yaml
โ”‚ย ย  โ””โ”€โ”€ my_app.log
โ”œโ”€โ”€ 5
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152219.gerard.14685.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152219.gerard.14697.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152219.gerard.14699.0
โ”‚ย ย  โ””โ”€โ”€ my_app.log
โ”œโ”€โ”€ 6
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152220.gerard.14727.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152221.gerard.14725.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152221.gerard.14726.0
โ”‚ย ย  โ””โ”€โ”€ my_app.log
โ”œโ”€โ”€ 7
โ”‚ย ย  โ”œโ”€โ”€ checkpoints
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152220.gerard.14744.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152221.gerard.14743.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152222.gerard.14742.0
โ”‚ย ย  โ”œโ”€โ”€ hparams.yaml
โ”‚ย ย  โ””โ”€โ”€ my_app.log
โ”œโ”€โ”€ 8
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152221.gerard.14751.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152221.gerard.14754.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152222.gerard.14753.0
โ”‚ย ย  โ””โ”€โ”€ my_app.log
โ”œโ”€โ”€ 9
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152221.gerard.14783.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152223.gerard.14785.0
โ”‚ย ย  โ”œโ”€โ”€ events.out.tfevents.1600152224.gerard.14788.0
โ”‚ย ย  โ””โ”€โ”€ my_app.log
โ”œโ”€โ”€ multirun.yaml
โ””โ”€โ”€ .submitit 
    โ”œโ”€โ”€ 14556
    โ”‚ย ย  โ”œโ”€โ”€ 14556_0_log.err
    โ”‚ย ย  โ”œโ”€โ”€ 14556_0_log.out
    โ”‚ย ย  โ”œโ”€โ”€ 14556_1_log.err
    โ”‚ย ย  โ”œโ”€โ”€ 14556_1_log.out
    โ”‚ย ย  โ”œโ”€โ”€ 14556_2_log.err
    โ”‚ย ย  โ”œโ”€โ”€ 14556_2_log.out
    โ”‚ย ย  โ””โ”€โ”€ 14556_submitted.pkl
    โ”œโ”€โ”€ 14932
    โ”‚ย ย  โ”œโ”€โ”€ 14932_0_log.err
    โ”‚ย ย  โ”œโ”€โ”€ 14932_0_log.out
    โ”‚ย ย  โ”œโ”€โ”€ 14932_0_result.pkl
    โ”‚ย ย  โ”œโ”€โ”€ 14932_1_log.err
    โ”‚ย ย  โ”œโ”€โ”€ 14932_1_log.out
    โ”‚ย ย  โ”œโ”€โ”€ 14932_2_log.err
    โ”‚ย ย  โ”œโ”€โ”€ 14932_2_log.out
    โ”‚ย ย  โ””โ”€โ”€ 14932_submitted.pkl
    โ”œโ”€โ”€ 14976
    โ”‚ย ย  โ”œโ”€โ”€ 14976_0_log.err
    โ”‚ย ย  โ”œโ”€โ”€ 14976_0_log.out
    โ”‚ย ย  โ”œโ”€โ”€ 14976_1_log.err
    โ”‚ย ย  โ”œโ”€โ”€ 14976_1_log.out
    โ”‚ย ย  โ”œโ”€โ”€ 14976_2_log.err
    โ”‚ย ย  โ”œโ”€โ”€ 14976_2_log.out
    โ”‚ย ย  โ”œโ”€โ”€ 14976_2_result.pkl
    โ”‚ย ย  โ””โ”€โ”€ 14976_submitted.pkl
    โ”œโ”€โ”€ 15030
    โ”‚ย ย  โ”œโ”€โ”€ 15030_0_log.err
    โ”‚ย ย  โ”œโ”€โ”€ 15030_0_log.out
    โ”‚ย ย  โ”œโ”€โ”€ 15030_0_result.pkl
    โ”‚ย ย  โ”œโ”€โ”€ 15030_1_log.err
    โ”‚ย ย  โ”œโ”€โ”€ 15030_1_log.out
    โ”‚ย ย  โ”œโ”€โ”€ 15030_2_log.err
    โ”‚ย ย  โ”œโ”€โ”€ 15030_2_log.out
    โ”‚ย ย  โ””โ”€โ”€ 15030_submitted.pkl

I omitted the .hydra folders and many results in .submitit for brevity if you can call it that.

Am I doing something wrong or is this the intended behavior?

Also, is it possible to suppress generation of the .submitit folder? Hydra already logs everything that I am interested in. Training ML models using progress trackers such as tqdm also creates massive log files here.

Generally submitit-local is mostly used for debugging, I am not sure it has this functionality.
@jrapin ?

About the .submitit directory:
It's not possible to disable it. Especially when running with SLURM (which is what submitit it really for) - You can have failures to start the job before the process even get to run. Those are impossible to debug without the .submitit directory.

@gerardsn, ~I think the repro you created in the joblib is different.~
~In the joblib launcher, the launcher spawn your processes before your @hydra.main() function is called, only then the workers are starting.~

~In the your pure joblib repro you first spwan the dataloader workers and then the joblib workers.~

Never mind, your repro is fine.

Looking at the stacktrace carefully, you can see that somehow torch dataloader spawning processes is hitting the joblib code.
They must somehow be manipulating the process spawning API pytorch is using to go through joblib and they are failing there.

  File "MNIST_hydra.py", line 25, in main
    for batch in data_loader:
  File "/home/omry/miniconda3/envs/torch-config/lib/python3.8/site-packages/torch/utils/data/dataloader.py", line 291, in __iter__
    return _MultiProcessingDataLoaderIter(self)
  File "/home/omry/miniconda3/envs/torch-config/lib/python3.8/site-packages/torch/utils/data/dataloader.py", line 737, in __init__
    w.start()
  File "/home/omry/miniconda3/envs/torch-config/lib/python3.8/multiprocessing/process.py", line 121, in start
    self._popen = self._Popen(self)
  File "/home/omry/miniconda3/envs/torch-config/lib/python3.8/multiprocessing/context.py", line 224, in _Popen
    return _default_context.get_context().Process._Popen(process_obj)
  File "/home/omry/miniconda3/envs/torch-config/lib/python3.8/site-packages/joblib/externals/loky/backend/process.py", line 39, in _Popen
    return Popen(process_obj)
  File "/home/omry/miniconda3/envs/torch-config/lib/python3.8/site-packages/joblib/externals/loky/backend/popen_loky_posix.py", line 52, in __init__
    self._launch(process_obj)
  File "/home/omry/miniconda3/envs/torch-config/lib/python3.8/site-packages/joblib/externals/loky/backend/popen_loky_posix.py", line 157, in _launch
    pid = fork_exec(cmd_python, self._fds, env=process_obj.env)
AttributeError: 'Process' object has no attribute 'env'

Changing Joblib to use prefer=threads instead of backend=loky runs without failure (no errors, not sure if it does the right thing). See change in last line of code below

import os, sys
import torch, torch.nn as nn, torch.nn.functional as F
from torch.utils.data import DataLoader
from torchvision import transforms, datasets
from joblib import Parallel, delayed

class Model(nn.Module):
    def __init__(self):
        super().__init__()
        self.l1 = torch.nn.Linear(28 * 28, 10)

    def forward(self, x):
        return torch.relu(self.l1(x.view(x.size(0), -1)))

def main(num_workers, lr):
    data_set = datasets.MNIST(os.getcwd(), download=True, train=True, transform=transforms.ToTensor())
    data_loader = DataLoader(data_set, batch_size=64, num_workers=num_workers)
    model = Model()
    optim = torch.optim.Adam(model.parameters(), lr=lr)

    for batch in data_loader:
        x, y = batch
        y_hat = model(x)
        loss = F.cross_entropy(y_hat, y)
    print(f'completed {lr}')

if __name__ == '__main__':
    num_workers = int(sys.argv[1])
    # Parallel(n_jobs=3, backend='loky')(delayed(main)(num_workers, lr) for lr in [1e-4, 1e-3, 1e-2])
    Parallel(n_jobs=3, prefer='threads')(delayed(main)(num_workers, lr) for lr in [1e-4, 1e-3, 1e-2])

Unfortunately hydra appears to be incompatible with anything else than the loky backend #423. Commenting out the hardcoded loky backend in the hydra-joblib-launcher does run, but it does not produce the expected results and does not run parallel jobs.

Hydra is not compatible with the threaded backend. You are going to have a bad time.

Closing as this is not a bug in Hydra. Feel free to continue discussing here.

Alright. I finally managed to get it to work by forcing the PyTorch DataLoader to use multiprocessing_context='fork'. Apparently the loky backend in joblib clashes with the other forking methods. Below is minimum working example using hydra, hydra-joblib-launcher, and DataLoader(..., num_workers>0, multiprocessing_context='fork').

import os
import torch, torch.nn as nn, torch.nn.functional as F
from torch.utils.data import DataLoader
from torchvision import transforms, datasets
import hydra

class Model(nn.Module):
    def __init__(self):
        super().__init__()
        self.l1 = torch.nn.Linear(28 * 28, 10)

    def forward(self, x):
        return torch.relu(self.l1(x.view(x.size(0), -1)))

@hydra.main()
def main(cfg):
    data_set = datasets.MNIST(os.getcwd(), download=True, train=True, transform=transforms.ToTensor())
    data_loader = DataLoader(data_set, batch_size=64, num_workers=cfg.num_workers, multiprocessing_context='fork')
    model = Model()
    optim = torch.optim.Adam(model.parameters(), lr=cfg.lr)

    for batch in data_loader:
        x, y = batch
        y_hat = model(x)
        loss = F.cross_entropy(y_hat, y)
    print(f'completed {cfg.lr}')

if __name__ == '__main__':
    main()

and

python MNIST_hydra.py hydra/launcher=joblib +lr=1e-4,1e-3,1e-2 +num_workers=1 -m

It is worth noting that if one of the jobs in the sweep fails, the entire sweep will terminate once the currently running jobs are finished.

Awesome, thanks.

Generally submitit-local is mostly used for debugging, I am not sure it has this functionality.
@jrapin ?

Hey, sorry for jumping in late (busy full time on a deadline :s)
yes submitit-local is mostly for debugging and takes some liberties with resources, but here that's not exactly the problem. ntasks_per_node is rather low level and reproduces the behavior or slurm, which is indeed running the same code each time. It's up to the code to actually split the work, based on environment variables (which are accessible through submitit.JobEnvironment().global_id for instance)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

goens picture goens  ยท  3Comments

omry picture omry  ยท  5Comments

emilemathieu picture emilemathieu  ยท  4Comments

SunQpark picture SunQpark  ยท  4Comments

moskomule picture moskomule  ยท  5Comments