Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SubprocVecEnv not working #640

Open
lhorus opened this issue Oct 7, 2018 · 10 comments
Open

SubprocVecEnv not working #640

lhorus opened this issue Oct 7, 2018 · 10 comments

Comments

@lhorus
Copy link

lhorus commented Oct 7, 2018

I'm trying to use SubprocVecEnv to initialize multiple instances of one environment to use in ACKTR and A2C, however, there seem to be problems with SubprocVecEnv. Following this code, the following happens:

def make_env():
    env = gym.make('Test-v0')
    return env

env = gym.make('Test-v0') #Works, env initialization message is printed
env = make_env() #Works, env initialization message is printed
env = SubprocVecEnv([make_env]) #Doesn't work, code gets stuck

Meaning, env creation through the function works fine, the same goes for manual creation, however, when SubprocVecEnv is called the code gets stuck, nothing seems to happen and it doesn't stop running, nor does the environment get created.

I tried calling manually env = SubprocVecEnv([make_env]) and it yields a strange error:

env = SubprocVecEnv([make_env])
Traceback (most recent call last):

File "", line 1, in
env = SubprocVecEnv([make_env])

File "c:\users\x\baselines\baselines\common\vec_env\subproc_vec_env.py", line 58, in init
observation_space, action_space = self.remotes[0].recv()

File "D:\Programs\Anaconda3\lib\multiprocessing\connection.py", line 250, in recv
buf = self._recv_bytes()

File "D:\Programs\Anaconda3\lib\multiprocessing\connection.py", line 321, in _recv_bytes
raise EOFError

EOFError

@lhorus
Copy link
Author

lhorus commented Oct 9, 2018

Apparently even with the default envs ('Cartpole-V1' as depicted in the linked example) has the same problem. Both custom and default envs work fine for DummyVecEnv's though.

Edited: After some testing, the problem seems to be at:

observation_space, action_space = self.remotes[0].recv()

Is the process eternally waiting for a response?

@lhorus
Copy link
Author

lhorus commented Oct 10, 2018

Update: 10/10/2018:

So I tried digging up a bit deeper, and eventually came to the conclusion that a part of the problem is in calling functions in the default way:

def make_env_complete(seed, num_env, start_index=0):

    def make_env(rank):
        def _thunk():
            env = gym.make('Test-v0')
            env.seed(seed+rank)
            env = Monitor(env, logger.get_dir() and os.path.join(logger.get_dir(), str(rank)))
            return env
        
        return _thunk   
    set_global_seeds(seed)

    return SubprocVecEnv([make_env(i + start_index) for i in range(num_env)])

Does not work either, it just hangs in there forever. However, if return _thunk() is used instead, something happens, an error is thrown to be more specific:

File "C:/Users/X/Desktop/untitled0.py", line 54, in
make_env_complete(0, 1, 0)

File "C:/Users/Horus/Desktop/untitled0.py", line 47, in make_env_complete
return SubprocVecEnv([make_env(i + start_index) for i in range(num_env)])

File "c:\users\X\baselines\baselines\common\vec_env\subproc_vec_env.py", line 53, in init
p.start()

File "D:\Programs\Anaconda3\lib\multiprocessing\process.py", line 105, in start
self._popen = self._Popen(self)

File "D:\Programs\Anaconda3\lib\multiprocessing\context.py", line 223, in _Popen
return _default_context.get_context().Process._Popen(process_obj)

File "D:\Programs\Anaconda3\lib\multiprocessing\context.py", line 322, in _Popen
return Popen(process_obj)

File "D:\Programs\Anaconda3\lib\multiprocessing\popen_spawn_win32.py", line 65, in init
reduction.dump(process_obj, to_child)

File "D:\Programs\Anaconda3\lib\multiprocessing\reduction.py", line 60, in dump
ForkingPickler(file, protocol).dump(obj)

File "c:\users\horus\baselines\baselines\common\vec_env_init_.py", line 176, in getstate
return cloudpickle.dumps(self.x)

File "D:\Programs\Anaconda3\lib\site-packages\cloudpickle\cloudpickle.py", line 895, in dumps
cp.dump(obj)

File "D:\Programs\Anaconda3\lib\site-packages\cloudpickle\cloudpickle.py", line 268, in dump
return Pickler.dump(self, obj)

File "D:\Programs\Anaconda3\lib\pickle.py", line 409, in dump
self.save(obj)

File "D:\Programs\Anaconda3\lib\pickle.py", line 521, in save
self.save_reduce(obj=obj, *rv)

