From a512150aab894f8a527e366d4cc65bf5d4264585 Mon Sep 17 00:00:00 2001 From: Trinkle23897 <463003665@qq.com> Date: Mon, 7 Sep 2020 07:25:45 +0800 Subject: [PATCH 1/6] fix bugs in MAPolicy --- README.md | 2 +- docs/index.rst | 6 +++--- tianshou/policy/imitation/base.py | 2 +- tianshou/policy/multiagent/mapolicy.py | 7 ++++--- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index b2a207997..3d321af32 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Here is Tianshou's other features: - Elegant framework, using only ~2000 lines of code - Support parallel environment simulation (synchronous or asynchronous) for all algorithms [Usage](https://tianshou.readthedocs.io/en/latest/tutorials/cheatsheet.html#parallel-sampling) - Support recurrent state representation in actor network and critic network (RNN-style training for POMDP) [Usage](https://tianshou.readthedocs.io/en/latest/tutorials/cheatsheet.html#rnn-style-training) -- Support any type of environment state (e.g. a dict, a self-defined class, ...) [Usage](https://tianshou.readthedocs.io/en/latest/tutorials/cheatsheet.html#user-defined-environment-and-different-state-representation) +- Support any type of environment state/action (e.g. a dict, a self-defined class, ...) [Usage](https://tianshou.readthedocs.io/en/latest/tutorials/cheatsheet.html#user-defined-environment-and-different-state-representation) - Support customized training process [Usage](https://tianshou.readthedocs.io/en/latest/tutorials/cheatsheet.html#customize-training-process) - Support n-step returns estimation and prioritized experience replay for all Q-learning based algorithms; GAE, nstep and PER are very fast thanks to numba jit function and vectorized numpy operation - Support multi-agent RL [Usage](https://tianshou.readthedocs.io/en/latest/tutorials/cheatsheet.html##multi-agent-reinforcement-learning) diff --git a/docs/index.rst b/docs/index.rst index dba58bd72..531e12c1d 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -24,11 +24,11 @@ Welcome to Tianshou! Here is Tianshou's other features: * Elegant framework, using only ~2000 lines of code -* Support parallel environment sampling for all algorithms: :ref:`parallel_sampling` -* Support recurrent state representation in actor network and critic network (RNN-style training for POMDP): :ref:`rnn_training` +* Support parallel environment simulation (synchronous or asynchronous) for all algorithms: :ref:`parallel_sampling` +* Support recurrent state/action representation in actor network and critic network (RNN-style training for POMDP): :ref:`rnn_training` * Support any type of environment state (e.g. a dict, a self-defined class, ...): :ref:`self_defined_env` * Support customized training process: :ref:`customize_training` -* Support n-step returns estimation :meth:`~tianshou.policy.BasePolicy.compute_nstep_return` and prioritized experience replay for all Q-learning based algorithms +* Support n-step returns estimation :meth:`~tianshou.policy.BasePolicy.compute_nstep_return` and prioritized experience replay :class:`~tianshou.data.PrioritizedReplayBuffer` for all Q-learning based algorithms; GAE, nstep and PER are very fast thanks to numba jit function and vectorized numpy operation * Support multi-agent RL: :doc:`/tutorials/tictactoe` 中文文档位于 `https://tianshou.readthedocs.io/zh/latest/ `_ diff --git a/tianshou/policy/imitation/base.py b/tianshou/policy/imitation/base.py index c01e45fd0..674de0429 100644 --- a/tianshou/policy/imitation/base.py +++ b/tianshou/policy/imitation/base.py @@ -8,7 +8,7 @@ class ImitationPolicy(BasePolicy): - """Implementation of vanilla imitation learning (for continuous action space). + """Implementation of vanilla imitation learning. :param torch.nn.Module model: a model following the rules in :class:`~tianshou.policy.BasePolicy`. (s -> a) diff --git a/tianshou/policy/multiagent/mapolicy.py b/tianshou/policy/multiagent/mapolicy.py index c0d991d63..1a38cbb2a 100644 --- a/tianshou/policy/multiagent/mapolicy.py +++ b/tianshou/policy/multiagent/mapolicy.py @@ -36,7 +36,8 @@ def process_fn(self, batch: Batch, buffer: ReplayBuffer, # reward can be empty Batch (after initial reset) or nparray. has_rew = isinstance(buffer.rew, np.ndarray) if has_rew: # save the original reward in save_rew - save_rew, buffer.rew = buffer.rew, Batch() + # since buffer.__setattr__ is not override, here we use _meta + save_rew, buffer._meta.rew = buffer.rew, Batch() for policy in self.policies: agent_index = np.nonzero(batch.obs.agent_id == policy.agent_id)[0] if len(agent_index) == 0: @@ -45,11 +46,11 @@ def process_fn(self, batch: Batch, buffer: ReplayBuffer, tmp_batch, tmp_indice = batch[agent_index], indice[agent_index] if has_rew: tmp_batch.rew = tmp_batch.rew[:, policy.agent_id - 1] - buffer.rew = save_rew[:, policy.agent_id - 1] + buffer._meta.rew = save_rew[:, policy.agent_id - 1] results[f'agent_{policy.agent_id}'] = \ policy.process_fn(tmp_batch, buffer, tmp_indice) if has_rew: # restore from save_rew - buffer.rew = save_rew + buffer._meta.rew = save_rew return Batch(results) def forward(self, batch: Batch, From 660faea041204a3779cc146bc238c9e63faeb9dd Mon Sep 17 00:00:00 2001 From: Trinkle23897 <463003665@qq.com> Date: Mon, 7 Sep 2020 19:37:55 +0800 Subject: [PATCH 2/6] sync bipedal results --- examples/box2d/README.md | 7 ++++++ examples/box2d/bipedal_hardcore_sac.py | 20 ++++++++++-------- .../box2d/results/sac/BipedalHardcore.png | Bin 0 -> 40704 bytes 3 files changed, 18 insertions(+), 9 deletions(-) create mode 100644 examples/box2d/README.md create mode 100644 examples/box2d/results/sac/BipedalHardcore.png diff --git a/examples/box2d/README.md b/examples/box2d/README.md new file mode 100644 index 000000000..0935534b4 --- /dev/null +++ b/examples/box2d/README.md @@ -0,0 +1,7 @@ +# Bipedal-Hardcore-SAC + +- Our default choice: remove the done flag penalty, will soon converge to \~250 reward within 100 epochs (10M env steps, 3~4 hours, see the image below) +- If the done penalty is not removed, it converges much slower than before, about 200 epochs (20M env steps) to reach the same performance (\~200 reward) +- Action noise is only necessary in the beginning. It is a negative impact at the end of the training. Removing it can reach \~255 (our best result under the original env, no done penalty removed). + +![](results/sac/BipedalHardcore.png) \ No newline at end of file diff --git a/examples/box2d/bipedal_hardcore_sac.py b/examples/box2d/bipedal_hardcore_sac.py index a92963d83..d93660f66 100644 --- a/examples/box2d/bipedal_hardcore_sac.py +++ b/examples/box2d/bipedal_hardcore_sac.py @@ -24,13 +24,13 @@ def get_args(): parser.add_argument('--gamma', type=float, default=0.99) parser.add_argument('--tau', type=float, default=0.005) parser.add_argument('--alpha', type=float, default=0.1) - parser.add_argument('--epoch', type=int, default=1000) - parser.add_argument('--step-per-epoch', type=int, default=2400) + parser.add_argument('--epoch', type=int, default=100) + parser.add_argument('--step-per-epoch', type=int, default=10000) parser.add_argument('--collect-per-step', type=int, default=10) parser.add_argument('--batch-size', type=int, default=128) parser.add_argument('--layer-num', type=int, default=1) parser.add_argument('--training-num', type=int, default=8) - parser.add_argument('--test-num', type=int, default=8) + parser.add_argument('--test-num', type=int, default=100) parser.add_argument('--logdir', type=str, default='log') parser.add_argument('--render', type=float, default=0.) parser.add_argument('--rew-norm', type=int, default=0) @@ -39,14 +39,13 @@ def get_args(): parser.add_argument( '--device', type=str, default='cuda' if torch.cuda.is_available() else 'cpu') + parser.add_argument('--resume_path', type=str, default=None) return parser.parse_args() class EnvWrapper(object): """Env wrapper for reward scale, action repeat and action noise""" - - def __init__(self, task, action_repeat=3, - reward_scale=5, act_noise=0.3): + def __init__(self, task, action_repeat=3, reward_scale=5, act_noise=0.3): self._env = gym.make(task) self.action_repeat = action_repeat self.reward_scale = reward_scale @@ -70,8 +69,6 @@ def step(self, action): def test_sac_bipedal(args=get_args()): - torch.set_num_threads(1) # we just need only one thread for NN - env = EnvWrapper(args.task) def IsStop(reward): @@ -118,6 +115,10 @@ def IsStop(reward): reward_normalization=args.rew_norm, ignore_done=args.ignore_done, estimation_step=args.n_step) + # load a previous policy + if args.resume_path: + policy.load_state_dict(torch.load(args.resume_path)) + print("Loaded agent from: ", args.resume_path) # collector train_collector = Collector( @@ -135,7 +136,8 @@ def save_fn(policy): result = offpolicy_trainer( policy, train_collector, test_collector, args.epoch, args.step_per_epoch, args.collect_per_step, args.test_num, - args.batch_size, stop_fn=IsStop, save_fn=save_fn, writer=writer) + args.batch_size, stop_fn=IsStop, save_fn=save_fn, writer=writer, + test_in_train=False) if __name__ == '__main__': pprint.pprint(result) diff --git a/examples/box2d/results/sac/BipedalHardcore.png b/examples/box2d/results/sac/BipedalHardcore.png new file mode 100644 index 0000000000000000000000000000000000000000..0b41969558239c056e2122bdc6975257b5572599 GIT binary patch literal 40704 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 literal 0 HcmV?d00001 From f8275ed3b4a26ebda5ed4e4c03d172c0e0a62240 Mon Sep 17 00:00:00 2001 From: Trinkle23897 <463003665@qq.com> Date: Mon, 7 Sep 2020 20:59:11 +0800 Subject: [PATCH 3/6] version test --- README.md | 4 ++-- docs/index.rst | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3d321af32..eebbfde64 100644 --- a/README.md +++ b/README.md @@ -74,8 +74,8 @@ $ pip install tianshou After installation, open your python console and type ```python -import tianshou as ts -print(ts.__version__) +import tianshou +print(tianshou.__version__) ``` If no error occurs, you have successfully installed Tianshou. diff --git a/docs/index.rst b/docs/index.rst index 531e12c1d..bfb2ddfdf 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -63,8 +63,8 @@ If you use Anaconda or Miniconda, you can install Tianshou through the following After installation, open your python console and type :: - import tianshou as ts - print(ts.__version__) + import tianshou + print(tianshou.__version__) If no error occurs, you have successfully installed Tianshou. From 62c2bf3a1d3a644b315261dcc6badf12c7c6688e Mon Sep 17 00:00:00 2001 From: Trinkle23897 <463003665@qq.com> Date: Tue, 8 Sep 2020 20:17:24 +0800 Subject: [PATCH 4/6] bump to v0.2.7 --- setup.py | 95 +++++++++++++++++++++++--------------------- tianshou/__init__.py | 2 +- 2 files changed, 50 insertions(+), 47 deletions(-) diff --git a/setup.py b/setup.py index 175112c44..93acc4a7e 100644 --- a/setup.py +++ b/setup.py @@ -1,69 +1,72 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +import os from setuptools import setup, find_packages +def get_version() -> str: + # https://packaging.python.org/guides/single-sourcing-package-version/ + init = open(os.path.join("tianshou", "__init__.py"), "r").read().split() + return init[init.index("__version__") + 2][1:-1] + + setup( - name='tianshou', - version='0.2.6', - description='A Library for Deep Reinforcement Learning', - long_description=open('README.md', encoding='utf8').read(), - long_description_content_type='text/markdown', - url='https://github.com/thu-ml/tianshou', - author='TSAIL', - author_email='trinkle23897@gmail.com', - license='MIT', - python_requires='>=3.6', + name="tianshou", + version=get_version(), + description="A Library for Deep Reinforcement Learning", + long_description=open("README.md", encoding="utf8").read(), + long_description_content_type="text/markdown", + url="https://github.com/thu-ml/tianshou", + author="TSAIL", + author_email="trinkle23897@gmail.com", + license="MIT", + python_requires=">=3.6", classifiers=[ # How mature is this project? Common values are # 3 - Alpha # 4 - Beta # 5 - Production/Stable - 'Development Status :: 3 - Alpha', + "Development Status :: 4 - Beta", # Indicate who your project is intended for - 'Intended Audience :: Science/Research', - 'Topic :: Scientific/Engineering :: Artificial Intelligence', - 'Topic :: Software Development :: Libraries :: Python Modules', + "Intended Audience :: Science/Research", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Topic :: Software Development :: Libraries :: Python Modules", # Pick your license as you wish (should match "license" above) - 'License :: OSI Approved :: MIT License', + "License :: OSI Approved :: MIT License", # Specify the Python versions you support here. In particular, ensure # that you indicate whether you support Python 2, Python 3 or both. - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", ], - keywords='reinforcement learning platform pytorch', - packages=find_packages(exclude=['test', 'test.*', - 'examples', 'examples.*', - 'docs', 'docs.*']), + keywords="reinforcement learning platform pytorch", + packages=find_packages( + exclude=["test", "test.*", "examples", "examples.*", "docs", "docs.*"] + ), install_requires=[ - 'gym>=0.15.4', - 'tqdm', - 'numpy', - 'tensorboard', - 'torch>=1.4.0', - 'numba>=0.51.0', + "gym>=0.15.4", + "tqdm", + "numpy", + "tensorboard", + "torch>=1.4.0", + "numba>=0.51.0", ], extras_require={ - 'dev': [ - 'Sphinx', - 'sphinx_rtd_theme', - 'sphinxcontrib-bibtex', - 'flake8', - 'pytest', - 'pytest-cov', - 'ray>=0.8.0', - ], - 'atari': [ - 'atari_py', - 'cv2', - ], - 'mujoco': [ - 'mujoco_py', - ], - 'pybullet': [ - 'pybullet', + "dev": [ + "Sphinx", + "sphinx_rtd_theme", + "sphinxcontrib-bibtex", + "flake8", + "pytest", + "pytest-cov", + "ray>=0.8.0", + "mypy", + "pydocstyle", + "doc8", ], + "atari": ["atari_py", "cv2"], + "mujoco": ["mujoco_py"], + "pybullet": ["pybullet"], }, ) diff --git a/tianshou/__init__.py b/tianshou/__init__.py index d44b4dc5a..e03d8640c 100644 --- a/tianshou/__init__.py +++ b/tianshou/__init__.py @@ -5,7 +5,7 @@ utils.pre_compile() -__version__ = '0.2.6' +__version__ = '0.2.7' __all__ = [ 'env', From e376bbc432ae9a66d1b2dd32b55cfb8bd1bf6160 Mon Sep 17 00:00:00 2001 From: Trinkle23897 <463003665@qq.com> Date: Tue, 8 Sep 2020 20:36:46 +0800 Subject: [PATCH 5/6] polish --- examples/box2d/bipedal_hardcore_sac.py | 1 + setup.py | 2 +- tianshou/policy/multiagent/mapolicy.py | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/box2d/bipedal_hardcore_sac.py b/examples/box2d/bipedal_hardcore_sac.py index d93660f66..4e123719b 100644 --- a/examples/box2d/bipedal_hardcore_sac.py +++ b/examples/box2d/bipedal_hardcore_sac.py @@ -45,6 +45,7 @@ def get_args(): class EnvWrapper(object): """Env wrapper for reward scale, action repeat and action noise""" + def __init__(self, task, action_repeat=3, reward_scale=5, act_noise=0.3): self._env = gym.make(task) self.action_repeat = action_repeat diff --git a/setup.py b/setup.py index 93acc4a7e..e8280f536 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ def get_version() -> str: # 3 - Alpha # 4 - Beta # 5 - Production/Stable - "Development Status :: 4 - Beta", + "Development Status :: 3 - Alpha", # Indicate who your project is intended for "Intended Audience :: Science/Research", "Topic :: Scientific/Engineering :: Artificial Intelligence", diff --git a/tianshou/policy/multiagent/mapolicy.py b/tianshou/policy/multiagent/mapolicy.py index 1a38cbb2a..74ab0ecac 100644 --- a/tianshou/policy/multiagent/mapolicy.py +++ b/tianshou/policy/multiagent/mapolicy.py @@ -36,7 +36,8 @@ def process_fn(self, batch: Batch, buffer: ReplayBuffer, # reward can be empty Batch (after initial reset) or nparray. has_rew = isinstance(buffer.rew, np.ndarray) if has_rew: # save the original reward in save_rew - # since buffer.__setattr__ is not override, here we use _meta + # Since we do not override buffer.__setattr__, here we use _meta to + # change buffer.rew, otherwise buffer.rew = Batch() has no effect. save_rew, buffer._meta.rew = buffer.rew, Batch() for policy in self.policies: agent_index = np.nonzero(batch.obs.agent_id == policy.agent_id)[0] From 96a2cc84cc74e841c23d61b9e6e25a2595557fee Mon Sep 17 00:00:00 2001 From: Trinkle23897 <463003665@qq.com> Date: Tue, 8 Sep 2020 20:40:19 +0800 Subject: [PATCH 6/6] revert --- setup.py | 86 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 45 insertions(+), 41 deletions(-) diff --git a/setup.py b/setup.py index e8280f536..789ea2d57 100644 --- a/setup.py +++ b/setup.py @@ -12,61 +12,65 @@ def get_version() -> str: setup( - name="tianshou", + name='tianshou', version=get_version(), - description="A Library for Deep Reinforcement Learning", - long_description=open("README.md", encoding="utf8").read(), - long_description_content_type="text/markdown", - url="https://github.com/thu-ml/tianshou", - author="TSAIL", - author_email="trinkle23897@gmail.com", - license="MIT", - python_requires=">=3.6", + description='A Library for Deep Reinforcement Learning', + long_description=open('README.md', encoding='utf8').read(), + long_description_content_type='text/markdown', + url='https://github.com/thu-ml/tianshou', + author='TSAIL', + author_email='trinkle23897@gmail.com', + license='MIT', + python_requires='>=3.6', classifiers=[ # How mature is this project? Common values are # 3 - Alpha # 4 - Beta # 5 - Production/Stable - "Development Status :: 3 - Alpha", + 'Development Status :: 3 - Alpha', # Indicate who your project is intended for - "Intended Audience :: Science/Research", - "Topic :: Scientific/Engineering :: Artificial Intelligence", - "Topic :: Software Development :: Libraries :: Python Modules", + 'Intended Audience :: Science/Research', + 'Topic :: Scientific/Engineering :: Artificial Intelligence', + 'Topic :: Software Development :: Libraries :: Python Modules', # Pick your license as you wish (should match "license" above) - "License :: OSI Approved :: MIT License", + 'License :: OSI Approved :: MIT License', # Specify the Python versions you support here. In particular, ensure # that you indicate whether you support Python 2, Python 3 or both. - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', ], - keywords="reinforcement learning platform pytorch", - packages=find_packages( - exclude=["test", "test.*", "examples", "examples.*", "docs", "docs.*"] - ), + keywords='reinforcement learning platform pytorch', + packages=find_packages(exclude=['test', 'test.*', + 'examples', 'examples.*', + 'docs', 'docs.*']), install_requires=[ - "gym>=0.15.4", - "tqdm", - "numpy", - "tensorboard", - "torch>=1.4.0", - "numba>=0.51.0", + 'gym>=0.15.4', + 'tqdm', + 'numpy', + 'tensorboard', + 'torch>=1.4.0', + 'numba>=0.51.0', ], extras_require={ - "dev": [ - "Sphinx", - "sphinx_rtd_theme", - "sphinxcontrib-bibtex", - "flake8", - "pytest", - "pytest-cov", - "ray>=0.8.0", - "mypy", - "pydocstyle", - "doc8", + 'dev': [ + 'Sphinx', + 'sphinx_rtd_theme', + 'sphinxcontrib-bibtex', + 'flake8', + 'pytest', + 'pytest-cov', + 'ray>=0.8.0', + ], + 'atari': [ + 'atari_py', + 'cv2', + ], + 'mujoco': [ + 'mujoco_py', + ], + 'pybullet': [ + 'pybullet', ], - "atari": ["atari_py", "cv2"], - "mujoco": ["mujoco_py"], - "pybullet": ["pybullet"], }, )