def _wrap_env(env: GymEnv, verbose: int = 0, monitor_wrapper: bool = True) -> VecEnv: """ " Wrap environment with the appropriate wrappers if needed. For instance, to have a vectorized environment or to re-order the image channels. :param env: :param verbose: :param monitor_wrapper: Whether to wrap the env in a ``Monitor`` when possible. :return: The wrapped environment. """ if not isinstance(env, VecEnv): if not is_wrapped(env, Monitor) and monitor_wrapper: if verbose >= 1: print("Wrapping the env with a `Monitor` wrapper") env = Monitor(env) if verbose >= 1: print("Wrapping the env in a DummyVecEnv.") env = DummyVecEnv([lambda: env]) # Make sure that dict-spaces are not nested (not supported) check_for_nested_spaces(env.observation_space) if isinstance(env.observation_space, gym.spaces.Dict): for space in env.observation_space.spaces.values(): if isinstance(space, gym.spaces.Dict): raise ValueError( "Nested observation spaces are not supported (Dict spaces inside Dict space)." ) if not is_vecenv_wrapped(env, VecTransposeImage): wrap_with_vectranspose = False if isinstance(env.observation_space, gym.spaces.Dict): # If even one of the keys is a image-space in need of transpose, apply transpose # If the image spaces are not consistent (for instance one is channel first, # the other channel last), VecTransposeImage will throw an error for space in env.observation_space.spaces.values(): wrap_with_vectranspose = wrap_with_vectranspose or ( is_image_space(space) and not is_image_space_channels_first(space)) else: wrap_with_vectranspose = is_image_space( env.observation_space ) and not is_image_space_channels_first(env.observation_space) if wrap_with_vectranspose: if verbose >= 1: print("Wrapping the env in a VecTransposeImage.") env = VecTransposeImage(env) return env
def test_image_space_checks(): not_image_space = spaces.Box(0, 1, shape=(10, )) assert not is_image_space(not_image_space) # Not uint8 not_image_space = spaces.Box(0, 255, shape=(10, 10, 3)) assert not is_image_space(not_image_space) # Not correct shape not_image_space = spaces.Box(0, 255, shape=(10, 10), dtype=np.uint8) assert not is_image_space(not_image_space) # Not correct low/high not_image_space = spaces.Box(0, 10, shape=(10, 10, 3), dtype=np.uint8) assert not is_image_space(not_image_space) # Not correct space not_image_space = spaces.Discrete(n=10) assert not is_image_space(not_image_space) an_image_space = spaces.Box(0, 255, shape=(10, 10, 3), dtype=np.uint8) assert is_image_space(an_image_space, check_channels=False) assert is_image_space(an_image_space, check_channels=True) channel_first_image_space = spaces.Box(0, 255, shape=(3, 10, 10), dtype=np.uint8) assert is_image_space(channel_first_image_space, check_channels=False) assert is_image_space(channel_first_image_space, check_channels=True) an_image_space_with_odd_channels = spaces.Box(0, 255, shape=(10, 10, 5), dtype=np.uint8) assert is_image_space(an_image_space_with_odd_channels) # Should not pass if we check if channels are valid for an image assert not is_image_space(an_image_space_with_odd_channels, check_channels=True) # Test if channel-check works channel_first_space = spaces.Box(0, 255, shape=(3, 10, 10), dtype=np.uint8) assert is_image_space_channels_first(channel_first_space) channel_last_space = spaces.Box(0, 255, shape=(10, 10, 3), dtype=np.uint8) assert not is_image_space_channels_first(channel_last_space) channel_mid_space = spaces.Box(0, 255, shape=(10, 3, 10), dtype=np.uint8) # Should raise a warning with pytest.warns(Warning): assert not is_image_space_channels_first(channel_mid_space)
def _wrap_env(env: GymEnv, verbose: int = 0, monitor_wrapper: bool = True) -> VecEnv: """ " Wrap environment with the appropriate wrappers if needed. For instance, to have a vectorized environment or to re-order the image channels. :param env: :param verbose: :param monitor_wrapper: Whether to wrap the env in a ``Monitor`` when possible. :return: The wrapped environment. """ if not isinstance(env, VecEnv): if not is_wrapped(env, Monitor) and monitor_wrapper: if verbose >= 1: print("Wrapping the env with a `Monitor` wrapper") env = Monitor(env) if verbose >= 1: print("Wrapping the env in a DummyVecEnv.") env = DummyVecEnv([lambda: env]) if (is_image_space(env.observation_space) and not is_vecenv_wrapped(env, VecTransposeImage) and not is_image_space_channels_first(env.observation_space)): if verbose >= 1: print("Wrapping the env in a VecTransposeImage.") env = VecTransposeImage(env) # check if wrapper for dict support is needed when using HER if isinstance(env.observation_space, gym.spaces.dict.Dict): env = ObsDictWrapper(env) return env
def create_envs(self, n_envs: int, eval_env: bool = False, no_log: bool = False) -> VecEnv: env = pistonball_v5.parallel_env() env = ss.color_reduction_v0(env, mode="B") env = ss.resize_v0(env, x_size=84, y_size=84, linear_interp=True) env = ss.frame_stack_v1(env, 3) env = ss.pettingzoo_env_to_vec_env_v1(env) print(n_envs) env = ss.concat_vec_envs_v1(env, n_envs, num_cpus=4, base_class="stable_baselines3") env = VecMonitor(env) env = self._maybe_normalize(env, eval_env) if is_image_space( env.observation_space) and not is_image_space_channels_first( env.observation_space): if self.verbose > 0: print("Wrapping into a VecTransposeImage") env = VecTransposeImage(env) return env
def _check_image_input(observation_space: spaces.Box, key: str = "") -> None: """ Check that the input will be compatible with Stable-Baselines when the observation is apparently an image. """ if observation_space.dtype != np.uint8: warnings.warn( f"It seems that your observation {key} is an image but the `dtype` " "of your observation_space is not `np.uint8`. " "If your observation is not an image, we recommend you to flatten the observation " "to have only a 1D vector" ) if np.any(observation_space.low != 0) or np.any(observation_space.high != 255): warnings.warn( f"It seems that your observation space {key} is an image but the " "upper and lower bounds are not in [0, 255]. " "Because the CNN policy normalize automatically the observation " "you may encounter issue if the values are not in that range." ) non_channel_idx = 0 # Check only if width/height of the image is big enough if is_image_space_channels_first(observation_space): non_channel_idx = -1 if observation_space.shape[non_channel_idx] < 36 or observation_space.shape[1] < 36: warnings.warn( "The minimal resolution for an image is 36x36 for the default `CnnPolicy`. " "You might need to use a custom feature extractor " "cf. https://stable-baselines3.readthedocs.io/en/master/guide/custom_policy.html" )
def create_envs(self, n_envs: int, eval_env: bool = False, no_log: bool = False) -> VecEnv: """ Create the environment and wrap it if necessary. :param n_envs: :param eval_env: Whether is it an environment used for evaluation or not :param no_log: Do not log training when doing hyperparameter optim (issue with writing the same file) :return: the vectorized environment, with appropriate wrappers """ # Do not log eval env (issue with writing the same file) log_dir = None if eval_env or no_log else self.save_path monitor_kwargs = {} # Special case for GoalEnvs: log success rate too if "Neck" in self.env_id or self.is_robotics_env(self.env_id) or "parking-v0" in self.env_id: monitor_kwargs = dict(info_keywords=("is_success",)) # On most env, SubprocVecEnv does not help and is quite memory hungry # therefore we use DummyVecEnv by default env = make_vec_env( env_id=self.env_id, n_envs=n_envs, seed=self.seed, env_kwargs=self.env_kwargs, monitor_dir=log_dir, wrapper_class=self.env_wrapper, vec_env_cls=self.vec_env_class, vec_env_kwargs=self.vec_env_kwargs, monitor_kwargs=monitor_kwargs, ) # Wrap the env into a VecNormalize wrapper if needed # and load saved statistics when present env = self._maybe_normalize(env, eval_env) # Optional Frame-stacking if self.frame_stack is not None: n_stack = self.frame_stack env = VecFrameStack(env, n_stack) if self.verbose > 0: print(f"Stacking {n_stack} frames") # Wrap if needed to re-order channels # (switch from channel last to channel first convention) if is_image_space(env.observation_space) and not is_image_space_channels_first(env.observation_space): if self.verbose > 0: print("Wrapping into a VecTransposeImage") env = VecTransposeImage(env) # check if wrapper for dict support is needed if self.algo == "her": if self.verbose > 0: print("Wrapping into a ObsDictWrapper") env = ObsDictWrapper(env) return env
def transpose_space(observation_space: spaces.Box, key: str = "") -> spaces.Box: """ Transpose an observation space (re-order channels). :param observation_space: :param key: In case of dictionary space, the key of the observation space. :return: """ # Sanity checks assert is_image_space(observation_space), "The observation space must be an image" assert not is_image_space_channels_first( observation_space ), f"The observation space {key} must follow the channel last convention" height, width, channels = observation_space.shape new_shape = (channels, height, width) return spaces.Box(low=0, high=255, shape=new_shape, dtype=observation_space.dtype)
def _wrap_env(env: GymEnv, verbose: int = 0) -> VecEnv: if not isinstance(env, VecEnv): if verbose >= 1: print("Wrapping the env in a DummyVecEnv.") env = DummyVecEnv([lambda: env]) if (is_image_space(env.observation_space) and not is_vecenv_wrapped(env, VecTransposeImage) and not is_image_space_channels_first(env.observation_space)): if verbose >= 1: print("Wrapping the env in a VecTransposeImage.") env = VecTransposeImage(env) # check if wrapper for dict support is needed when using HER if isinstance(env.observation_space, gym.spaces.dict.Dict): env = ObsDictWrapper(env) return env
def __init__(self, venv: VecEnv, n_stack: int, channels_order: Optional[str] = None): self.venv = venv self.n_stack = n_stack wrapped_obs_space = venv.observation_space assert isinstance( wrapped_obs_space, spaces.Box ), "VecFrameStack only work with gym.spaces.Box observation space" if channels_order is None: # Detect channel location automatically for images if is_image_space(wrapped_obs_space): self.channels_first = is_image_space_channels_first( wrapped_obs_space) else: # Default behavior for non-image space, stack on the last axis self.channels_first = False else: assert channels_order in { "last", "first" }, "`channels_order` must be one of following: 'last', 'first'" self.channels_first = channels_order == "first" # This includes the vec-env dimension (first) self.stack_dimension = 1 if self.channels_first else -1 repeat_axis = 0 if self.channels_first else -1 low = np.repeat(wrapped_obs_space.low, self.n_stack, axis=repeat_axis) high = np.repeat(wrapped_obs_space.high, self.n_stack, axis=repeat_axis) self.stackedobs = np.zeros((venv.num_envs, ) + low.shape, low.dtype) observation_space = spaces.Box(low=low, high=high, dtype=venv.observation_space.dtype) VecEnvWrapper.__init__(self, venv, observation_space=observation_space)
def compute_stacking( num_envs: int, n_stack: int, observation_space: spaces.Box, channels_order: Optional[str] = None, ) -> Tuple[bool, int, np.ndarray, int]: """ Calculates the parameters in order to stack observations :param num_envs: Number of environments in the stack :param n_stack: The number of observations to stack :param observation_space: The observation space :param channels_order: The order of the channels :return: tuple of channels_first, stack_dimension, stackedobs, repeat_axis """ channels_first = False if channels_order is None: # Detect channel location automatically for images if is_image_space(observation_space): channels_first = is_image_space_channels_first( observation_space) else: # Default behavior for non-image space, stack on the last axis channels_first = False else: assert channels_order in { "last", "first", }, "`channels_order` must be one of following: 'last', 'first'" channels_first = channels_order == "first" # This includes the vec-env dimension (first) stack_dimension = 1 if channels_first else -1 repeat_axis = 0 if channels_first else -1 low = np.repeat(observation_space.low, n_stack, axis=repeat_axis) stackedobs = np.zeros((num_envs, ) + low.shape, low.dtype) return channels_first, stack_dimension, stackedobs, repeat_axis
def create_envs(self, n_envs: int, eval_env: bool = False, no_log: bool = False) -> VecEnv: """ Create the environment and wrap it if necessary. :param n_envs: :param eval_env: Whether is it an environment used for evaluation or not :param no_log: Do not log training when doing hyperparameter optim (issue with writing the same file) :return: the vectorized environment, with appropriate wrappers """ # Do not log eval env (issue with writing the same file) log_dir = None if eval_env or no_log else self.save_path monitor_kwargs = {} # Special case for GoalEnvs: log success rate too if "Neck" in self.env_id or self.is_robotics_env( self.env_id) or "parking-v0" in self.env_id: monitor_kwargs = dict(info_keywords=("is_success", )) # On most env, SubprocVecEnv does not help and is quite memory hungry # therefore we use DummyVecEnv by default env = make_vec_env( env_id=self.env_id, n_envs=n_envs, seed=self.seed, env_kwargs=self.env_kwargs, monitor_dir=log_dir, wrapper_class=self.env_wrapper, vec_env_cls=self.vec_env_class, vec_env_kwargs=self.vec_env_kwargs, monitor_kwargs=monitor_kwargs, ) # Wrap the env into a VecNormalize wrapper if needed # and load saved statistics when present env = self._maybe_normalize(env, eval_env) # Optional Frame-stacking if self.frame_stack is not None: n_stack = self.frame_stack env = VecFrameStack(env, n_stack) if self.verbose > 0: print(f"Stacking {n_stack} frames") if not is_vecenv_wrapped(env, VecTransposeImage): wrap_with_vectranspose = False if isinstance(env.observation_space, gym.spaces.Dict): # If even one of the keys is a image-space in need of transpose, apply transpose # If the image spaces are not consistent (for instance one is channel first, # the other channel last), VecTransposeImage will throw an error for space in env.observation_space.spaces.values(): wrap_with_vectranspose = wrap_with_vectranspose or ( is_image_space(space) and not is_image_space_channels_first(space)) else: wrap_with_vectranspose = is_image_space( env.observation_space ) and not is_image_space_channels_first(env.observation_space) if wrap_with_vectranspose: if self.verbose >= 1: print("Wrapping the env in a VecTransposeImage.") env = VecTransposeImage(env) return env
def create_envs(self, n_envs: int, eval_env: bool = False, no_log: bool = False) -> VecEnv: """ Create the environment and wrap it if necessary. :param n_envs: :param eval_env: Whether is it an environment used for evaluation or not :param no_log: Do not log training when doing hyperparameter optim (issue with writing the same file) :return: the vectorized environment, with appropriate wrappers """ # Do not log eval env (issue with writing the same file) log_dir = None if eval_env or no_log else self.save_path monitor_kwargs = {} # Special case for GoalEnvs: log success rate too if "Neck" in self.env_id or self.is_robotics_env( self.env_id) or "parking-v0" in self.env_id: monitor_kwargs = dict(info_keywords=("is_success", )) # Note: made custom to support Gazebo Runtime wrapping def make_env(): def _init(): env = self.env_wrapper(env=self.env_id, **self.env_kwargs) env.seed(self.seed) env.action_space.seed(self.seed) monitor_path = log_dir if log_dir is not None else None if monitor_path is not None: os.makedirs(log_dir, exist_ok=True) env = Monitor(env, filename=monitor_path, **monitor_kwargs) return env return _init if self.vec_env_class is None: self.vec_env_class = DummyVecEnv env = self.vec_env_class([make_env()], **self.vec_env_kwargs) # Wrap the env into a VecNormalize wrapper if needed # and load saved statistics when present env = self._maybe_normalize(env, eval_env) # Optional Frame-stacking if self.frame_stack is not None: n_stack = self.frame_stack env = VecFrameStack(env, n_stack) if self.verbose > 0: print(f"Stacking {n_stack} frames") # Wrap if needed to re-order channels # (switch from channel last to channel first convention) if is_image_space( env.observation_space) and not is_image_space_channels_first( env.observation_space): if self.verbose > 0: print("Wrapping into a VecTransposeImage") env = VecTransposeImage(env) # check if wrapper for dict support is needed if self.algo == "her": if self.verbose > 0: print("Wrapping into a ObsDictWrapper") env = ObsDictWrapper(env) return env
def image_transpose(env): if is_image_space(env.observation_space) and not is_image_space_channels_first( env.observation_space ): env = VecTransposeImage(env) return env