File "D:\Programs\Anaconda3\lib\pickle.py", line 634, in save_reduce
save(state)

File "D:\Programs\Anaconda3\lib\pickle.py", line 476, in save
f(self, obj) # Call unbound method with explicit self

File "D:\Programs\Anaconda3\lib\pickle.py", line 821, in save_dict
self._batch_setitems(obj.items())

File "D:\Programs\Anaconda3\lib\pickle.py", line 847, in _batch_setitems
save(v)

File "D:\Programs\Anaconda3\lib\pickle.py", line 521, in save
self.save_reduce(obj=obj, *rv)

File "D:\Programs\Anaconda3\lib\pickle.py", line 634, in save_reduce
save(state)

File "D:\Programs\Anaconda3\lib\pickle.py", line 476, in save
f(self, obj) # Call unbound method with explicit self

File "D:\Programs\Anaconda3\lib\pickle.py", line 821, in save_dict
self._batch_setitems(obj.items())

File "D:\Programs\Anaconda3\lib\pickle.py", line 847, in _batch_setitems
save(v)

File "D:\Programs\Anaconda3\lib\pickle.py", line 476, in save
f(self, obj) # Call unbound method with explicit self

File "D:\Programs\Anaconda3\lib\site-packages\cloudpickle\cloudpickle.py", line 790, in save_file
raise pickle.PicklingError("Cannot pickle files that are not opened for reading: %s" % obj.mode)

PicklingError: Cannot pickle files that are not opened for reading: wt

@pzhokhov
Copy link
Collaborator

Hi @lhorus ! As you have already established, SubprocVecEnv's are nasty to debug, because if the worker process fails, the parent process is just left there waiting or gets an uninformative broken pipe. Making SubprocVecEnv more debug-friendly is on a TODO list; however, as a general rule of thumb for now, if the problem is with vecenv, I'd recommend replacing SubprocVecEnv with DummyVecEnv and debug that way. Now, in your particular case sounds like the error is actually specific to SubprocVecEnv - for some reason forking the process has troubles with open file descriptors. If you were to remove Monitor wrapper from make_env, would the resulting thing work?

@abhiskk
Copy link

abhiskk commented Oct 30, 2018

I also faced the same issue and got it to work following the below hack, note that this is not a fix to the problem, it is a hack so that you can use SubProcEnv:

import os

from stable_baselines import logger
from stable_baselines.bench import Monitor
from stable_baselines.common import set_global_seeds
from stable_baselines.common.atari_wrappers import make_atari, wrap_deepmind
from stable_baselines.common.cmd_util import make_atari_env
from stable_baselines.common.vec_env import DummyVecEnv


from stable_baselines.common.policies import CnnPolicy
from stable_baselines.common.vec_env import VecFrameStack
from stable_baselines import ACER


def custom_atari_env(env_id, num_env, seed, wrapper_kwargs=None, start_index=0,
                     allow_early_resets=True):
    """
    Create a wrapped, monitored SubprocVecEnv for Atari.

    :param env_id: (str) the environment ID
    :param num_env: (int) the number of environment you wish to have
           in subprocesses
    :param seed: (int) the inital seed for RNG
    :param wrapper_kwargs: (dict) the parameters for wrap_deepmind function
    :param start_index: (int) start rank index
    :param allow_early_resets: (bool) allows early reset of the environment
    :return: (Gym Environment) The atari environment
    """
    if wrapper_kwargs is None:
        wrapper_kwargs = {}
    
    def make_env(rank):
        def _thunk():
            env = make_atari(env_id)
            env.seed(seed + rank)
            env = Monitor(env, logger.get_dir() and os.path.join(
                logger.get_dir(), str(rank)),
                          allow_early_resets=allow_early_resets)
            return wrap_deepmind(env, **wrapper_kwargs)
        
        return _thunk
    
    set_global_seeds(seed)
    return DummyVecEnv([make_env(i + start_index) for i in range(num_env)])


def main():
    env = custom_atari_env('BreakoutNoFrameskip-v4', num_env=4, seed=42)
    env = make_atari_env('PongNoFrameskip-v4', num_env=4, seed=42)
    env = VecFrameStack(env, n_stack=4)
    model = ACER(CnnPolicy, env, verbose=1,
                 tensorboard_log='/private/home/akadian/teas-baselines/'
                                 'simple-nav/stable-baselines-runs')
    model.learn(total_timesteps=25)
    model.save('acer.pong.model')


