mirror of
https://github.com/saymrwulf/stable-baselines3.git
synced 2026-05-26 22:45:15 +00:00
* First commit * Fixing missing refs from a quick merge from master * Reformat * Adding DictBuffers * Reformat * Minor reformat * added slow dict test. Added SACMultiInputPolicy for future. Added private static image transpose helper to common policy * Ran black on buffers * Ran isort * Adding StackedObservations classes used within VecStackEnvs wrappers. Made test_dict_env shorter and removed slow * Running isort :facepalm * Fixed typing issues * Adding docstrings and typing. Using util for moving data to device. * Fixed trailing commas * Fix types * Minor edits * Avoid duplicating code * Fix calls to parents * Adding assert to buffers. Updating changelong * Running format on buffers * Adding multi-input policies to dqn,td3,a2c. Fixing warnings. Fixed bug with DictReplayBuffer as Replay buffers use only 1 env * Fixing warnings, splitting is_vectorized_observation into multiple functions based on space type * Created envs folder in common. Updated imports. Moved stacked_obs to vec_env folder * Moved envs to envs directory. Moved stacked obs to vec_envs. Started update on documentation * Fixes * Running code style * Update docstrings on torch_layers * Decapitalize non-constant variables * Using NatureCNN architecture in combined extractor. Increasing img size in multi input env. Adding memory reduction in test * Update doc * Update doc * Fix format * Removing NineRoom env. Using nested preprocess. Removing mutable default args * running code style * Passing channel check through to stacked dict observations. * Running black * Adding channel control to SimpleMultiObsEnv. Passing check_channels to CombinedExtractor * Remove optimize memory for dict buffers * Update doc * Move identity env * Minor edits + bump version * Update doc * Fix doc build * Bug fixes + add support for more type of dict env * Fixes + add multi env test * Add support for vectranspose * Fix stacked obs for dict and add tests * Add check for nested spaces. Fix dict-subprocvecenv test * Fix (single) pytype error * Simplify CombinedExtractor * Fix tests * Fix check * Merge branch 'master' into feat/dict_observations * Fix for net_arch with dict and vector obs * Fixes * Add consistency test * Update env checker * Add some docs on dict obs * Update default CNN feature vector size * Refactor HER (#351) * Start refactoring HER * Fixes * Additional fixes * Faster tests * WIP: HER as a custom replay buffer * New replay only version (working with DQN) * Add support for all off-policy algorithms * Fix saving/loading * Remove ObsDictWrapper and add VecNormalize tests with dict * Stable-Baselines3 v1.0 (#354) * Bump version and update doc * Fix name * Apply suggestions from code review Co-authored-by: Adam Gleave <adam@gleave.me> * Update docs/index.rst Co-authored-by: Adam Gleave <adam@gleave.me> * Update wording for RL zoo Co-authored-by: Adam Gleave <adam@gleave.me> * Add gym-pybullet-drones project (#358) * Update projects.rst Added gym-pybullet-drones * Update projects.rst Longer title underline * Update changelog Co-authored-by: Antonin Raffin <antonin.raffin@ensta.org> * Include SuperSuit in projects (#359) * include supersuit * longer title underline * Update changelog.rst * Fix default arguments + add bugbear (#363) * Fix potential bug + add bug bear * Remove unused variables * Minor: version bump * Add code of conduct + update doc (#373) * Add code of conduct * Fix DQN doc example * Update doc (channel-last/first) * Apply suggestions from code review Co-authored-by: Anssi <kaneran21@hotmail.com> * Apply suggestions from code review Co-authored-by: Adam Gleave <adam@gleave.me> Co-authored-by: Anssi <kaneran21@hotmail.com> Co-authored-by: Adam Gleave <adam@gleave.me> * Make installation command compatible with ZSH (#376) * Add quotes * Add Zsh bracket info * Add clarify pip installation line * Make note bold * Add Zsh pip installation note * Add handle timeouts param * Fixes * Fixes (buffer size, extend test) * Fix `max_episode_length` redefinition * Fix potential issue * Add some docs on dict obs * Fix performance bug * Fix slowdown * Add package to install (#378) * Add package to install * Update docs packages installation command Co-authored-by: Antonin RAFFIN <antonin.raffin@ensta.org> * Fix backward compat + add test * Fix VecEnv detection * Update doc * Fix vec env check * Support for `VecMonitor` for gym3-style environments (#311) * add vectorized monitor * auto format of the code * add documentation and VecExtractDictObs * refactor and add test cases * add test cases and format * avoid circular import and fix doc * fix type * fix type * oops * Update stable_baselines3/common/monitor.py Co-authored-by: Antonin RAFFIN <antonin.raffin@ensta.org> * Update stable_baselines3/common/monitor.py Co-authored-by: Antonin RAFFIN <antonin.raffin@ensta.org> * add test cases * update changelog * fix mutable argument * quick fix * Apply suggestions from code review * fix terminal observation for gym3 envs * delete comment * Update doc and bump version * Add warning when already using `Monitor` wrapper * Update vecmonitor tests * Fixes Co-authored-by: Antonin RAFFIN <antonin.raffin@ensta.org> * Reformat * Fixed loading of ``ent_coef`` for ``SAC`` and ``TQC``, it was not optimized anymore (#392) * Fix ent coef loading bug * Add test * Add comment * Reuse save path * Add test for GAE + rename `RolloutBuffer.dones` for clarification (#375) * Fix return computation + add test for GAE * Rename `last_dones` to `episode_starts` for clarification * Revert advantage * Cleanup test * Rename variable * Clarify return computation * Clarify docs * Add multi-episode rollout test * Reformat Co-authored-by: Anssi "Miffyli" Kanervisto <kaneran21@hotmail.com> * Fixed saving of `A2C` and `PPO` policy when using gSDE (#401) * Improve doc and replay buffer loading * Add support for images * Fix doc * Update Procgen doc * Update changelog * Update docstrings Co-authored-by: Adam Gleave <adam@gleave.me> Co-authored-by: Jacopo Panerati <jacopo.panerati@utoronto.ca> Co-authored-by: Justin Terry <justinkterry@gmail.com> Co-authored-by: Anssi <kaneran21@hotmail.com> Co-authored-by: Tom Dörr <tomdoerr96@gmail.com> Co-authored-by: Tom Dörr <tom.doerr@tum.de> Co-authored-by: Costa Huang <costa.huang@outlook.com> * Update doc and minor fixes * Update doc * Added note about MultiInputPolicy in error of NatureCNN * Merge branch 'master' into feat/dict_observations * Address comments * Naming clarifications * Actually saving the file would be nice * Fix edge case when doing online sampling with HER * Cleanup * Add sanity check Co-authored-by: Antonin RAFFIN <antonin.raffin@ensta.org> Co-authored-by: Anssi "Miffyli" Kanervisto <kaneran21@hotmail.com> Co-authored-by: Adam Gleave <adam@gleave.me> Co-authored-by: Jacopo Panerati <jacopo.panerati@utoronto.ca> Co-authored-by: Justin Terry <justinkterry@gmail.com> Co-authored-by: Tom Dörr <tomdoerr96@gmail.com> Co-authored-by: Tom Dörr <tom.doerr@tum.de> Co-authored-by: Costa Huang <costa.huang@outlook.com>
264 lines
11 KiB
Python
264 lines
11 KiB
Python
import time
|
|
from typing import Any, Dict, List, Optional, Tuple, Type, Union
|
|
|
|
import gym
|
|
import numpy as np
|
|
import torch as th
|
|
|
|
from stable_baselines3.common import logger
|
|
from stable_baselines3.common.base_class import BaseAlgorithm
|
|
from stable_baselines3.common.buffers import DictRolloutBuffer, RolloutBuffer
|
|
from stable_baselines3.common.callbacks import BaseCallback
|
|
from stable_baselines3.common.policies import ActorCriticPolicy, BasePolicy
|
|
from stable_baselines3.common.type_aliases import GymEnv, MaybeCallback, Schedule
|
|
from stable_baselines3.common.utils import obs_as_tensor, safe_mean
|
|
from stable_baselines3.common.vec_env import VecEnv
|
|
|
|
|
|
class OnPolicyAlgorithm(BaseAlgorithm):
|
|
"""
|
|
The base for On-Policy algorithms (ex: A2C/PPO).
|
|
|
|
:param policy: The policy model to use (MlpPolicy, CnnPolicy, ...)
|
|
:param env: The environment to learn from (if registered in Gym, can be str)
|
|
:param learning_rate: The learning rate, it can be a function
|
|
of the current progress remaining (from 1 to 0)
|
|
:param n_steps: The number of steps to run for each environment per update
|
|
(i.e. batch size is n_steps * n_env where n_env is number of environment copies running in parallel)
|
|
:param gamma: Discount factor
|
|
:param gae_lambda: Factor for trade-off of bias vs variance for Generalized Advantage Estimator.
|
|
Equivalent to classic advantage when set to 1.
|
|
:param ent_coef: Entropy coefficient for the loss calculation
|
|
:param vf_coef: Value function coefficient for the loss calculation
|
|
:param max_grad_norm: The maximum value for the gradient clipping
|
|
:param use_sde: Whether to use generalized State Dependent Exploration (gSDE)
|
|
instead of action noise exploration (default: False)
|
|
:param sde_sample_freq: Sample a new noise matrix every n steps when using gSDE
|
|
Default: -1 (only sample at the beginning of the rollout)
|
|
:param policy_base: The base policy used by this method
|
|
:param tensorboard_log: the log location for tensorboard (if None, no logging)
|
|
:param create_eval_env: Whether to create a second environment that will be
|
|
used for evaluating the agent periodically. (Only available when passing string for the environment)
|
|
:param monitor_wrapper: When creating an environment, whether to wrap it
|
|
or not in a Monitor wrapper.
|
|
:param policy_kwargs: additional arguments to be passed to the policy on creation
|
|
:param verbose: the verbosity level: 0 no output, 1 info, 2 debug
|
|
:param seed: Seed for the pseudo random generators
|
|
:param device: Device (cpu, cuda, ...) on which the code should be run.
|
|
Setting it to auto, the code will be run on the GPU if possible.
|
|
:param _init_setup_model: Whether or not to build the network at the creation of the instance
|
|
:param supported_action_spaces: The action spaces supported by the algorithm.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
policy: Union[str, Type[ActorCriticPolicy]],
|
|
env: Union[GymEnv, str],
|
|
learning_rate: Union[float, Schedule],
|
|
n_steps: int,
|
|
gamma: float,
|
|
gae_lambda: float,
|
|
ent_coef: float,
|
|
vf_coef: float,
|
|
max_grad_norm: float,
|
|
use_sde: bool,
|
|
sde_sample_freq: int,
|
|
policy_base: Type[BasePolicy] = ActorCriticPolicy,
|
|
tensorboard_log: Optional[str] = None,
|
|
create_eval_env: bool = False,
|
|
monitor_wrapper: bool = True,
|
|
policy_kwargs: Optional[Dict[str, Any]] = None,
|
|
verbose: int = 0,
|
|
seed: Optional[int] = None,
|
|
device: Union[th.device, str] = "auto",
|
|
_init_setup_model: bool = True,
|
|
supported_action_spaces: Optional[Tuple[gym.spaces.Space, ...]] = None,
|
|
):
|
|
|
|
super(OnPolicyAlgorithm, self).__init__(
|
|
policy=policy,
|
|
env=env,
|
|
policy_base=policy_base,
|
|
learning_rate=learning_rate,
|
|
policy_kwargs=policy_kwargs,
|
|
verbose=verbose,
|
|
device=device,
|
|
use_sde=use_sde,
|
|
sde_sample_freq=sde_sample_freq,
|
|
create_eval_env=create_eval_env,
|
|
support_multi_env=True,
|
|
seed=seed,
|
|
tensorboard_log=tensorboard_log,
|
|
supported_action_spaces=supported_action_spaces,
|
|
)
|
|
|
|
self.n_steps = n_steps
|
|
self.gamma = gamma
|
|
self.gae_lambda = gae_lambda
|
|
self.ent_coef = ent_coef
|
|
self.vf_coef = vf_coef
|
|
self.max_grad_norm = max_grad_norm
|
|
self.rollout_buffer = None
|
|
|
|
if _init_setup_model:
|
|
self._setup_model()
|
|
|
|
def _setup_model(self) -> None:
|
|
self._setup_lr_schedule()
|
|
self.set_random_seed(self.seed)
|
|
|
|
buffer_cls = DictRolloutBuffer if isinstance(self.observation_space, gym.spaces.Dict) else RolloutBuffer
|
|
|
|
self.rollout_buffer = buffer_cls(
|
|
self.n_steps,
|
|
self.observation_space,
|
|
self.action_space,
|
|
self.device,
|
|
gamma=self.gamma,
|
|
gae_lambda=self.gae_lambda,
|
|
n_envs=self.n_envs,
|
|
)
|
|
self.policy = self.policy_class( # pytype:disable=not-instantiable
|
|
self.observation_space,
|
|
self.action_space,
|
|
self.lr_schedule,
|
|
use_sde=self.use_sde,
|
|
**self.policy_kwargs # pytype:disable=not-instantiable
|
|
)
|
|
self.policy = self.policy.to(self.device)
|
|
|
|
def collect_rollouts(
|
|
self,
|
|
env: VecEnv,
|
|
callback: BaseCallback,
|
|
rollout_buffer: RolloutBuffer,
|
|
n_rollout_steps: int,
|
|
) -> bool:
|
|
"""
|
|
Collect experiences using the current policy and fill a ``RolloutBuffer``.
|
|
The term rollout here refers to the model-free notion and should not
|
|
be used with the concept of rollout used in model-based RL or planning.
|
|
|
|
:param env: The training environment
|
|
:param callback: Callback that will be called at each step
|
|
(and at the beginning and end of the rollout)
|
|
:param rollout_buffer: Buffer to fill with rollouts
|
|
:param n_steps: Number of experiences to collect per environment
|
|
:return: True if function returned with at least `n_rollout_steps`
|
|
collected, False if callback terminated rollout prematurely.
|
|
"""
|
|
assert self._last_obs is not None, "No previous observation was provided"
|
|
n_steps = 0
|
|
rollout_buffer.reset()
|
|
# Sample new weights for the state dependent exploration
|
|
if self.use_sde:
|
|
self.policy.reset_noise(env.num_envs)
|
|
|
|
callback.on_rollout_start()
|
|
|
|
while n_steps < n_rollout_steps:
|
|
if self.use_sde and self.sde_sample_freq > 0 and n_steps % self.sde_sample_freq == 0:
|
|
# Sample a new noise matrix
|
|
self.policy.reset_noise(env.num_envs)
|
|
|
|
with th.no_grad():
|
|
# Convert to pytorch tensor or to TensorDict
|
|
obs_tensor = obs_as_tensor(self._last_obs, self.device)
|
|
actions, values, log_probs = self.policy.forward(obs_tensor)
|
|
actions = actions.cpu().numpy()
|
|
|
|
# Rescale and perform action
|
|
clipped_actions = actions
|
|
# Clip the actions to avoid out of bound error
|
|
if isinstance(self.action_space, gym.spaces.Box):
|
|
clipped_actions = np.clip(actions, self.action_space.low, self.action_space.high)
|
|
|
|
new_obs, rewards, dones, infos = env.step(clipped_actions)
|
|
|
|
self.num_timesteps += env.num_envs
|
|
|
|
# Give access to local variables
|
|
callback.update_locals(locals())
|
|
if callback.on_step() is False:
|
|
return False
|
|
|
|
self._update_info_buffer(infos)
|
|
n_steps += 1
|
|
|
|
if isinstance(self.action_space, gym.spaces.Discrete):
|
|
# Reshape in case of discrete action
|
|
actions = actions.reshape(-1, 1)
|
|
rollout_buffer.add(self._last_obs, actions, rewards, self._last_episode_starts, values, log_probs)
|
|
self._last_obs = new_obs
|
|
self._last_episode_starts = dones
|
|
|
|
with th.no_grad():
|
|
# Compute value for the last timestep
|
|
obs_tensor = obs_as_tensor(new_obs, self.device)
|
|
_, values, _ = self.policy.forward(obs_tensor)
|
|
|
|
rollout_buffer.compute_returns_and_advantage(last_values=values, dones=dones)
|
|
|
|
callback.on_rollout_end()
|
|
|
|
return True
|
|
|
|
def train(self) -> None:
|
|
"""
|
|
Consume current rollout data and update policy parameters.
|
|
Implemented by individual algorithms.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
def learn(
|
|
self,
|
|
total_timesteps: int,
|
|
callback: MaybeCallback = None,
|
|
log_interval: int = 1,
|
|
eval_env: Optional[GymEnv] = None,
|
|
eval_freq: int = -1,
|
|
n_eval_episodes: int = 5,
|
|
tb_log_name: str = "OnPolicyAlgorithm",
|
|
eval_log_path: Optional[str] = None,
|
|
reset_num_timesteps: bool = True,
|
|
) -> "OnPolicyAlgorithm":
|
|
iteration = 0
|
|
|
|
total_timesteps, callback = self._setup_learn(
|
|
total_timesteps, eval_env, callback, eval_freq, n_eval_episodes, eval_log_path, reset_num_timesteps, tb_log_name
|
|
)
|
|
|
|
callback.on_training_start(locals(), globals())
|
|
|
|
while self.num_timesteps < total_timesteps:
|
|
|
|
continue_training = self.collect_rollouts(self.env, callback, self.rollout_buffer, n_rollout_steps=self.n_steps)
|
|
|
|
if continue_training is False:
|
|
break
|
|
|
|
iteration += 1
|
|
self._update_current_progress_remaining(self.num_timesteps, total_timesteps)
|
|
|
|
# Display training infos
|
|
if log_interval is not None and iteration % log_interval == 0:
|
|
fps = int(self.num_timesteps / (time.time() - self.start_time))
|
|
logger.record("time/iterations", iteration, exclude="tensorboard")
|
|
if len(self.ep_info_buffer) > 0 and len(self.ep_info_buffer[0]) > 0:
|
|
logger.record("rollout/ep_rew_mean", safe_mean([ep_info["r"] for ep_info in self.ep_info_buffer]))
|
|
logger.record("rollout/ep_len_mean", safe_mean([ep_info["l"] for ep_info in self.ep_info_buffer]))
|
|
logger.record("time/fps", fps)
|
|
logger.record("time/time_elapsed", int(time.time() - self.start_time), exclude="tensorboard")
|
|
logger.record("time/total_timesteps", self.num_timesteps, exclude="tensorboard")
|
|
logger.dump(step=self.num_timesteps)
|
|
|
|
self.train()
|
|
|
|
callback.on_training_end()
|
|
|
|
return self
|
|
|
|
def _get_torch_save_params(self) -> Tuple[List[str], List[str]]:
|
|
state_dicts = ["policy", "policy.optimizer"]
|
|
|
|
return state_dicts, []
|