if __name__ == '__main__':
    main()

@pzhokhov
Copy link
Collaborator

@abhiskk from what I understand, your hack does not actually use SubprocVecEnv (it is replaced with DummyVecEnv). In OP's particular problem that should help indeed (because no forking / pickling is going on in DummyVecEnv); however, this fix will make things quite a bit slower.

@abhiskk
Copy link

abhiskk commented Oct 30, 2018

It does use SubprocVecEnv, I just make a dummy call to custom_atari_env before making a call to the original make_atari_env.

env = custom_atari_env('BreakoutNoFrameskip-v4', num_env=4, seed=42)
env = make_atari_env('PongNoFrameskip-v4', num_env=4, seed=42)

The call to custom_atari_env I think sets something up in the background and the make_atari_env does not throw an error after that.

@glmcdona
Copy link

I was having a similar issue when creating a UnityEnv gym wrapper using SubprocVecEnv. For me, the fix was editing the default RPC server port because the port was already open. Editing:
\mlagents\envs\environment.py -> UnityEnvironment -> init() to use a different default base_port.

A computer restart would have likely fixed this as well, I recommend trying a hard reset of your computer if you face a similar issue.

@Coronon
Copy link

Coronon commented Jan 29, 2019

I have the same issue, this should get fixed. My code works perfectly fine with Dummy env but Subproc throws the exact same error

@pzhokhov
Copy link
Collaborator

pzhokhov commented Jan 29, 2019

@Coronon could you provide the code you are running and error stacktrace? The baselines code has changed quite a bit since the issue was opened; it would be helpful to have a recent stacktrace.

shwang pushed a commit to shwang/baselines that referenced this issue Jan 10, 2020
* Pass build arguments to Docker

* Fix Codacy downloader to work with latest releases

* Add automatic Docker push for release

* Update changelog

* Print pytype version in CI

* Fix type error

* Fix other type false positive
@entityJY
Copy link

entityJY commented Jan 8, 2025

I have the same issue, running on a Mac M3
`python
import gymnasium as gym
from gymnasium.wrappers.transform_observation import GrayscaleObservation

from stable_baselines3.common.env_util import make_vec_env
from stable_baselines3.common.vec_env import SubprocVecEnv

from stable_baselines3.common.monitor import Monitor

import ale_py

gym.register_envs(ale_py)

environment_name = "SpaceInvadersNoFrameskip-v4"

env = gym.make(environment_name)
env = make_vec_env(environment_name, n_envs=8, vec_env_cls=SubprocVecEnv) # <---- error on this line
`

Error given:
RuntimeError:
An attempt has been made to start a new process before the
current process has finished its bootstrapping phase.

    This probably means that you are not using fork to start your
    child processes and you have forgotten to use the proper idiom
    in the main module:

        if __name__ == '__main__':
            freeze_support()
            ...

    The "freeze_support()" line can be omitted if the program
    is not going to be frozen to produce an executable.

    To fix this issue, refer to the "Safe importing of main module"
    section in https://docs.python.org/3/library/multiprocessing.html

Traceback (most recent call last):
File "/Users/.../.../.../PPO/experimentConfig.py", line 18, in
env = make_vec_env(environment_name, n_envs=8, vec_env_cls=SubprocVecEnv)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/homebrew/Caskroom/miniconda/base/envs/RL/lib/python3.11/site-packages/stable_baselines3/common/env_util.py", line 125, in make_vec_env
vec_env = vec_env_cls([make_env(i + start_index) for i in range(n_envs)], **vec_env_kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/homebrew/Caskroom/miniconda/base/envs/RL/lib/python3.11/site-packages/stable_baselines3/common/vec_env/subproc_vec_env.py", line 118, in init
observation_space, action_space = self.remotes[0].recv()
^^^^^^^^^^^^^^^^^^^^^^
File "/opt/homebrew/Caskroom/miniconda/base/envs/RL/lib/python3.11/multiprocessing/connection.py", line 250, in recv
buf = self._recv_bytes()
^^^^^^^^^^^^^^^^^^
File "/opt/homebrew/Caskroom/miniconda/base/envs/RL/lib/python3.11/multiprocessing/connection.py", line 430, in _recv_bytes
buf = self._recv(4)
^^^^^^^^^^^^^
File "/opt/homebrew/Caskroom/miniconda/base/envs/RL/lib/python3.11/multiprocessing/connection.py", line 399, in _recv
raise EOFError
EOFError

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants