diff --git a/codes/A2C/agent.py b/codes/A2C/agent.py index 9de9aab..e095bc5 100644 --- a/codes/A2C/agent.py +++ b/codes/A2C/agent.py @@ -12,10 +12,10 @@ Environment: import torch.optim as optim from A2C.model import ActorCritic class A2C: - def __init__(self,state_dim,action_dim,cfg) -> None: + def __init__(self,n_states,n_actions,cfg) -> None: self.gamma = cfg.gamma self.device = cfg.device - self.model = ActorCritic(state_dim, action_dim, cfg.hidden_size).to(self.device) + self.model = ActorCritic(n_states, n_actions, cfg.hidden_size).to(self.device) self.optimizer = optim.Adam(self.model.parameters()) def compute_returns(self,next_value, rewards, masks): diff --git a/codes/A2C/model.py b/codes/A2C/model.py index 5e77d4d..473bcb2 100644 --- a/codes/A2C/model.py +++ b/codes/A2C/model.py @@ -13,19 +13,19 @@ import torch.nn as nn import torch.nn.functional as F from torch.distributions import Categorical class ActorCritic(nn.Module): - def __init__(self, num_inputs, num_outputs, hidden_size, std=0.0): + def __init__(self, n_states, n_actions, hidden_dim): super(ActorCritic, self).__init__() self.critic = nn.Sequential( - nn.Linear(num_inputs, hidden_size), + nn.Linear(n_states, hidden_dim), nn.ReLU(), - nn.Linear(hidden_size, 1) + nn.Linear(hidden_dim, 1) ) self.actor = nn.Sequential( - nn.Linear(num_inputs, hidden_size), + nn.Linear(n_states, hidden_dim), nn.ReLU(), - nn.Linear(hidden_size, num_outputs), + nn.Linear(hidden_dim, n_actions), nn.Softmax(dim=1), ) diff --git a/codes/A2C/task0_train.ipynb b/codes/A2C/task0_train.ipynb new file mode 100644 index 0000000..aa9b772 --- /dev/null +++ b/codes/A2C/task0_train.ipynb @@ -0,0 +1,265 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "from pathlib import Path\n", + "curr_path = str(Path().absolute()) # 当前路径\n", + "parent_path = str(Path().absolute().parent) # 父路径\n", + "sys.path.append(parent_path) # 添加路径到系统路径\n", + "import math\n", + "import random\n", + "\n", + "import gym\n", + "import numpy as np\n", + "\n", + "import torch\n", + "import torch.nn as nn\n", + "import torch.optim as optim\n", + "import torch.nn.functional as F\n", + "from torch.distributions import Categorical\n", + "\n", + "from IPython.display import clear_output\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "use_cuda = torch.cuda.is_available()\n", + "device = torch.device(\"cuda\" if use_cuda else \"cpu\")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "from common.multiprocessing_env import SubprocVecEnv\n", + "\n", + "num_envs = 16\n", + "env_name = \"CartPole-v0\"\n", + "\n", + "def make_env():\n", + " def _thunk():\n", + " env = gym.make(env_name)\n", + " return env\n", + "\n", + " return _thunk\n", + "\n", + "envs = [make_env() for i in range(num_envs)]\n", + "envs = SubprocVecEnv(envs)\n", + "\n", + "env = gym.make(env_name)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "class ActorCritic(nn.Module):\n", + " def __init__(self, num_inputs, num_outputs, hidden_size, std=0.0):\n", + " super(ActorCritic, self).__init__()\n", + " \n", + " self.critic = nn.Sequential(\n", + " nn.Linear(num_inputs, hidden_size),\n", + " nn.ReLU(),\n", + " nn.Linear(hidden_size, 1)\n", + " )\n", + " \n", + " self.actor = nn.Sequential(\n", + " nn.Linear(num_inputs, hidden_size),\n", + " nn.ReLU(),\n", + " nn.Linear(hidden_size, num_outputs),\n", + " nn.Softmax(dim=1),\n", + " )\n", + " \n", + " def forward(self, x):\n", + " value = self.critic(x)\n", + " probs = self.actor(x)\n", + " dist = Categorical(probs)\n", + " return dist, value" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "def plot(frame_idx, rewards):\n", + " clear_output(True)\n", + " plt.figure(figsize=(20,5))\n", + " plt.subplot(131)\n", + " plt.title('frame %s. reward: %s' % (frame_idx, rewards[-1]))\n", + " plt.plot(rewards)\n", + " plt.show()\n", + " \n", + "def test_env(vis=False):\n", + " state = env.reset()\n", + " if vis: env.render()\n", + " done = False\n", + " total_reward = 0\n", + " while not done:\n", + " state = torch.FloatTensor(state).unsqueeze(0).to(device)\n", + " dist, _ = model(state)\n", + " next_state, reward, done, _ = env.step(dist.sample().cpu().numpy()[0])\n", + " state = next_state\n", + " if vis: env.render()\n", + " total_reward += reward\n", + " return total_reward" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "def compute_returns(next_value, rewards, masks, gamma=0.99):\n", + " R = next_value\n", + " returns = []\n", + " for step in reversed(range(len(rewards))):\n", + " R = rewards[step] + gamma * R * masks[step]\n", + " returns.insert(0, R)\n", + " return returns" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "num_inputs = envs.observation_space.shape[0]\n", + "num_outputs = envs.action_space.n\n", + "\n", + "#Hyper params:\n", + "hidden_size = 256\n", + "lr = 3e-4\n", + "num_steps = 5\n", + "\n", + "model = ActorCritic(num_inputs, num_outputs, hidden_size).to(device)\n", + "optimizer = optim.Adam(model.parameters())" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "max_frames = 20000\n", + "frame_idx = 0\n", + "test_rewards = []" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "state = envs.reset()\n", + "\n", + "while frame_idx < max_frames:\n", + "\n", + " log_probs = []\n", + " values = []\n", + " rewards = []\n", + " masks = []\n", + " entropy = 0\n", + "\n", + " for _ in range(num_steps):\n", + " state = torch.FloatTensor(state).to(device)\n", + " dist, value = model(state)\n", + "\n", + " action = dist.sample()\n", + " next_state, reward, done, _ = envs.step(action.cpu().numpy())\n", + "\n", + " log_prob = dist.log_prob(action)\n", + " entropy += dist.entropy().mean()\n", + " \n", + " log_probs.append(log_prob)\n", + " values.append(value)\n", + " rewards.append(torch.FloatTensor(reward).unsqueeze(1).to(device))\n", + " masks.append(torch.FloatTensor(1 - done).unsqueeze(1).to(device))\n", + " \n", + " state = next_state\n", + " frame_idx += 1\n", + " \n", + " if frame_idx % 1000 == 0:\n", + " test_rewards.append(np.mean([test_env() for _ in range(10)]))\n", + " plot(frame_idx, test_rewards)\n", + " \n", + " next_state = torch.FloatTensor(next_state).to(device)\n", + " _, next_value = model(next_state)\n", + " returns = compute_returns(next_value, rewards, masks)\n", + " \n", + " log_probs = torch.cat(log_probs)\n", + " returns = torch.cat(returns).detach()\n", + " values = torch.cat(values)\n", + "\n", + " advantage = returns - values\n", + "\n", + " actor_loss = -(log_probs * advantage.detach()).mean()\n", + " critic_loss = advantage.pow(2).mean()\n", + "\n", + " loss = actor_loss + 0.5 * critic_loss - 0.001 * entropy\n", + "\n", + " optimizer.zero_grad()\n", + " loss.backward()\n", + " optimizer.step()" + ] + } + ], + "metadata": { + "interpreter": { + "hash": "fe38df673a99c62a9fea33a7aceda74c9b65b12ee9d076c5851d98b692a4989a" + }, + "kernelspec": { + "display_name": "Python 3.7.9 64-bit ('py37': conda)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.9" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/codes/A2C/task0_train.py b/codes/A2C/task0_train.py index 69f6976..5927048 100644 --- a/codes/A2C/task0_train.py +++ b/codes/A2C/task0_train.py @@ -1,8 +1,7 @@ import sys,os -curr_path = os.path.dirname(__file__) -parent_path = os.path.dirname(curr_path) -sys.path.append(parent_path) # add current terminal path to sys.path - +curr_path = os.path.dirname(os.path.abspath(__file__)) # 当前文件所在绝对路径 +parent_path = os.path.dirname(curr_path) # 父路径 +sys.path.append(parent_path) # 添加路径到系统路径sys.path import gym import numpy as np @@ -17,17 +16,28 @@ from common.plot import plot_rewards curr_time = datetime.datetime.now().strftime("%Y%m%d-%H%M%S") # obtain current time class A2CConfig: def __init__(self) -> None: - self.algo='A2C' - self.env= 'CartPole-v0' - self.result_path = curr_path+"/outputs/" +self.env+'/'+curr_time+'/results/' # path to save results - self.model_path = curr_path+"/outputs/" +self.env+'/'+curr_time+'/models/' # path to save models - self.n_envs = 8 - self.gamma = 0.99 - self.hidden_size = 256 + self.algo='A2C' # 算法名称 + self.env_name= 'CartPole-v0' # 环境名称 + self.n_envs = 8 # 异步的环境数目 + self.gamma = 0.99 # 强化学习中的折扣因子 + self.hidden_dim = 256 self.lr = 1e-3 # learning rate self.max_frames = 30000 self.n_steps = 5 self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") +class PlotConfig: + def __init__(self) -> None: + self.algo = "DQN" # 算法名称 + self.env_name = 'CartPole-v0' # 环境名称 + self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # 检测GPU + + self.result_path = curr_path+"/outputs/" + self.env_name + \ + '/'+curr_time+'/results/' # 保存结果的路径 + self.model_path = curr_path+"/outputs/" + self.env_name + \ + '/'+curr_time+'/models/' # 保存模型的路径 + self.save = True # 是否保存图片 + + def make_envs(env_name): def _thunk(): env = gym.make(env_name) @@ -57,11 +67,11 @@ def compute_returns(next_value, rewards, masks, gamma=0.99): def train(cfg,envs): - env = gym.make(cfg.env) # a single env + env = gym.make(cfg.env_name) # a single env env.seed(10) state_dim = envs.observation_space.shape[0] action_dim = envs.action_space.n - model = ActorCritic(state_dim, action_dim, cfg.hidden_size).to(cfg.device) + model = ActorCritic(state_dim, action_dim, cfg.hidden_dim).to(cfg.device) optimizer = optim.Adam(model.parameters()) frame_idx = 0 test_rewards = [] @@ -112,9 +122,11 @@ def train(cfg,envs): return test_rewards, test_ma_rewards if __name__ == "__main__": cfg = A2CConfig() - envs = [make_envs(cfg.env) for i in range(cfg.n_envs)] - envs = SubprocVecEnv(envs) # 8 env + plot_cfg = PlotConfig() + envs = [make_envs(cfg.env_name) for i in range(cfg.n_envs)] + envs = SubprocVecEnv(envs) + # 训练 rewards,ma_rewards = train(cfg,envs) - make_dir(cfg.result_path,cfg.model_path) - save_results(rewards,ma_rewards,tag='train',path=cfg.result_path) - plot_rewards(rewards,ma_rewards,tag="train",env=cfg.env,algo = cfg.algo,path=cfg.result_path) + make_dir(plot_cfg.result_path,plot_cfg.model_path) + save_results(rewards, ma_rewards, tag='train', path=plot_cfg.result_path) # 保存结果 + plot_rewards(rewards, ma_rewards, plot_cfg, tag="train") # 画出结果 diff --git a/codes/DDPG/outputs/Pendulum-v0/20210504-024530/models/checkpoint.pt b/codes/DDPG/outputs/Pendulum-v0/20210504-024530/models/checkpoint.pt deleted file mode 100644 index be79646..0000000 Binary files a/codes/DDPG/outputs/Pendulum-v0/20210504-024530/models/checkpoint.pt and /dev/null differ diff --git a/codes/DDPG/outputs/Pendulum-v0/20210504-024530/results/eval_ma_rewards.npy b/codes/DDPG/outputs/Pendulum-v0/20210504-024530/results/eval_ma_rewards.npy deleted file mode 100644 index 7062ae6..0000000 Binary files a/codes/DDPG/outputs/Pendulum-v0/20210504-024530/results/eval_ma_rewards.npy and /dev/null differ diff --git a/codes/DDPG/outputs/Pendulum-v0/20210504-024530/results/eval_rewards.npy b/codes/DDPG/outputs/Pendulum-v0/20210504-024530/results/eval_rewards.npy deleted file mode 100644 index f5156f8..0000000 Binary files a/codes/DDPG/outputs/Pendulum-v0/20210504-024530/results/eval_rewards.npy and /dev/null differ diff --git a/codes/DDPG/outputs/Pendulum-v0/20210504-024530/results/eval_rewards_curve.png b/codes/DDPG/outputs/Pendulum-v0/20210504-024530/results/eval_rewards_curve.png deleted file mode 100644 index 53589b0..0000000 Binary files a/codes/DDPG/outputs/Pendulum-v0/20210504-024530/results/eval_rewards_curve.png and /dev/null differ diff --git a/codes/DDPG/outputs/Pendulum-v0/20210504-024530/results/train_ma_rewards.npy b/codes/DDPG/outputs/Pendulum-v0/20210504-024530/results/train_ma_rewards.npy deleted file mode 100644 index e2d734b..0000000 Binary files a/codes/DDPG/outputs/Pendulum-v0/20210504-024530/results/train_ma_rewards.npy and /dev/null differ diff --git a/codes/DDPG/outputs/Pendulum-v0/20210504-024530/results/train_rewards.npy b/codes/DDPG/outputs/Pendulum-v0/20210504-024530/results/train_rewards.npy deleted file mode 100644 index 092936c..0000000 Binary files a/codes/DDPG/outputs/Pendulum-v0/20210504-024530/results/train_rewards.npy and /dev/null differ diff --git a/codes/DDPG/outputs/Pendulum-v0/20210504-024530/results/train_rewards_curve.png b/codes/DDPG/outputs/Pendulum-v0/20210504-024530/results/train_rewards_curve.png deleted file mode 100644 index 60e508a..0000000 Binary files a/codes/DDPG/outputs/Pendulum-v0/20210504-024530/results/train_rewards_curve.png and /dev/null differ diff --git a/codes/DDPG/task0_train.py b/codes/DDPG/task0_train.py index 29437f4..ea76661 100644 --- a/codes/DDPG/task0_train.py +++ b/codes/DDPG/task0_train.py @@ -12,7 +12,7 @@ LastEditTime: 2021-09-16 01:31:33 import sys,os curr_path = os.path.dirname(os.path.abspath(__file__)) # 当前文件所在绝对路径 parent_path = os.path.dirname(curr_path) # 父路径 -sys.path.append(parent_path) # 添加父路径到系统路径sys.path +sys.path.append(parent_path) # 添加路径到系统路径sys.path import datetime import gym @@ -21,44 +21,51 @@ import torch from DDPG.env import NormalizedActions, OUNoise from DDPG.agent import DDPG from common.utils import save_results,make_dir -from common.plot import plot_rewards, plot_rewards_cn +from common.plot import plot_rewards curr_time = datetime.datetime.now().strftime("%Y%m%d-%H%M%S") # 获取当前时间 class DDPGConfig: def __init__(self): self.algo = 'DDPG' # 算法名称 - self.env = 'Pendulum-v0' # 环境名称 - self.result_path = curr_path+"/outputs/" + self.env + \ - '/'+curr_time+'/results/' # 保存结果的路径 - self.model_path = curr_path+"/outputs/" + self.env + \ - '/'+curr_time+'/models/' # 保存模型的路径 + self.env_name = 'Pendulum-v0' # 环境名称 + self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # 检测GPU self.train_eps = 300 # 训练的回合数 self.eval_eps = 50 # 测试的回合数 self.gamma = 0.99 # 折扣因子 self.critic_lr = 1e-3 # 评论家网络的学习率 self.actor_lr = 1e-4 # 演员网络的学习率 - self.memory_capacity = 8000 - self.batch_size = 128 - self.target_update = 2 - self.hidden_dim = 256 + self.memory_capacity = 8000 # 经验回放的容量 + self.batch_size = 128 # mini-batch SGD中的批量大小 + self.target_update = 2 # 目标网络的更新频率 + self.hidden_dim = 256 # 网络隐藏层维度 self.soft_tau = 1e-2 # 软更新参数 - self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + +class PlotConfig: + def __init__(self) -> None: + self.algo = "DQN" # 算法名称 + self.env_name = 'CartPole-v0' # 环境名称 + self.result_path = curr_path+"/outputs/" + self.env_name + \ + '/'+curr_time+'/results/' # 保存结果的路径 + self.model_path = curr_path+"/outputs/" + self.env_name + \ + '/'+curr_time+'/models/' # 保存模型的路径 + self.save = True # 是否保存图片 + self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # 检测GPU def env_agent_config(cfg,seed=1): - env = NormalizedActions(gym.make(cfg.env)) + env = NormalizedActions(gym.make(cfg.env_name)) # 装饰action噪声 env.seed(seed) # 随机种子 - state_dim = env.observation_space.shape[0] - action_dim = env.action_space.shape[0] - agent = DDPG(state_dim,action_dim,cfg) + n_states = env.observation_space.shape[0] + n_actions = env.action_space.shape[0] + agent = DDPG(n_states,n_actions,cfg) return env,agent def train(cfg, env, agent): print('开始训练!') - print(f'环境:{cfg.env},算法:{cfg.algo},设备:{cfg.device}') + print(f'环境:{cfg.env_name},算法:{cfg.algo},设备:{cfg.device}') ou_noise = OUNoise(env.action_space) # 动作噪声 - rewards = [] # 记录奖励 - ma_rewards = [] # 记录滑动平均奖励 + rewards = [] # 记录所有回合的奖励 + ma_rewards = [] # 记录所有回合的滑动平均奖励 for i_ep in range(cfg.train_eps): state = env.reset() ou_noise.reset() @@ -86,9 +93,9 @@ def train(cfg, env, agent): def eval(cfg, env, agent): print('开始测试!') - print(f'环境:{cfg.env}, 算法:{cfg.algo}, 设备:{cfg.device}') - rewards = [] # 记录奖励 - ma_rewards = [] # 记录滑动平均奖励 + print(f'环境:{cfg.env_name}, 算法:{cfg.algo}, 设备:{cfg.device}') + rewards = [] # 记录所有回合的奖励 + ma_rewards = [] # 记录所有回合的滑动平均奖励 for i_ep in range(cfg.eval_eps): state = env.reset() done = False @@ -112,17 +119,18 @@ def eval(cfg, env, agent): if __name__ == "__main__": cfg = DDPGConfig() + plot_cfg = PlotConfig() # 训练 env,agent = env_agent_config(cfg,seed=1) rewards, ma_rewards = train(cfg, env, agent) - make_dir(cfg.result_path, cfg.model_path) - agent.save(path=cfg.model_path) - save_results(rewards, ma_rewards, tag='train', path=cfg.result_path) - plot_rewards_cn(rewards, ma_rewards, tag="train", env = cfg.env, algo=cfg.algo, path=cfg.result_path) + make_dir(plot_cfg.result_path, plot_cfg.model_path) + agent.save(path=plot_cfg.model_path) + save_results(rewards, ma_rewards, tag='train', path=plot_cfg.result_path) + plot_rewards(rewards, ma_rewards, plot_cfg, tag="train") # 测试 env,agent = env_agent_config(cfg,seed=10) - agent.load(path=cfg.model_path) - rewards,ma_rewards = eval(cfg,env,agent) + agent.load(path=plot_cfg.model_path) + rewards,ma_rewards = eval(plot_cfg,env,agent) save_results(rewards,ma_rewards,tag = 'eval',path = cfg.result_path) - plot_rewards_cn(rewards,ma_rewards,tag = "eval",env = cfg.env,algo = cfg.algo,path=cfg.result_path) + plot_rewards(rewards,ma_rewards,plot_cfg,tag = "eval") diff --git a/codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/models/dqn_checkpoint.pth b/codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/models/dqn_checkpoint.pth deleted file mode 100644 index 0686337..0000000 Binary files a/codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/models/dqn_checkpoint.pth and /dev/null differ diff --git a/codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/results/train_ma_rewards.npy b/codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/results/train_ma_rewards.npy deleted file mode 100644 index 952fab3..0000000 Binary files a/codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/results/train_ma_rewards.npy and /dev/null differ diff --git a/codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/results/train_rewards.npy b/codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/results/train_rewards.npy deleted file mode 100644 index 43e4be6..0000000 Binary files a/codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/results/train_rewards.npy and /dev/null differ diff --git a/codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/results/train_rewards_curve.png b/codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/results/train_rewards_curve.png deleted file mode 100644 index d4b6789..0000000 Binary files a/codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/results/train_rewards_curve.png and /dev/null differ diff --git a/codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/results/eval_rewards_curve.png b/codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/results/eval_rewards_curve.png deleted file mode 100644 index a260f79..0000000 Binary files a/codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/results/eval_rewards_curve.png and /dev/null differ diff --git a/codes/DQN-series/DQN/task0_train.ipynb b/codes/DQN-series/DQN/task0_train.ipynb deleted file mode 100644 index b9a04fc..0000000 --- a/codes/DQN-series/DQN/task0_train.ipynb +++ /dev/null @@ -1,379 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "import sys\n", - "from pathlib import Path\n", - "curr_path = str(Path().absolute()) # 当前路径\n", - "parent_path = str(Path().absolute().parent) # 父路径\n", - "sys.path.append(parent_path) # 添加路径到系统路径\n", - "\n", - "import math,random\n", - "import gym\n", - "import torch\n", - "import torch.nn as nn\n", - "import torch.optim as optim\n", - "import torch.nn.functional as F\n", - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "import seaborn as sns\n", - "from IPython.display import clear_output # 清空单元格输出区域" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 网络模型" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "class MLP(nn.Module):\n", - " def __init__(self, n_states,n_actions,hidden_dim=128):\n", - " \"\"\" 初始化q网络,为全连接网络\n", - " n_states: 输入的特征数即环境的状态数\n", - " n_actions: 输出的动作维度\n", - " \"\"\"\n", - " super(MLP, self).__init__()\n", - " self.fc1 = nn.Linear(n_states, hidden_dim) # 输入层\n", - " self.fc2 = nn.Linear(hidden_dim,hidden_dim) # 隐藏层\n", - " self.fc3 = nn.Linear(hidden_dim, n_actions) # 输出层\n", - " \n", - " def forward(self, x):\n", - " # 各层对应的激活函数\n", - " x = F.relu(self.fc1(x)) \n", - " x = F.relu(self.fc2(x))\n", - " return self.fc3(x)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 经验回放" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "class ReplayBuffer:\n", - " def __init__(self, capacity):\n", - " self.capacity = capacity # 经验回放的容量\n", - " self.buffer = [] # 缓冲区\n", - " self.position = 0 \n", - " \n", - " def push(self, state, action, reward, next_state, done):\n", - " ''' 缓冲区是一个队列,容量超出时去掉开始存入的转移(transition)\n", - " '''\n", - " if len(self.buffer) < self.capacity:\n", - " self.buffer.append(None)\n", - " self.buffer[self.position] = (state, action, reward, next_state, done)\n", - " self.position = (self.position + 1) % self.capacity \n", - " \n", - " def sample(self, batch_size):\n", - " batch = random.sample(self.buffer, batch_size) # 随机采出小批量转移\n", - " state, action, reward, next_state, done = zip(*batch) # 解压成状态,动作等\n", - " return state, action, reward, next_state, done\n", - " \n", - " def __len__(self):\n", - " ''' 返回当前存储的量\n", - " '''\n", - " return len(self.buffer)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## DQN" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "class DQN:\n", - " def __init__(self, n_states, n_actions, cfg):\n", - "\n", - " self.n_actions = n_actions # 总的动作个数\n", - " self.device = cfg.device # 设备,cpu或gpu等\n", - " self.gamma = cfg.gamma # 奖励的折扣因子\n", - " # e-greedy策略相关参数\n", - " self.frame_idx = 0 # 用于epsilon的衰减计数\n", - " self.epsilon = lambda frame_idx: cfg.epsilon_end + \\\n", - " (cfg.epsilon_start - cfg.epsilon_end) * \\\n", - " math.exp(-1. * frame_idx / cfg.epsilon_decay)\n", - " self.batch_size = cfg.batch_size\n", - " self.policy_net = MLP(n_states, n_actions,hidden_dim=cfg.hidden_dim).to(self.device)\n", - " self.target_net = MLP(n_states, n_actions,hidden_dim=cfg.hidden_dim).to(self.device)\n", - " for target_param, param in zip(self.target_net.parameters(),self.policy_net.parameters()): # 复制参数到目标网路targe_net\n", - " target_param.data.copy_(param.data)\n", - " self.optimizer = optim.Adam(self.policy_net.parameters(), lr=cfg.lr) # 优化器\n", - " self.memory = ReplayBuffer(cfg.memory_capacity) # 经验回放\n", - "\n", - " def choose_action(self, state):\n", - " ''' 选择动作\n", - " '''\n", - " self.frame_idx += 1\n", - " if random.random() > self.epsilon(self.frame_idx):\n", - " with torch.no_grad():\n", - " state = torch.tensor([state], device=self.device, dtype=torch.float32)\n", - " q_values = self.policy_net(state)\n", - " action = q_values.max(1)[1].item() # 选择Q值最大的动作\n", - " else:\n", - " action = random.randrange(self.n_actions)\n", - " return action\n", - " def update(self):\n", - " if len(self.memory) < self.batch_size: # 当memory中不满足一个批量时,不更新策略\n", - " return\n", - " # 从经验回放中(replay memory)中随机采样一个批量的转移(transition)\n", - " state_batch, action_batch, reward_batch, next_state_batch, done_batch = self.memory.sample(\n", - " self.batch_size)\n", - " # 转为张量\n", - " state_batch = torch.tensor(state_batch, device=self.device, dtype=torch.float)\n", - " action_batch = torch.tensor(action_batch, device=self.device).unsqueeze(1) \n", - " reward_batch = torch.tensor(reward_batch, device=self.device, dtype=torch.float) \n", - " next_state_batch = torch.tensor(next_state_batch, device=self.device, dtype=torch.float)\n", - " done_batch = torch.tensor(np.float32(done_batch), device=self.device)\n", - " q_values = self.policy_net(state_batch).gather(dim=1, index=action_batch) # 计算当前状态(s_t,a)对应的Q(s_t, a)\n", - " next_q_values = self.target_net(next_state_batch).max(1)[0].detach() # 计算下一时刻的状态(s_t_,a)对应的Q值\n", - " # 计算期望的Q值,对于终止状态,此时done_batch[0]=1, 对应的expected_q_value等于reward\n", - " expected_q_values = reward_batch + self.gamma * next_q_values * (1-done_batch)\n", - " loss = nn.MSELoss()(q_values, expected_q_values.unsqueeze(1)) # 计算均方根损失\n", - " # 优化更新模型\n", - " self.optimizer.zero_grad() \n", - " loss.backward()\n", - " for param in self.policy_net.parameters(): # clip防止梯度爆炸\n", - " param.grad.data.clamp_(-1, 1)\n", - " self.optimizer.step()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### DQN参数" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "class DQNConfig:\n", - " def __init__(self):\n", - " self.algo = \"DQN\" # 算法名称\n", - " self.env = 'CartPole-v0' # 环境名称\n", - " self.train_eps = 200 # 训练的回合数\n", - " self.eval_eps = 30 # 测试的回合数\n", - " self.gamma = 0.95 # 强化学习中的折扣因子\n", - " self.epsilon_start = 0.90 # e-greedy策略中初始epsilon\n", - " self.epsilon_end = 0.01 # e-greedy策略中的终止epsilon\n", - " self.epsilon_decay = 500 # e-greedy策略中epsilon的衰减率\n", - " self.lr = 0.0001 # 学习率\n", - " self.memory_capacity = 100000 # 经验回放的容量\n", - " self.batch_size = 64 # mini-batch SGD中的批量大小\n", - " self.target_update = 4 # 目标网络的更新频率\n", - " self.device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\") # 检测GPU\n", - " self.hidden_dim = 256 # 网络隐藏层" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 创建环境" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "def env_agent_config(cfg,seed=1):\n", - " ''' 创建环境和智能体\n", - " '''\n", - " env = gym.make(cfg.env) # 创建环境\n", - " env.seed(seed) # 设置随机种子\n", - " n_states = env.observation_space.shape[0] # 状态数\n", - " n_actions = env.action_space.n # 动作数\n", - " agent = DQN(n_states,n_actions,cfg) # 创建智能体\n", - " return env,agent" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 训练" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXsAAAEcCAYAAAAmzxTpAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/MnkTPAAAACXBIWXMAAAsTAAALEwEAmpwYAABlAklEQVR4nO2dd5gUVdb/P1XVaXJiEjkPAygMjKASFDCAwqLrqiwrZnddXUyLrq6sKIou6qu+urrIuqvryk9cXxVEUcyYkayIBMkwOYeeDhV+f3SY7pnuyTNMuJ/nmWe6q25Vnaqu/tbpc889VzIMw0AgEAgE3Rr5ZBsgEAgEgvZHiL1AIBD0AITYCwQCQQ9AiL1AIBD0AITYCwQCQQ9AiL1AIBD0AITYBzB9+nS+/vrrDj/uli1bOP/88zv8uIJajh8/TkZGBqqqtvm+P/zwQ8466yyysrLYvXt3m++/NTgcDm688UbGjx/PLbfccrLNaXfuvvtunnzyyZNtxklBiH0nIDs7mw0bNpxsMwTtxPLly/nLX/7C9u3bGTlyZL31GRkZjB07lqysLCZOnMhVV13F+vXr67X79NNP+dWvfsXYsWOZOHEiixYtIj8/37/+zTffJCMjg3/84x9B202dOpVNmzaFtO3999+nqKiITZs28fTTT7fyTD1UVVWxbNkyzj77bLKysjjnnHNYtmwZJSUlLdrfm2++ya9//eugZXfffTejR48mKyuLCRMmcM0113DgwIG2ML9FuFwu7rnnHsaNG8ekSZN48cUXT5ot4RBi3wFomnayTWg13eEcThY5OTkMGzaswTZr165l+/btvPfee1x88cUsXbqUv/3tb/7177//Pn/84x+56qqr+Pbbb3nnnXcwm83Mnz+fiooKf7v4+HheeOEFqqqqmmzbwIEDMZlMzT6vUL+CXC4XV111FT///DMvvPACW7du5bXXXiM+Pp4ffvihTY7h47rrrmP79u1s3LiRxMRE7rnnnmbvv6145plnOHLkCJ9++ikvv/wyL7zwAp9//vlJsycUQuzDoOs6K1eu5JxzzmHixInceuutlJWV+dffcsstTJo0ifHjx/Ob3/yG/fv3+9fdfffdLFmyhBtuuIGxY8eyadMmpk+fzj//+U/mzJnD+PHjue2223A6nQBs2rSJqVOn+rdvqC3AP/7xDyZPnszkyZN5/fXXycjI4MiRIyHPo6ysjHvuuYfJkydz2mmncdNNNwGhvaXA/dQ9h3/+859MmjQpSPQ//PBD5syZ06TrVZf//ve/nHvuuUyYMIEbb7wxyEPNyMjg1Vdf5bzzziM7O5sHHniAcAO9NU1jxYoVnHPOOWRlZfHLX/6S3NzckGGZBQsW8Prrr/u3W758ORMnTmTGjBls3LgxaL9vvPEGs2bNIisrixkzZrB69eqw56LrOs899xzTpk3jjDPO4K677qKyshKXy0VWVhaapjF37lzOOeecsPvwkZiYyEUXXcT999/P888/T2lpKYZhsHz5cn7/+98zZ84cbDYbycnJLFu2jIiICF5++WX/9oMHDyYrK4uXXnqp0WM9/fTTPPfcc7z33ntkZWXx+uuvhz0XqA11vf7665x99tlcddVV9fa5du1acnNz+dvf/sbQoUORZZmkpCRuvvlmzjrrLAD/fZKVlcUFF1zAhx9+6N/+zTffZN68eTz88MNMnDiR22+/nSVLlrBjxw6ysrLIzs6ud8yIiAjmzJnj/w4eOHCABQsWkJ2dzYUXXsjHH38c9hp8+umnzJ07l+zsbObNm8eePXtCtlu5cmW9MNdDDz3EQw89BMBbb73FTTfdRFxcHEOGDOHSSy/lrbfeaujydzhC7MPwn//8h48++ohXXnmFL774gri4OJYuXepfP3XqVDZs2MA333zDyJEjWbRoUdD277zzDjfeeCPbtm1j/PjxALz33nu88MILfPzxx+zdu5c333wz7PHDtf3888956aWXePHFF/nwww/D/jz3cdddd1FTU8O7777L119/zdVXX93kaxB4DldddRURERF8++23/vXr1q3zi31j1yuQb775hv/5n//hqaee4ssvv6RPnz7ccccdQW0+++wz/u///o+3336b9957jy+++CLkvl588UXeffddVq5cybZt23j44Yex2WyNntt///tfPv30U9asWcMbb7zB+++/H7Q+KSmJ559/nm3btvHII4/wyCOP8OOPP4bc15tvvslbb73Fyy+/zEcffYTdbmfp0qVYLBa2b98OeETwo48+atQuHzNmzEDTNL7//nsOHjxITk4OM2fODGojyzLnnXceX375ZdDyW2+9lX//+98NPmzB47D87ne/Y9asWWzfvp1LL7007LkEsnnzZtavX88///nPevv8+uuvmTJlClFRUWGP269f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J598wgUXXFCv7aWXXsrq1avZuXMnhmFgt9v57LPPwmYyJSYmMmHCBO655x769u3LkCFD/Osuuugi/v73v1NeXs6BAwd4/fXXufjiixu1tyMRnn0YrrzySgzD4Nprr6WgoICkpCQuuOACzjnnHC666CK+/PJLpkyZQnx8PLfeeiuvvvpqh9h11llnsWDBAq688kokSeKmm25izZo1WCyWkO0fffRRHnnkEWbNmoXb7WbixImcdtppDBo0iJtvvpmrr74am83GHXfc4fdUGmL27Nk88cQTTJ061f+FhYavV13OPPNMbr31VhYuXEhFRQVZWVktHuhyzTXX4HK5uPbaayktLWXw4ME8++yzADz44IM88MADPPnkk/zqV78iKyvLv91ll13G4cOHmTt3LlFRUVx33XX+zufo6GgWL17MbbfdhsvlYtq0aSG9Zx+XXHIJ+fn5XHHFFTidTiZPnsxf/vKXZp3H3LlzkSQJs9lMRkYG99xzj7/zG+CCCy7AYrHw97//ncWLF/vDSv/5z39CxvLB0xE6d+7cZt2brT0Xi8XCSy+9xNNPP821115LRUUFSUlJzJgxg1NPPZWEhASuvfZa5s2bhyRJXHTRRYwbN67BfZ5++ukMHTqUyZMnI0lSg0kJFouFFStW8MADD/D888+TmprKo48+GiTMPk455RQefPBBli5dypEjR7DZbIwbNy5kxo+P2bNn86c//Yk777wzaPktt9zCkiVLmDZtGjabjRtuuCEow64zIInJS7o2Bw4cYPbs2fzwww8t8iQFXZMvv/ySP/7xj7z00ktkZmaebHMEXQARxumCfPjhh7hcLsrLy3nssceYNm2aEPoexuTJk3nkkUdCZo4IBKEQnn0X5LrrrmPHjh0oisJpp53GkiVLwv6UFwgEAhBiLxAIBD0CEcYRCASCHoAQe4FAIOgBCLEXCASCHkCnTuEoLa1G15vfpZCUFE1xcdNKvHYkwq7m01ltE3Y1j85qF3Re21pilyxLJCSELtPQqcVe140Wib1v286IsKv5dFbbhF3No7PaBZ3Xtra0S4RxBAKBoAcgxF4gEAh6AELsBQKBoAfQqNiXlpZyww03cP755zNnzhz+8Ic/+CcO3rFjB7/4xS84//zzufbaa4NqXje0TiAQCAQdS6NiL0kS119/PRs2bGDdunX069ePxx9/HF3XufPOO7nv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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def train(cfg, env, agent):\n", - " ''' 训练\n", - " '''\n", - " print('开始训练!')\n", - " print(f'环境:{cfg.env}, 算法:{cfg.algo}, 设备:{cfg.device}')\n", - " rewards = [] # 记录所有回合的奖励\n", - " ma_rewards = [] # 记录所有回合的滑动平均奖励\n", - " for i_ep in range(cfg.train_eps):\n", - " ep_reward = 0 # 记录一回合内的奖励\n", - " state = env.reset() # 重置环境,返回初始状态\n", - " while True:\n", - " action = agent.choose_action(state) # 选择动作\n", - " next_state, reward, done, _ = env.step(action) # 更新环境,返回transition\n", - " agent.memory.push(state, action, reward, next_state, done) # 保存transition\n", - " state = next_state # 更新下一个状态\n", - " agent.update() # 更新智能体\n", - " ep_reward += reward # 累加奖励\n", - " if done:\n", - " break\n", - " if (i_ep+1) % cfg.target_update == 0: # 智能体目标网络更新\n", - " agent.target_net.load_state_dict(agent.policy_net.state_dict())\n", - " if (i_ep+1)%10 == 0: \n", - " print('回合:{}/{}, 奖励:{}'.format(i_ep+1, cfg.train_eps, ep_reward))\n", - " rewards.append(ep_reward)\n", - " if ma_rewards:\n", - " ma_rewards.append(0.9*ma_rewards[-1]+0.1*ep_reward)\n", - " else:\n", - " ma_rewards.append(ep_reward)\n", - " print('完成训练!')\n", - " return rewards, ma_rewards\n", - "\n", - "def plot_rewards(rewards,ma_rewards,plot_cfg):\n", - " # clear_output(True) # 清空单元格输出区域,因为多次打印,每次需要清楚前面打印的图片\n", - " sns.set() \n", - " plt.figure() # 创建一个图形实例,方便同时多画几个图\n", - " plt.title(\"learning curve on {} of {} for {}\".format(plot_cfg.device, plot_cfg.algo, plot_cfg.env))\n", - " plt.xlabel('epsiodes')\n", - " plt.plot(rewards,label='rewards')\n", - " plt.plot(ma_rewards,label='ma rewards')\n", - " plt.legend()\n", - " plt.show()\n", - "\n", - "class PlotConfig:\n", - " def __init__(self) -> None:\n", - " self.algo = \"DQN\" # 算法名称\n", - " self.env = 'CartPole-v0' # 环境名称\n", - " self.device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\") # 检测GPU\n", - "\n", - "cfg = DQNConfig()\n", - "plot_cfg = PlotConfig()\n", - "env,agent = env_agent_config(cfg,seed=1)\n", - "rewards, ma_rewards = train(cfg, env, agent)\n", - "plot_rewards(rewards, ma_rewards, plot_cfg) # 画出结果" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def eval(cfg,env,agent):\n", - " print('开始测试!')\n", - " print(f'环境:{cfg.env}, 算法:{cfg.algo}, 设备:{cfg.device}')\n", - " # 由于测试不需要使用epsilon-greedy策略,所以相应的值设置为0\n", - " cfg.epsilon_start = 0.0 # e-greedy策略中初始epsilon\n", - " cfg.epsilon_end = 0.0 # e-greedy策略中的终止epsilon\n", - " rewards = [] # 记录所有回合的奖励\n", - " ma_rewards = [] # 记录所有回合的滑动平均奖励\n", - " for i_ep in range(cfg.eval_eps):\n", - " ep_reward = 0 # 记录一回合内的奖励\n", - " state = env.reset() # 重置环境,返回初始状态\n", - " while True:\n", - " action = agent.choose_action(state) # 选择动作\n", - " next_state, reward, done, _ = env.step(action) # 更新环境,返回transition\n", - " state = next_state # 更新下一个状态\n", - " ep_reward += reward # 累加奖励\n", - " if done:\n", - " break\n", - " rewards.append(ep_reward)\n", - " if ma_rewards:\n", - " ma_rewards.append(ma_rewards[-1]*0.9+ep_reward*0.1)\n", - " else:\n", - " ma_rewards.append(ep_reward)\n", - " if (i_ep+1)%3 == 0: \n", - " print(f\"回合:{i_ep+1}/{cfg.eval_eps}, 奖励:{ep_reward:.1f}\")\n", - " print('完成测试!')\n", - " return rewards,ma_rewards\n", - "\n", - "rewards,ma_rewards = eval(cfg,env,agent)\n", - "plot_rewards(rewards,ma_rewards, plot_cfg) # 画出结果\n" - ] - } - ], - "metadata": { - "interpreter": { - "hash": "fe38df673a99c62a9fea33a7aceda74c9b65b12ee9d076c5851d98b692a4989a" - }, - "kernelspec": { - "display_name": "Python 3.7.10 64-bit ('py37': conda)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.10" - }, - "metadata": { - "interpreter": { - "hash": "366e1054dee9d4501b0eb8f87335afd3c67fc62db6ee611bbc7f8f5a1fefe232" - } - }, - "orig_nbformat": 2 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/codes/DQN-series/README.md b/codes/DQN-series/README.md deleted file mode 100644 index 38de319..0000000 --- a/codes/DQN-series/README.md +++ /dev/null @@ -1,3 +0,0 @@ - -本目录下汇总了基础的DQN及其变种或升级,如下 - diff --git a/codes/DQN-series/DQN/README.md b/codes/DQN/README.md similarity index 100% rename from codes/DQN-series/DQN/README.md rename to codes/DQN/README.md diff --git a/codes/DQN-series/DQN/agent.py b/codes/DQN/agent.py similarity index 100% rename from codes/DQN-series/DQN/agent.py rename to codes/DQN/agent.py diff --git a/codes/DQN-series/DQN/assets/eval_rewards_curve.png b/codes/DQN/assets/eval_rewards_curve.png similarity index 100% rename from codes/DQN-series/DQN/assets/eval_rewards_curve.png rename to codes/DQN/assets/eval_rewards_curve.png diff --git a/codes/DQN-series/DQN/assets/image-20210507162813393.png b/codes/DQN/assets/image-20210507162813393.png similarity index 100% rename from codes/DQN-series/DQN/assets/image-20210507162813393.png rename to codes/DQN/assets/image-20210507162813393.png diff --git a/codes/DQN-series/DQN/assets/rewards_curve_train.png b/codes/DQN/assets/rewards_curve_train.png similarity index 100% rename from codes/DQN-series/DQN/assets/rewards_curve_train.png rename to codes/DQN/assets/rewards_curve_train.png diff --git a/codes/DQN-series/DQN/assets/train_rewards_curve.png b/codes/DQN/assets/train_rewards_curve.png similarity index 100% rename from codes/DQN-series/DQN/assets/train_rewards_curve.png rename to codes/DQN/assets/train_rewards_curve.png diff --git a/codes/DQN-series/DQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70.png b/codes/DQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70.png similarity index 100% rename from codes/DQN-series/DQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70.png rename to codes/DQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70.png diff --git a/codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/models/dqn_checkpoint.pth b/codes/DQN/outputs/CartPole-v0/20211111-165800/models/dqn_checkpoint.pth similarity index 100% rename from codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/models/dqn_checkpoint.pth rename to codes/DQN/outputs/CartPole-v0/20211111-165800/models/dqn_checkpoint.pth diff --git a/codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/results/eval_ma_rewards.npy b/codes/DQN/outputs/CartPole-v0/20211111-165800/results/eval_ma_rewards.npy similarity index 100% rename from codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/results/eval_ma_rewards.npy rename to codes/DQN/outputs/CartPole-v0/20211111-165800/results/eval_ma_rewards.npy diff --git a/codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/results/eval_rewards.npy b/codes/DQN/outputs/CartPole-v0/20211111-165800/results/eval_rewards.npy similarity index 100% rename from codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/results/eval_rewards.npy rename to codes/DQN/outputs/CartPole-v0/20211111-165800/results/eval_rewards.npy diff --git a/codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/results/eval_rewards_curve.png b/codes/DQN/outputs/CartPole-v0/20211111-165800/results/eval_rewards_curve.png similarity index 100% rename from codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/results/eval_rewards_curve.png rename to codes/DQN/outputs/CartPole-v0/20211111-165800/results/eval_rewards_curve.png diff --git a/codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/results/train_ma_rewards.npy b/codes/DQN/outputs/CartPole-v0/20211111-165800/results/train_ma_rewards.npy similarity index 100% rename from codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/results/train_ma_rewards.npy rename to codes/DQN/outputs/CartPole-v0/20211111-165800/results/train_ma_rewards.npy diff --git a/codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/results/train_rewards.npy b/codes/DQN/outputs/CartPole-v0/20211111-165800/results/train_rewards.npy similarity index 100% rename from codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/results/train_rewards.npy rename to codes/DQN/outputs/CartPole-v0/20211111-165800/results/train_rewards.npy diff --git a/codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/results/train_rewards_curve.png b/codes/DQN/outputs/CartPole-v0/20211111-165800/results/train_rewards_curve.png similarity index 100% rename from codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/results/train_rewards_curve.png rename to codes/DQN/outputs/CartPole-v0/20211111-165800/results/train_rewards_curve.png diff --git a/codes/DQN/task0_train.ipynb b/codes/DQN/task0_train.ipynb new file mode 100644 index 0000000..464e216 --- /dev/null +++ b/codes/DQN/task0_train.ipynb @@ -0,0 +1,423 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "from pathlib import Path\n", + "curr_path = str(Path().absolute()) # 当前路径\n", + "parent_path = str(Path().absolute().parent) # 父路径\n", + "sys.path.append(parent_path) # 添加路径到系统路径\n", + "\n", + "import math,random\n", + "import gym\n", + "import torch\n", + "import torch.nn as nn\n", + "import torch.optim as optim\n", + "import torch.nn.functional as F\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import seaborn as sns\n", + "from IPython.display import clear_output # 清空单元格输出区域" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 网络模型" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "class MLP(nn.Module):\n", + " def __init__(self, n_states,n_actions,hidden_dim=128):\n", + " \"\"\" 初始化q网络,为全连接网络\n", + " n_states: 输入的特征数即环境的状态数\n", + " n_actions: 输出的动作维度\n", + " \"\"\"\n", + " super(MLP, self).__init__()\n", + " self.fc1 = nn.Linear(n_states, hidden_dim) # 输入层\n", + " self.fc2 = nn.Linear(hidden_dim,hidden_dim) # 隐藏层\n", + " self.fc3 = nn.Linear(hidden_dim, n_actions) # 输出层\n", + " \n", + " def forward(self, x):\n", + " # 各层对应的激活函数\n", + " x = F.relu(self.fc1(x)) \n", + " x = F.relu(self.fc2(x))\n", + " return self.fc3(x)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 经验回放" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "class ReplayBuffer:\n", + " def __init__(self, capacity):\n", + " self.capacity = capacity # 经验回放的容量\n", + " self.buffer = [] # 缓冲区\n", + " self.position = 0 \n", + " \n", + " def push(self, state, action, reward, next_state, done):\n", + " ''' 缓冲区是一个队列,容量超出时去掉开始存入的转移(transition)\n", + " '''\n", + " if len(self.buffer) < self.capacity:\n", + " self.buffer.append(None)\n", + " self.buffer[self.position] = (state, action, reward, next_state, done)\n", + " self.position = (self.position + 1) % self.capacity \n", + " \n", + " def sample(self, batch_size):\n", + " batch = random.sample(self.buffer, batch_size) # 随机采出小批量转移\n", + " state, action, reward, next_state, done = zip(*batch) # 解压成状态,动作等\n", + " return state, action, reward, next_state, done\n", + " \n", + " def __len__(self):\n", + " ''' 返回当前存储的量\n", + " '''\n", + " return len(self.buffer)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## DQN" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "class DQN:\n", + " def __init__(self, n_states, n_actions, cfg):\n", + "\n", + " self.n_actions = n_actions # 总的动作个数\n", + " self.device = cfg.device # 设备,cpu或gpu等\n", + " self.gamma = cfg.gamma # 奖励的折扣因子\n", + " # e-greedy策略相关参数\n", + " self.frame_idx = 0 # 用于epsilon的衰减计数\n", + " self.epsilon = lambda frame_idx: cfg.epsilon_end + \\\n", + " (cfg.epsilon_start - cfg.epsilon_end) * \\\n", + " math.exp(-1. * frame_idx / cfg.epsilon_decay)\n", + " self.batch_size = cfg.batch_size\n", + " self.policy_net = MLP(n_states, n_actions,hidden_dim=cfg.hidden_dim).to(self.device)\n", + " self.target_net = MLP(n_states, n_actions,hidden_dim=cfg.hidden_dim).to(self.device)\n", + " for target_param, param in zip(self.target_net.parameters(),self.policy_net.parameters()): # 复制参数到目标网路targe_net\n", + " target_param.data.copy_(param.data)\n", + " self.optimizer = optim.Adam(self.policy_net.parameters(), lr=cfg.lr) # 优化器\n", + " self.memory = ReplayBuffer(cfg.memory_capacity) # 经验回放\n", + "\n", + " def choose_action(self, state):\n", + " ''' 选择动作\n", + " '''\n", + " self.frame_idx += 1\n", + " if random.random() > self.epsilon(self.frame_idx):\n", + " with torch.no_grad():\n", + " state = torch.tensor([state], device=self.device, dtype=torch.float32)\n", + " q_values = self.policy_net(state)\n", + " action = q_values.max(1)[1].item() # 选择Q值最大的动作\n", + " else:\n", + " action = random.randrange(self.n_actions)\n", + " return action\n", + " def update(self):\n", + " if len(self.memory) < self.batch_size: # 当memory中不满足一个批量时,不更新策略\n", + " return\n", + " # 从经验回放中(replay memory)中随机采样一个批量的转移(transition)\n", + " state_batch, action_batch, reward_batch, next_state_batch, done_batch = self.memory.sample(\n", + " self.batch_size)\n", + " # 转为张量\n", + " state_batch = torch.tensor(state_batch, device=self.device, dtype=torch.float)\n", + " action_batch = torch.tensor(action_batch, device=self.device).unsqueeze(1) \n", + " reward_batch = torch.tensor(reward_batch, device=self.device, dtype=torch.float) \n", + " next_state_batch = torch.tensor(next_state_batch, device=self.device, dtype=torch.float)\n", + " done_batch = torch.tensor(np.float32(done_batch), device=self.device)\n", + " q_values = self.policy_net(state_batch).gather(dim=1, index=action_batch) # 计算当前状态(s_t,a)对应的Q(s_t, a)\n", + " next_q_values = self.target_net(next_state_batch).max(1)[0].detach() # 计算下一时刻的状态(s_t_,a)对应的Q值\n", + " # 计算期望的Q值,对于终止状态,此时done_batch[0]=1, 对应的expected_q_value等于reward\n", + " expected_q_values = reward_batch + self.gamma * next_q_values * (1-done_batch)\n", + " loss = nn.MSELoss()(q_values, expected_q_values.unsqueeze(1)) # 计算均方根损失\n", + " # 优化更新模型\n", + " self.optimizer.zero_grad() \n", + " loss.backward()\n", + " for param in self.policy_net.parameters(): # clip防止梯度爆炸\n", + " param.grad.data.clamp_(-1, 1)\n", + " self.optimizer.step()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### DQN参数" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "class DQNConfig:\n", + " def __init__(self):\n", + " self.algo = \"DQN\" # 算法名称\n", + " self.env = 'CartPole-v0' # 环境名称\n", + " self.train_eps = 200 # 训练的回合数\n", + " self.eval_eps = 20 # 测试的回合数\n", + " self.gamma = 0.95 # 强化学习中的折扣因子\n", + " self.epsilon_start = 0.90 # e-greedy策略中初始epsilon\n", + " self.epsilon_end = 0.01 # e-greedy策略中的终止epsilon\n", + " self.epsilon_decay = 500 # e-greedy策略中epsilon的衰减率\n", + " self.lr = 0.0001 # 学习率\n", + " self.memory_capacity = 100000 # 经验回放的容量\n", + " self.batch_size = 64 # mini-batch SGD中的批量大小\n", + " self.target_update = 4 # 目标网络的更新频率\n", + " self.device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\") # 检测GPU\n", + " self.hidden_dim = 256 # 网络隐藏层" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 创建环境" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "def env_agent_config(cfg,seed=1):\n", + " ''' 创建环境和智能体\n", + " '''\n", + " env = gym.make(cfg.env) # 创建环境\n", + " env.seed(seed) # 设置随机种子\n", + " n_states = env.observation_space.shape[0] # 状态数\n", + " n_actions = env.action_space.n # 动作数\n", + " agent = DQN(n_states,n_actions,cfg) # 创建智能体\n", + " return env,agent" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 训练" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "开始训练!\n", + "环境:CartPole-v0, 算法:DQN, 设备:cuda\n", + "回合:10/200, 奖励:12.0\n", + "回合:20/200, 奖励:16.0\n", + "回合:30/200, 奖励:15.0\n", + "回合:40/200, 奖励:14.0\n", + "回合:50/200, 奖励:13.0\n", + "回合:60/200, 奖励:27.0\n", + "回合:70/200, 奖励:36.0\n", + "回合:80/200, 奖励:33.0\n", + "回合:90/200, 奖励:200.0\n", + "回合:100/200, 奖励:200.0\n", + "回合:110/200, 奖励:200.0\n", + "回合:120/200, 奖励:200.0\n", + "回合:130/200, 奖励:200.0\n", + "回合:140/200, 奖励:200.0\n", + "回合:150/200, 奖励:200.0\n", + "回合:160/200, 奖励:200.0\n", + "回合:170/200, 奖励:200.0\n", + "回合:180/200, 奖励:200.0\n", + "回合:190/200, 奖励:200.0\n", + "回合:200/200, 奖励:200.0\n", + "完成训练!\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXsAAAEcCAYAAAAmzxTpAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/MnkTPAAAACXBIWXMAAAsTAAALEwEAmpwYAABYfklEQVR4nO3deXwU9f348dfMXrlPkhAIgiDEACKBACqHAlZRUbSWSql41m+tFk+8KgVFsUWtWi0WrVZr5SdqRRAEEW/xQJBLBBHCTe773mNmfn9sdsmxuTfJJnk/Hw8eJDvXezaT937yns98PophGAZCCCG6NbWzAxBCCNH+JNkLIUQPIMleCCF6AEn2QgjRA0iyF0KIHkCSvRBC9ACS7GuYMmUKX3/9dYcfd+vWrVx44YUdflxx0vHjx0lOTsblcvl93xs3buTcc88lNTWVPXv2+H3/bVFVVcXNN9/M6NGjue222zo7nHZ3//338/TTT3d2GJ1Ckn0ASEtLY8OGDZ0dhmgnS5Ys4c9//jPbt29n6NCh9ZYnJyczcuRIUlNTGTduHNdeey3r1q2rt96nn37Kr371K0aOHMm4ceOYN28e2dnZ3uUrV64kOTmZf/3rX7W2mzRpEps3b/YZ2wcffEBeXh6bN2/m2WefbeOZupWVlbF48WLOO+88UlNTOf/881m8eDEFBQWt2t/KlSv5zW9+U+u1+++/n+HDh5OamsrYsWO5/vrrSU9P90f4reJwOHjggQcYNWoU48eP55VXXum0WBoiyb4DaJrW2SG0WXc4h86SkZHB4MGDG11n9erVbN++nfXr13PFFVewaNEi/vGPf3iXf/DBB9x9991ce+21fPvtt6xduxaLxcLs2bMpKSnxrhcVFcVLL71EWVlZs2MbMGAAZrO5xefl668gh8PBtddey4EDB3jppZf4/vvvefPNN4mKiuKHH37wyzE8brzxRrZv387nn39OTEwMDzzwQIv37y/PPfccR44c4dNPP+W1117jpZde4osvvui0eHyRZN8AXdd58cUXOf/88xk3bhy33347RUVF3uW33XYb48ePZ/To0fz2t79l//793mX3338/Cxcu5KabbmLkyJFs3ryZKVOm8PLLL3PppZcyevRo7rjjDux2OwCbN29m0qRJ3u0bWxfgX//6FxMmTGDChAm8/fbbJCcnc+TIEZ/nUVRUxAMPPMCECRMYM2YMt9xyC+C7tVRzP3XP4eWXX2b8+PG1kv7GjRu59NJLm/V+1fXWW2/xi1/8grFjx3LzzTfXaqEmJyfzxhtvcMEFF5CWlsbDDz9MQw96a5rGsmXLOP/880lNTeWXv/wlmZmZPssyc+bM4e233/Zut2TJEsaNG8fUqVP5/PPPa+33nXfe4aKLLiI1NZWpU6eyYsWKBs9F13Wef/55Jk+ezNlnn829995LaWkpDoeD1NRUNE1jxowZnH/++Q3uwyMmJobLL7+chx56iBdeeIHCwkIMw2DJkiX84Q9/4NJLLyUoKIi4uDgWL15McHAwr732mnf7gQMHkpqayquvvtrksZ599lmef/551q9fT2pqKm+//XaD5wInS11vv/025513Htdee229fa5evZrMzEz+8Y9/cNppp6GqKrGxsdx6662ce+65AN7rJDU1lYsvvpiNGzd6t1+5ciWzZs3iscceY9y4cdx5550sXLiQHTt2kJqaSlpaWr1jBgcHc+mll3p/B9PT05kzZw5paWlccsklfPzxxw2+B59++ikzZswgLS2NWbNm8dNPP/lc78UXX6xX5nr00Ud59NFHAXj33Xe55ZZbiIyMZNCgQcycOZN33323sbe/w0myb8B///tfPvroI15//XW+/PJLIiMjWbRokXf5pEmT2LBhA9988w1Dhw5l3rx5tbZfu3YtN998M9u2bWP06NEArF+/npdeeomPP/6Yffv2sXLlygaP39C6X3zxBa+++iqvvPIKGzdubPDPc497772XyspK3n//fb7++muuu+66Zr8HNc/h2muvJTg4mG+//da7fM2aNd5k39T7VdM333zD3/72N5555hk2bdpE3759ueuuu2qt89lnn/G///2P9957j/Xr1/Pll1/63Ncrr7zC+++/z4svvsi2bdt47LHHCAoKavLc3nrrLT799FNWrVrFO++8wwcffFBreWxsLC+88ALbtm3jL3/5C3/5y1/48ccffe5r5cqVvPvuu7z22mt89NFHVFRUsGjRIqxWK9u3bwfcSfCjjz5qMi6PqVOnomkau3bt4uDBg2RkZDBt2rRa66iqygUXXMCmTZtqvX777bfzn//8p9EPW3A3WH7/+99z0UUXsX37dmbOnNngudS0ZcsW1q1bx8svv1xvn19//TUTJ04kNDS0weP269eP5cuX8/333/PHP/6Re+65h5ycHO/yXbt20a9fP7766iueeOIJHn74YUaOHMn27dvZunVrvf2Vl5ezZs0aUlJScDqd3HzzzYwfP56vv/6a+fPnM2/ePA4ePFhvuz179vCnP/2JRYsWsXnzZq666ipuueUWHA5HvXUvueQSPv/8c+9fTJqm8cEHHzB9+nSKi4vJzc3l9NNP965/+umnc+DAgQbfg84gyb4BK1as4M4776R3795YrVb++Mc/smHDBm9L8Ve/+hVhYWFYrVbmzp3LTz/95G0BgfuXdfTo0aiqis1mA9wty4SEBKKiopg8eTJ79+5t8PgNrbt+/Xp++ctfMnjwYIKDg5k7d26D+8jJyeGLL77g4YcfJjIyEovFwtixY5v9HtQ9h0suuYS1a9cC7rrsF198wSWXXNKs96umNWvWcOWVVzJs2DCsVit33XUXO3bs4Pjx4951brrpJiIiIujTpw/jxo1rsMX19ttvc/vttzNw4EAUReH0008nOjq6yXNbv3491157LYmJiURFRfH73/++1vLzzjuPU045BUVRGDt2LOPHj/eZaDznc91119GvXz9CQ0O56667WLduXZtu9losFqKjoykuLqawsBCA+Pj4euvFxcV5l3ukpKRwzjnn1KvdN0dzzmXu3LmEhIT4/FAtKioiLi6u0WNcdNFFJCQkoKoqF198Mf3792fXrl3e5fHx8cyZMwez2dzoB/e///1v0tLSuOCCCygvL+evf/0rO3fupKKigv/7v//DarVy9tlnM3nyZN5///1627/55ptcddVVnHnmmZhMJq644gosFgs7duyot27fvn0ZOnSo9wP722+/JSgoiJEjR1JRUQFAeHi4d/3w8HDKy8sbfR86WssLdT1ERkYGt956K6p68vNQVVXy8/Pp1asXTz/9NB988AEFBQXedQoLC70/8MTExHr7rPlLEBwcXKs109x1c3JyGD58uHeZr+N4ZGVlERkZSWRkZFOn61PdfV966aXMmjWLhx9+mI0bNzJ06FD69u0LNP5+JSQk1NpPTk4Ow4YN834fGhpKVFQU2dnZJCUlAfXPv6FfnKysLE455ZQWn1tOTk6t8+vTp0+t5Z9//jlLly7l8OHD6LpOVVUVQ4YMaXBfnvcB3InB5XL5PPfmcjqdFBQUEBkZ6f3wysnJoV+/frXWy83N9fnhdttttzFz5kyuv/76Fh23sXPx6N27d4PbR0VFkZub2+gxVq1axSuvvMKJEycAqKioqPWB1dj+a7rhhhu48847a722e/duevfuXes67NOnT60yoUdGRgarVq3i9ddf977mdDrJycnhvffeY+HChQCMHj2al156ienTp7N27Vouv/xy1q5dy/Tp0wEICQkB3A0gT8OurKys0b9uOoMk+wb07t2bxx57zFuCqWnVqlV8/PHHvPLKKyQlJVFaWsqYMWMarCv7U3x8fK0LNzMzs8F1e/fuTXFxMSUlJURERNRaFhwcTFVVlff7pn5BAU477TT69OnDF198Ueti9xyroffL1zl4ftHB/cteVFTUqsTYu3dvjh49Wi8Re34Bq6qqCAsLA2qfY1xcXK33rubXDoeD2267jSVLljB16lQsFgu33HJLgz/fuueTkZGB2WwmNja2xefj8fHHH2MymRgxYgRRUVH07t2bDz74gJtuusm7jq7rfPjhh0yZMqXe9oMGDeKCCy5g2bJlLTpuY+eSlZUFgKIoDW5/zjnn8Mwzz1BRUeH9GdR04sQJ5s+fz6uvvkpqaiomk4kZM2bUWqfu/hs7nq/4s7Ky0HXdm/AzMzMZMGBAvXUTExO5+eab+cMf/uBzX5dddlmt7y+66CKWLFlCVlYWGzdu5M033wQgMjKSuLg4fvrpJ8aPHw/ATz/9xGmnndbsuDuClHEa8Jvf/IZnnnnGe+EXFBR4/4QrLy/HarUSHR1NZWUlTz31VIfFNW3aNFauXEl6ejqVlZU8//zzDa4bHx/PpEmTePjhhykuLsbpdLJlyxbAXVPcv38/e/fuxW6389xzzzXr+NOnT+c///kPW7ZsqVVDbuz98rWPlStXsnfvXhwOB0899RQjRozwtupbYubMmfz973/n8OHDGIbBTz/9RGFhITExMSQkJLB69Wo0TeN///sfx44d82530UUX8d///pesrCyKi4t58cUXvcscDgcOh4OYmBjMZjOff/45X331VZPvybFjxygvL+fpp5/moosualUPl6KiIt577z0WLVrETTfdRHR0NIqicN999/HPf/6TNWvWYLfbyc3N5cEHH6SwsJCrr77a575uvfVW3nnnnVrlxaa09VxmzJhB7969mTt3Lunp6ei6TmFhIcuWLePzzz+nsrISRVGIiYkB3DfCa3Zu8CU2Npbs7GyftfS6RowYQVBQEC+99BJO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Ll3a6PvS1vesWyV7IYQQvnWbMo4QQoiGSbIXQogeQJK9EEL0AJLshRCiB5BkL4QQPYAkeyEasWzZMh588MFWbXv//ffz9NNP+zkiIVqn24yNI0R7uPnmmzs7BCH8Qlr2QgjRA0iyF91KdnY2c+fO5ayzzmLKlCm89tprADz33HPcdttt3HHHHaSmpnLFFVfw008/ebd78cUXmThxIqmpqVx44YV888033u3mzZvnXe/jjz/mkksuIS0tjTlz5pCenu5dtmfPHq644gpSU1O54447sNvttWL79NNPmTFjBmlpacyaNatZxxfCb1o3/L4QgUfTNOOKK64wnnvuOcNutxtHjx41pkyZYnzxxRfGs88+awwdOtRYv3694XA4jJdeesmYPHmy4XA4jPT0dGPSpElGVlaWYRiGcezYMePIkSOGYRjGs88+a9x9992GYRjGwYMHjTPPPNPYtGmT4XA4jBdffNE4//zzDbvdbtjtduO8884zXnnlFcPhcBjr1683hg4dajz11FOGYRjGjz/+aJx11lnGjh07DJfLZaxcudKYPHmyYbfbGz2+EP4iLXvRbfzwww8UFBTwxz/+EavVSr9+/fj1r3/NunXrABg2bBjTpk3DYrFw/fXX43A42LlzJyaTCYfDQXp6Ok6nk6SkJE455ZR6+1+3bh3nnnsu48ePx2KxcOONN1JVVcX27dvZuXMnTqeTa6+9FovFwrRp0zjjjDO827755ptcddVVnHnmmZhMJq644gosFgs7duxo9vGFaAu5QSu6jRMnTpCTk+OdzQfcww2npaXRp08fevfu7X1dVVUSEhK86//pT3/iueee48CBA0yYMIH777+fhISEWvvPycmpNYGEZ1ja7OxsTCYTCQkJKIriXV5z3YyMDFatWsXrr7/ufc3pdJKTk8PYsWObdXwh2kJa9qLbSExMJCkpia1bt3r/bd++nX/9618AZGVledfVdZ3s7GzviIaXXnopb7zxBp9++imKovDkk0/W2398fHytMcUNw/DOYBUXF0d2djZGjXEFa66bmJjIzTffXCu2nTt3eqcybM7xhWgLSfai2xgxYgShoaG8+OKLVFVVoWkaP//8M7t27QLgxx9/5MMPP8TlcvGf//wHq9XKmWeeycGDB/nmm29wOBxYrVZsNlutCUA8LrroIj7//HO++eYbnE4n//73v7FaraSmpjJy5EjMZjOvvfYaTqeTDz/8kB9++MG77cyZM1mxYgU7d+7EMAwqKir47LPPKCsra/bxhWgLKeOIbsNkMrFs2TKWLFnC1KlTcTgcnHrqqdxxxx0ATJ06lXXr1nHffffRv39/nnvuOSwWCw6Hg7/97W+kp6djsVhITU31OR3dwIEDeeKJJ3jkkUfIzs4mJSWFZcuWeSefeO655/jzn//MM888w7nnnssvfvEL77ZnnHEGjzzyCIsWLeLIkSMEBQUxatQo0tLSmn18IdpCxrMXPcJzzz3HkSNHpDwieiz5W1EIIXoASfZCCNEDSBlHCCF6AGnZCyFEDyDJXgghegBJ9kII0QNIshdCiB5Akr0QQvQAkuyFEKIH+P/8aCkb/MwAKAAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "def train(cfg, env, agent):\n", + " ''' 训练\n", + " '''\n", + " print('开始训练!')\n", + " print(f'环境:{cfg.env}, 算法:{cfg.algo}, 设备:{cfg.device}')\n", + " rewards = [] # 记录所有回合的奖励\n", + " ma_rewards = [] # 记录所有回合的滑动平均奖励\n", + " for i_ep in range(cfg.train_eps):\n", + " ep_reward = 0 # 记录一回合内的奖励\n", + " state = env.reset() # 重置环境,返回初始状态\n", + " while True:\n", + " action = agent.choose_action(state) # 选择动作\n", + " next_state, reward, done, _ = env.step(action) # 更新环境,返回transition\n", + " agent.memory.push(state, action, reward, next_state, done) # 保存transition\n", + " state = next_state # 更新下一个状态\n", + " agent.update() # 更新智能体\n", + " ep_reward += reward # 累加奖励\n", + " if done:\n", + " break\n", + " if (i_ep+1) % cfg.target_update == 0: # 智能体目标网络更新\n", + " agent.target_net.load_state_dict(agent.policy_net.state_dict())\n", + " if (i_ep+1)%10 == 0: \n", + " print('回合:{}/{}, 奖励:{}'.format(i_ep+1, cfg.train_eps, ep_reward))\n", + " rewards.append(ep_reward)\n", + " if ma_rewards:\n", + " ma_rewards.append(0.9*ma_rewards[-1]+0.1*ep_reward)\n", + " else:\n", + " ma_rewards.append(ep_reward)\n", + " print('完成训练!')\n", + " return rewards, ma_rewards\n", + "\n", + "def plot_rewards(rewards,ma_rewards,plot_cfg):\n", + " # clear_output(True) # 清空单元格输出区域,因为多次打印,每次需要清楚前面打印的图片\n", + " sns.set() \n", + " plt.figure() # 创建一个图形实例,方便同时多画几个图\n", + " plt.title(\"learning curve on {} of {} for {}\".format(plot_cfg.device, plot_cfg.algo, plot_cfg.env))\n", + " plt.xlabel('epsiodes')\n", + " plt.plot(rewards,label='rewards')\n", + " plt.plot(ma_rewards,label='ma rewards')\n", + " plt.legend()\n", + " plt.show()\n", + "\n", + "class PlotConfig:\n", + " def __init__(self) -> None:\n", + " self.algo = \"DQN\" # 算法名称\n", + " self.env = 'CartPole-v0' # 环境名称\n", + " self.device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\") # 检测GPU\n", + "\n", + "cfg = DQNConfig()\n", + "plot_cfg = PlotConfig()\n", + "env,agent = env_agent_config(cfg,seed=1)\n", + "rewards, ma_rewards = train(cfg, env, agent)\n", + "plot_rewards(rewards, ma_rewards, plot_cfg) # 画出结果" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "开始测试!\n", + "环境:CartPole-v0, 算法:DQN, 设备:cuda\n", + "回合:3/20, 奖励:200.0\n", + "回合:6/20, 奖励:200.0\n", + "回合:9/20, 奖励:200.0\n", + "回合:12/20, 奖励:200.0\n", + "回合:15/20, 奖励:200.0\n", + "回合:18/20, 奖励:200.0\n", + "完成测试!\n" + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "def eval(cfg,env,agent):\n", + " print('开始测试!')\n", + " print(f'环境:{cfg.env}, 算法:{cfg.algo}, 设备:{cfg.device}')\n", + " # 由于测试不需要使用epsilon-greedy策略,所以相应的值设置为0\n", + " cfg.epsilon_start = 0.0 # e-greedy策略中初始epsilon\n", + " cfg.epsilon_end = 0.0 # e-greedy策略中的终止epsilon\n", + " rewards = [] # 记录所有回合的奖励\n", + " ma_rewards = [] # 记录所有回合的滑动平均奖励\n", + " for i_ep in range(cfg.eval_eps):\n", + " ep_reward = 0 # 记录一回合内的奖励\n", + " state = env.reset() # 重置环境,返回初始状态\n", + " while True:\n", + " action = agent.choose_action(state) # 选择动作\n", + " next_state, reward, done, _ = env.step(action) # 更新环境,返回transition\n", + " state = next_state # 更新下一个状态\n", + " ep_reward += reward # 累加奖励\n", + " if done:\n", + " break\n", + " rewards.append(ep_reward)\n", + " if ma_rewards:\n", + " ma_rewards.append(ma_rewards[-1]*0.9+ep_reward*0.1)\n", + " else:\n", + " ma_rewards.append(ep_reward)\n", + " if (i_ep+1)%3 == 0: \n", + " print(f\"回合:{i_ep+1}/{cfg.eval_eps}, 奖励:{ep_reward:.1f}\")\n", + " print('完成测试!')\n", + " return rewards,ma_rewards\n", + "\n", + "rewards,ma_rewards = eval(cfg,env,agent)\n", + "plot_rewards(rewards,ma_rewards, plot_cfg) # 画出结果\n" + ] + } + ], + "metadata": { + "interpreter": { + "hash": "fe38df673a99c62a9fea33a7aceda74c9b65b12ee9d076c5851d98b692a4989a" + }, + "kernelspec": { + "display_name": "Python 3.7.10 64-bit ('py37': conda)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.9" + }, + "metadata": { + "interpreter": { + "hash": "366e1054dee9d4501b0eb8f87335afd3c67fc62db6ee611bbc7f8f5a1fefe232" + } + }, + "orig_nbformat": 2 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/codes/DQN-series/DQN/task0_train.py b/codes/DQN/task0_train.py similarity index 90% rename from codes/DQN-series/DQN/task0_train.py rename to codes/DQN/task0_train.py index 6827bb0..5fd0ccd 100644 --- a/codes/DQN-series/DQN/task0_train.py +++ b/codes/DQN/task0_train.py @@ -12,7 +12,7 @@ LastEditTime: 2021-09-15 15:34:13 import sys,os curr_path = os.path.dirname(os.path.abspath(__file__)) # 当前文件所在绝对路径 parent_path = os.path.dirname(curr_path) # 父路径 -sys.path.append(parent_path) # 添加父路径到系统路径sys.path +sys.path.append(parent_path) # 添加路径到系统路径 import gym import torch @@ -26,9 +26,11 @@ curr_time = datetime.datetime.now().strftime("%Y%m%d-%H%M%S") # 获取当前时 class DQNConfig: def __init__(self): self.algo = "DQN" # 算法名称 - self.env = 'CartPole-v0' # 环境名称 + self.env_name = 'CartPole-v0' # 环境名称 + self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # 检测GPU self.train_eps = 200 # 训练的回合数 self.eval_eps = 30 # 测试的回合数 + # 超参数 self.gamma = 0.95 # 强化学习中的折扣因子 self.epsilon_start = 0.90 # e-greedy策略中初始epsilon self.epsilon_end = 0.01 # e-greedy策略中的终止epsilon @@ -37,23 +39,22 @@ class DQNConfig: self.memory_capacity = 100000 # 经验回放的容量 self.batch_size = 64 # mini-batch SGD中的批量大小 self.target_update = 4 # 目标网络的更新频率 - self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # 检测GPU self.hidden_dim = 256 # 网络隐藏层 class PlotConfig: def __init__(self) -> None: self.algo = "DQN" # 算法名称 - self.env = 'CartPole-v0' # 环境名称 - self.result_path = curr_path+"/outputs/" + self.env + \ + self.env_name = 'CartPole-v0' # 环境名称 + self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # 检测GPU + self.result_path = curr_path+"/outputs/" + self.env_name + \ '/'+curr_time+'/results/' # 保存结果的路径 - self.model_path = curr_path+"/outputs/" + self.env + \ + self.model_path = curr_path+"/outputs/" + self.env_name + \ '/'+curr_time+'/models/' # 保存模型的路径 self.save = True # 是否保存图片 - self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # 检测GPU - + def env_agent_config(cfg,seed=1): ''' 创建环境和智能体 ''' - env = gym.make(cfg.env) # 创建环境 + env = gym.make(cfg.env_name) # 创建环境 env.seed(seed) # 设置随机种子 n_states = env.observation_space.shape[0] # 状态数 n_actions = env.action_space.n # 动作数 @@ -64,7 +65,7 @@ def train(cfg, env, agent): ''' 训练 ''' print('开始训练!') - print(f'环境:{cfg.env}, 算法:{cfg.algo}, 设备:{cfg.device}') + print(f'环境:{cfg.env_name}, 算法:{cfg.algo}, 设备:{cfg.device}') rewards = [] # 记录所有回合的奖励 ma_rewards = [] # 记录所有回合的滑动平均奖励 for i_ep in range(cfg.train_eps): @@ -93,7 +94,7 @@ def train(cfg, env, agent): def eval(cfg,env,agent): print('开始测试!') - print(f'环境:{cfg.env}, 算法:{cfg.algo}, 设备:{cfg.device}') + print(f'环境:{cfg.env_name}, 算法:{cfg.algo}, 设备:{cfg.device}') # 由于测试不需要使用epsilon-greedy策略,所以相应的值设置为0 cfg.epsilon_start = 0.0 # e-greedy策略中初始epsilon cfg.epsilon_end = 0.0 # e-greedy策略中的终止epsilon diff --git a/codes/DQN-series/DoubleDQN/README.md b/codes/DoubleDQN/README.md similarity index 100% rename from codes/DQN-series/DoubleDQN/README.md rename to codes/DoubleDQN/README.md diff --git a/codes/DQN-series/DoubleDQN/agent.py b/codes/DoubleDQN/agent.py similarity index 100% rename from codes/DQN-series/DoubleDQN/agent.py rename to codes/DoubleDQN/agent.py diff --git a/codes/DQN-series/DoubleDQN/assets/20201222145725907.png b/codes/DoubleDQN/assets/20201222145725907.png similarity index 100% rename from codes/DQN-series/DoubleDQN/assets/20201222145725907.png rename to codes/DoubleDQN/assets/20201222145725907.png diff --git a/codes/DQN-series/DoubleDQN/assets/20201222150225327.png b/codes/DoubleDQN/assets/20201222150225327.png similarity index 100% rename from codes/DQN-series/DoubleDQN/assets/20201222150225327.png rename to codes/DoubleDQN/assets/20201222150225327.png diff --git a/codes/DQN-series/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70-20210328110837128.png b/codes/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70-20210328110837128.png similarity index 100% rename from codes/DQN-series/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70-20210328110837128.png rename to codes/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70-20210328110837128.png diff --git a/codes/DQN-series/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70-20210328110837146.png b/codes/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70-20210328110837146.png similarity index 100% rename from codes/DQN-series/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70-20210328110837146.png rename to codes/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70-20210328110837146.png diff --git a/codes/DQN-series/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70-20210328110837157.png b/codes/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70-20210328110837157.png similarity index 100% rename from codes/DQN-series/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70-20210328110837157.png rename to codes/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70-20210328110837157.png diff --git a/codes/DQN-series/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70.png b/codes/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70.png similarity index 100% rename from codes/DQN-series/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70.png rename to codes/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70.png diff --git a/codes/DQN-series/DoubleDQN/outputs/CartPole-v0/20210504-150900/models/checkpoint.pth b/codes/DoubleDQN/outputs/CartPole-v0/20210504-150900/models/checkpoint.pth similarity index 100% rename from codes/DQN-series/DoubleDQN/outputs/CartPole-v0/20210504-150900/models/checkpoint.pth rename to codes/DoubleDQN/outputs/CartPole-v0/20210504-150900/models/checkpoint.pth diff --git a/codes/DQN-series/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/eval_ma_rewards.npy b/codes/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/eval_ma_rewards.npy similarity index 100% rename from codes/DQN-series/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/eval_ma_rewards.npy rename to codes/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/eval_ma_rewards.npy diff --git a/codes/DQN-series/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/eval_rewards.npy b/codes/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/eval_rewards.npy similarity index 100% rename from codes/DQN-series/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/eval_rewards.npy rename to codes/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/eval_rewards.npy diff --git a/codes/DQN-series/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/eval_rewards_curve.png b/codes/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/eval_rewards_curve.png similarity index 100% rename from codes/DQN-series/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/eval_rewards_curve.png rename to codes/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/eval_rewards_curve.png diff --git a/codes/DQN-series/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/train_ma_rewards.npy b/codes/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/train_ma_rewards.npy similarity index 100% rename from codes/DQN-series/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/train_ma_rewards.npy rename to codes/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/train_ma_rewards.npy diff --git a/codes/DQN-series/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/train_rewards.npy b/codes/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/train_rewards.npy similarity index 100% rename from codes/DQN-series/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/train_rewards.npy rename to codes/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/train_rewards.npy diff --git a/codes/DQN-series/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/train_rewards_curve.png b/codes/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/train_rewards_curve.png similarity index 100% rename from codes/DQN-series/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/train_rewards_curve.png rename to codes/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/train_rewards_curve.png diff --git a/codes/DQN-series/DoubleDQN/task0_train.ipynb b/codes/DoubleDQN/task0_train.ipynb similarity index 100% rename from codes/DQN-series/DoubleDQN/task0_train.ipynb rename to codes/DoubleDQN/task0_train.ipynb diff --git a/codes/DQN-series/DoubleDQN/task0_train.py b/codes/DoubleDQN/task0_train.py similarity index 100% rename from codes/DQN-series/DoubleDQN/task0_train.py rename to codes/DoubleDQN/task0_train.py diff --git a/codes/DQN-series/DuelingDQN/assets/task0_train_20211112021954.png b/codes/DuelingDQN/assets/task0_train_20211112021954.png similarity index 100% rename from codes/DQN-series/DuelingDQN/assets/task0_train_20211112021954.png rename to codes/DuelingDQN/assets/task0_train_20211112021954.png diff --git a/codes/DQN-series/DuelingDQN/task0_train.ipynb b/codes/DuelingDQN/task0_train.ipynb similarity index 100% rename from codes/DQN-series/DuelingDQN/task0_train.ipynb rename to codes/DuelingDQN/task0_train.ipynb diff --git a/codes/DQN-series/HierarchicalDQN/README.md b/codes/HierarchicalDQN/README.md similarity index 100% rename from codes/DQN-series/HierarchicalDQN/README.md rename to codes/HierarchicalDQN/README.md diff --git a/codes/DQN-series/HierarchicalDQN/agent.py b/codes/HierarchicalDQN/agent.py similarity index 100% rename from codes/DQN-series/HierarchicalDQN/agent.py rename to codes/HierarchicalDQN/agent.py diff --git a/codes/DQN-series/HierarchicalDQN/assets/image-20210331153115575.png b/codes/HierarchicalDQN/assets/image-20210331153115575.png similarity index 100% rename from codes/DQN-series/HierarchicalDQN/assets/image-20210331153115575.png rename to codes/HierarchicalDQN/assets/image-20210331153115575.png diff --git a/codes/DQN-series/HierarchicalDQN/assets/image-20210331153542314.png b/codes/HierarchicalDQN/assets/image-20210331153542314.png similarity index 100% rename from codes/DQN-series/HierarchicalDQN/assets/image-20210331153542314.png rename to codes/HierarchicalDQN/assets/image-20210331153542314.png diff --git a/codes/DQN-series/HierarchicalDQN/results/20210331-134559/ma_rewards_train.npy b/codes/HierarchicalDQN/results/20210331-134559/ma_rewards_train.npy similarity index 100% rename from codes/DQN-series/HierarchicalDQN/results/20210331-134559/ma_rewards_train.npy rename to codes/HierarchicalDQN/results/20210331-134559/ma_rewards_train.npy diff --git a/codes/DQN-series/HierarchicalDQN/results/20210331-134559/rewards_curve_train.png b/codes/HierarchicalDQN/results/20210331-134559/rewards_curve_train.png similarity index 100% rename from codes/DQN-series/HierarchicalDQN/results/20210331-134559/rewards_curve_train.png rename to codes/HierarchicalDQN/results/20210331-134559/rewards_curve_train.png diff --git a/codes/DQN-series/HierarchicalDQN/results/20210331-134559/rewards_train.npy b/codes/HierarchicalDQN/results/20210331-134559/rewards_train.npy similarity index 100% rename from codes/DQN-series/HierarchicalDQN/results/20210331-134559/rewards_train.npy rename to codes/HierarchicalDQN/results/20210331-134559/rewards_train.npy diff --git a/codes/DQN-series/HierarchicalDQN/results/20210331-145852/losses_curve.png b/codes/HierarchicalDQN/results/20210331-145852/losses_curve.png similarity index 100% rename from codes/DQN-series/HierarchicalDQN/results/20210331-145852/losses_curve.png rename to codes/HierarchicalDQN/results/20210331-145852/losses_curve.png diff --git a/codes/DQN-series/HierarchicalDQN/results/20210331-145852/ma_rewards_train.npy b/codes/HierarchicalDQN/results/20210331-145852/ma_rewards_train.npy similarity index 100% rename from codes/DQN-series/HierarchicalDQN/results/20210331-145852/ma_rewards_train.npy rename to codes/HierarchicalDQN/results/20210331-145852/ma_rewards_train.npy diff --git a/codes/DQN-series/HierarchicalDQN/results/20210331-145852/rewards_curve_train.png b/codes/HierarchicalDQN/results/20210331-145852/rewards_curve_train.png similarity index 100% rename from codes/DQN-series/HierarchicalDQN/results/20210331-145852/rewards_curve_train.png rename to codes/HierarchicalDQN/results/20210331-145852/rewards_curve_train.png diff --git a/codes/DQN-series/HierarchicalDQN/results/20210331-145852/rewards_train.npy b/codes/HierarchicalDQN/results/20210331-145852/rewards_train.npy similarity index 100% rename from codes/DQN-series/HierarchicalDQN/results/20210331-145852/rewards_train.npy rename to codes/HierarchicalDQN/results/20210331-145852/rewards_train.npy diff --git a/codes/DQN-series/HierarchicalDQN/saved_model/20210331-134559/meta_checkpoint.pth b/codes/HierarchicalDQN/saved_model/20210331-134559/meta_checkpoint.pth similarity index 100% rename from codes/DQN-series/HierarchicalDQN/saved_model/20210331-134559/meta_checkpoint.pth rename to codes/HierarchicalDQN/saved_model/20210331-134559/meta_checkpoint.pth diff --git a/codes/DQN-series/HierarchicalDQN/saved_model/20210331-134559/policy_checkpoint.pth b/codes/HierarchicalDQN/saved_model/20210331-134559/policy_checkpoint.pth similarity index 100% rename from codes/DQN-series/HierarchicalDQN/saved_model/20210331-134559/policy_checkpoint.pth rename to codes/HierarchicalDQN/saved_model/20210331-134559/policy_checkpoint.pth diff --git a/codes/DQN-series/HierarchicalDQN/saved_model/20210331-145852/meta_checkpoint.pth b/codes/HierarchicalDQN/saved_model/20210331-145852/meta_checkpoint.pth similarity index 100% rename from codes/DQN-series/HierarchicalDQN/saved_model/20210331-145852/meta_checkpoint.pth rename to codes/HierarchicalDQN/saved_model/20210331-145852/meta_checkpoint.pth diff --git a/codes/DQN-series/HierarchicalDQN/saved_model/20210331-145852/policy_checkpoint.pth b/codes/HierarchicalDQN/saved_model/20210331-145852/policy_checkpoint.pth similarity index 100% rename from codes/DQN-series/HierarchicalDQN/saved_model/20210331-145852/policy_checkpoint.pth rename to codes/HierarchicalDQN/saved_model/20210331-145852/policy_checkpoint.pth diff --git a/codes/DQN-series/HierarchicalDQN/task0_train.ipynb b/codes/HierarchicalDQN/task0_train.ipynb similarity index 100% rename from codes/DQN-series/HierarchicalDQN/task0_train.ipynb rename to codes/HierarchicalDQN/task0_train.ipynb diff --git a/codes/DQN-series/HierarchicalDQN/task0_train.py b/codes/HierarchicalDQN/task0_train.py similarity index 100% rename from codes/DQN-series/HierarchicalDQN/task0_train.py rename to codes/HierarchicalDQN/task0_train.py diff --git a/codes/DQN-series/NoisyDQN/task0_train.ipynb b/codes/NoisyDQN/task0_train.ipynb similarity index 100% rename from codes/DQN-series/NoisyDQN/task0_train.ipynb rename to codes/NoisyDQN/task0_train.ipynb diff --git a/codes/PPO/agent.py b/codes/PPO/agent.py index 28b2861..8e669f6 100644 --- a/codes/PPO/agent.py +++ b/codes/PPO/agent.py @@ -29,13 +29,16 @@ class PPO: self.memory = PPOMemory(cfg.batch_size) self.loss = 0 - def choose_action(self, observation): - state = torch.tensor([observation], dtype=torch.float).to(self.device) + def choose_action(self, state,continuous=False): + state = torch.tensor([state], dtype=torch.float).to(self.device) dist = self.actor(state) value = self.critic(state) action = dist.sample() probs = torch.squeeze(dist.log_prob(action)).item() - action = torch.squeeze(action).item() + if continuous: + action = torch.tanh(action) + else: + action = torch.squeeze(action).item() value = torch.squeeze(value).item() return action, probs, value diff --git a/codes/PPO/outputs/CartPole-v0/20211117-184614/models/ppo_actor.pt b/codes/PPO/outputs/CartPole-v0/20211117-184614/models/ppo_actor.pt new file mode 100644 index 0000000..6d7edc6 Binary files /dev/null and b/codes/PPO/outputs/CartPole-v0/20211117-184614/models/ppo_actor.pt differ diff --git a/codes/PPO/outputs/CartPole-v0/20211117-184614/models/ppo_critic.pt b/codes/PPO/outputs/CartPole-v0/20211117-184614/models/ppo_critic.pt new file mode 100644 index 0000000..63c35a8 Binary files /dev/null and b/codes/PPO/outputs/CartPole-v0/20211117-184614/models/ppo_critic.pt differ diff --git a/codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/results/eval_ma_rewards.npy b/codes/PPO/outputs/CartPole-v0/20211117-184614/results/eval_ma_rewards.npy similarity index 52% rename from codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/results/eval_ma_rewards.npy rename to codes/PPO/outputs/CartPole-v0/20211117-184614/results/eval_ma_rewards.npy index 343fcc6..14bca8b 100644 Binary files a/codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/results/eval_ma_rewards.npy and b/codes/PPO/outputs/CartPole-v0/20211117-184614/results/eval_ma_rewards.npy differ diff --git a/codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/results/eval_rewards.npy b/codes/PPO/outputs/CartPole-v0/20211117-184614/results/eval_rewards.npy similarity index 52% rename from codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/results/eval_rewards.npy rename to codes/PPO/outputs/CartPole-v0/20211117-184614/results/eval_rewards.npy index 343fcc6..14bca8b 100644 Binary files a/codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/results/eval_rewards.npy and b/codes/PPO/outputs/CartPole-v0/20211117-184614/results/eval_rewards.npy differ diff --git a/codes/PPO/outputs/CartPole-v0/20211117-184614/results/eval_rewards_curve.png b/codes/PPO/outputs/CartPole-v0/20211117-184614/results/eval_rewards_curve.png new file mode 100644 index 0000000..59eb91a Binary files /dev/null and b/codes/PPO/outputs/CartPole-v0/20211117-184614/results/eval_rewards_curve.png differ diff --git a/codes/PPO/outputs/CartPole-v0/20211117-184614/results/train_ma_rewards.npy b/codes/PPO/outputs/CartPole-v0/20211117-184614/results/train_ma_rewards.npy new file mode 100644 index 0000000..9db0ffe Binary files /dev/null and b/codes/PPO/outputs/CartPole-v0/20211117-184614/results/train_ma_rewards.npy differ diff --git a/codes/PPO/outputs/CartPole-v0/20211117-184614/results/train_rewards.npy b/codes/PPO/outputs/CartPole-v0/20211117-184614/results/train_rewards.npy new file mode 100644 index 0000000..5800e79 Binary files /dev/null and b/codes/PPO/outputs/CartPole-v0/20211117-184614/results/train_rewards.npy differ diff --git a/codes/PPO/outputs/CartPole-v0/20211117-184614/results/train_rewards_curve.png b/codes/PPO/outputs/CartPole-v0/20211117-184614/results/train_rewards_curve.png new file mode 100644 index 0000000..b4a5cfe Binary files /dev/null and b/codes/PPO/outputs/CartPole-v0/20211117-184614/results/train_rewards_curve.png differ diff --git a/codes/PPO/results/CartPole-v0/20210506-004345/models/ppo_actor.pt b/codes/PPO/results/CartPole-v0/20210506-004345/models/ppo_actor.pt deleted file mode 100644 index 652ec59..0000000 Binary files a/codes/PPO/results/CartPole-v0/20210506-004345/models/ppo_actor.pt and /dev/null differ diff --git a/codes/PPO/results/CartPole-v0/20210506-004345/models/ppo_critic.pt b/codes/PPO/results/CartPole-v0/20210506-004345/models/ppo_critic.pt deleted file mode 100644 index 9c71cfb..0000000 Binary files a/codes/PPO/results/CartPole-v0/20210506-004345/models/ppo_critic.pt and /dev/null differ diff --git a/codes/PPO/results/CartPole-v0/20210506-004345/results/eval_ma_rewards.npy b/codes/PPO/results/CartPole-v0/20210506-004345/results/eval_ma_rewards.npy deleted file mode 100644 index a8a5243..0000000 Binary files a/codes/PPO/results/CartPole-v0/20210506-004345/results/eval_ma_rewards.npy and /dev/null differ diff --git a/codes/PPO/results/CartPole-v0/20210506-004345/results/eval_rewards.npy b/codes/PPO/results/CartPole-v0/20210506-004345/results/eval_rewards.npy deleted file mode 100644 index a8a5243..0000000 Binary files a/codes/PPO/results/CartPole-v0/20210506-004345/results/eval_rewards.npy and /dev/null differ diff --git a/codes/PPO/results/CartPole-v0/20210506-004345/results/eval_rewards_curve.png b/codes/PPO/results/CartPole-v0/20210506-004345/results/eval_rewards_curve.png deleted file mode 100644 index 624437a..0000000 Binary files a/codes/PPO/results/CartPole-v0/20210506-004345/results/eval_rewards_curve.png and /dev/null differ diff --git a/codes/PPO/results/CartPole-v0/20210506-004345/results/train_ma_rewards.npy b/codes/PPO/results/CartPole-v0/20210506-004345/results/train_ma_rewards.npy deleted file mode 100644 index b232547..0000000 Binary files a/codes/PPO/results/CartPole-v0/20210506-004345/results/train_ma_rewards.npy and /dev/null differ diff --git a/codes/PPO/results/CartPole-v0/20210506-004345/results/train_rewards.npy b/codes/PPO/results/CartPole-v0/20210506-004345/results/train_rewards.npy deleted file mode 100644 index d6c6cd5..0000000 Binary files a/codes/PPO/results/CartPole-v0/20210506-004345/results/train_rewards.npy and /dev/null differ diff --git a/codes/PPO/results/CartPole-v0/20210506-004345/results/train_rewards_curve.png b/codes/PPO/results/CartPole-v0/20210506-004345/results/train_rewards_curve.png deleted file mode 100644 index 67d24f9..0000000 Binary files a/codes/PPO/results/CartPole-v0/20210506-004345/results/train_rewards_curve.png and /dev/null differ diff --git a/codes/PPO/results/CartPole-v0/20210506-013522/models/ppo_actor.pt b/codes/PPO/results/CartPole-v0/20210506-013522/models/ppo_actor.pt deleted file mode 100644 index fb5fb41..0000000 Binary files a/codes/PPO/results/CartPole-v0/20210506-013522/models/ppo_actor.pt and /dev/null differ diff --git a/codes/PPO/results/CartPole-v0/20210506-013522/models/ppo_critic.pt b/codes/PPO/results/CartPole-v0/20210506-013522/models/ppo_critic.pt deleted file mode 100644 index f9eb037..0000000 Binary files a/codes/PPO/results/CartPole-v0/20210506-013522/models/ppo_critic.pt and /dev/null differ diff --git a/codes/PPO/results/CartPole-v0/20210506-013522/results/eval_ma_rewards.npy b/codes/PPO/results/CartPole-v0/20210506-013522/results/eval_ma_rewards.npy deleted file mode 100644 index 54f966e..0000000 Binary files a/codes/PPO/results/CartPole-v0/20210506-013522/results/eval_ma_rewards.npy and /dev/null differ diff --git a/codes/PPO/results/CartPole-v0/20210506-013522/results/eval_rewards.npy b/codes/PPO/results/CartPole-v0/20210506-013522/results/eval_rewards.npy deleted file mode 100644 index a44c265..0000000 Binary files a/codes/PPO/results/CartPole-v0/20210506-013522/results/eval_rewards.npy and /dev/null differ diff --git a/codes/PPO/results/CartPole-v0/20210506-013522/results/eval_rewards_curve.png b/codes/PPO/results/CartPole-v0/20210506-013522/results/eval_rewards_curve.png deleted file mode 100644 index 18f5f0b..0000000 Binary files a/codes/PPO/results/CartPole-v0/20210506-013522/results/eval_rewards_curve.png and /dev/null differ diff --git a/codes/PPO/results/CartPole-v0/20210506-013522/results/train_ma_rewards.npy b/codes/PPO/results/CartPole-v0/20210506-013522/results/train_ma_rewards.npy deleted file mode 100644 index 8bf7615..0000000 Binary files a/codes/PPO/results/CartPole-v0/20210506-013522/results/train_ma_rewards.npy and /dev/null differ diff --git a/codes/PPO/results/CartPole-v0/20210506-013522/results/train_rewards.npy b/codes/PPO/results/CartPole-v0/20210506-013522/results/train_rewards.npy deleted file mode 100644 index cde0ab6..0000000 Binary files a/codes/PPO/results/CartPole-v0/20210506-013522/results/train_rewards.npy and /dev/null differ diff --git a/codes/PPO/results/CartPole-v0/20210506-013522/results/train_rewards_curve.png b/codes/PPO/results/CartPole-v0/20210506-013522/results/train_rewards_curve.png deleted file mode 100644 index 6c0db9b..0000000 Binary files a/codes/PPO/results/CartPole-v0/20210506-013522/results/train_rewards_curve.png and /dev/null differ diff --git a/codes/PPO/task0_train.py b/codes/PPO/task0_train.py index 04dfae0..e1354c6 100644 --- a/codes/PPO/task0_train.py +++ b/codes/PPO/task0_train.py @@ -10,14 +10,13 @@ Discription: Environment: ''' import sys,os -curr_path = os.path.dirname(__file__) -parent_path=os.path.dirname(curr_path) -sys.path.append(parent_path) # add current terminal path to sys.path +curr_path = os.path.dirname(os.path.abspath(__file__)) # 当前文件所在绝对路径 +parent_path = os.path.dirname(curr_path) # 父路径 +sys.path.append(parent_path) # 添加路径到系统路径 import gym import torch import datetime -import tqdm from PPO.agent import PPO from common.plot import plot_rewards from common.utils import save_results,make_dir @@ -26,12 +25,12 @@ curr_time = datetime.datetime.now().strftime("%Y%m%d-%H%M%S") # obtain current t class PPOConfig: def __init__(self) -> None: - self.env = 'CartPole-v0' - self.algo = 'PPO' - self.result_path = curr_path+"/results/" +self.env+'/'+curr_time+'/results/' # path to save results - self.model_path = curr_path+"/results/" +self.env+'/'+curr_time+'/models/' # path to save models - self.train_eps = 200 # max training episodes - self.eval_eps = 50 + self.algo = "DQN" # 算法名称 + self.env_name = 'CartPole-v0' # 环境名称 + self.continuous = False # 环境是否为连续动作 + self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # 检测GPU + self.train_eps = 200 # 训练的回合数 + self.eval_eps = 20 # 测试的回合数 self.batch_size = 5 self.gamma=0.99 self.n_epochs = 4 @@ -41,10 +40,20 @@ class PPOConfig: self.policy_clip=0.2 self.hidden_dim = 256 self.update_fre = 20 # frequency of agent update - self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # check gpu +class PlotConfig: + def __init__(self) -> None: + self.algo = "DQN" # 算法名称 + self.env_name = 'CartPole-v0' # 环境名称 + self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # 检测GPU + self.result_path = curr_path+"/outputs/" + self.env_name + \ + '/'+curr_time+'/results/' # 保存结果的路径 + self.model_path = curr_path+"/outputs/" + self.env_name + \ + '/'+curr_time+'/models/' # 保存模型的路径 + self.save = True # 是否保存图片 + def env_agent_config(cfg,seed=1): - env = gym.make(cfg.env) + env = gym.make(cfg.env_name) env.seed(seed) state_dim = env.observation_space.shape[0] action_dim = env.action_space.n @@ -53,44 +62,44 @@ def env_agent_config(cfg,seed=1): def train(cfg,env,agent): print('开始训练!') - print(f'Env:{cfg.env}, Algorithm:{cfg.algo}, Device:{cfg.device}') - rewards= [] - ma_rewards = [] # moving average rewards - running_steps = 0 + print(f'环境:{cfg.env_name}, 算法:{cfg.algo}, 设备:{cfg.device}') + rewards = [] # 记录所有回合的奖励 + ma_rewards = [] # 记录所有回合的滑动平均奖励 + steps = 0 for i_ep in range(cfg.train_eps): state = env.reset() done = False ep_reward = 0 while not done: - action, prob, val = agent.choose_action(state) + action, prob, val = agent.choose_action(state,continuous=cfg.continuous) state_, reward, done, _ = env.step(action) - running_steps += 1 + steps += 1 ep_reward += reward agent.memory.push(state, action, prob, val, reward, done) - if running_steps % cfg.update_fre == 0: + if steps % cfg.update_fre == 0: agent.update() state = state_ rewards.append(ep_reward) if ma_rewards: - ma_rewards.append( - 0.9*ma_rewards[-1]+0.1*ep_reward) + ma_rewards.append(0.9*ma_rewards[-1]+0.1*ep_reward) else: ma_rewards.append(ep_reward) - print(f"回合:{i_ep+1}/{cfg.train_eps},奖励:{ep_reward:.2f}") - print('Complete training!') + if (i_ep+1)%10 == 0: + print(f"回合:{i_ep+1}/{cfg.train_eps},奖励:{ep_reward:.2f}") + print('完成训练!') return rewards,ma_rewards def eval(cfg,env,agent): - print('Start to eval !') - print(f'Env:{cfg.env}, Algorithm:{cfg.algo}, Device:{cfg.device}') - rewards= [] - ma_rewards = [] # moving average rewards + print('开始测试!') + print(f'环境:{cfg.env_name}, 算法:{cfg.algo}, 设备:{cfg.device}') + rewards = [] # 记录所有回合的奖励 + ma_rewards = [] # 记录所有回合的滑动平均奖励 for i_ep in range(cfg.eval_eps): state = env.reset() done = False ep_reward = 0 while not done: - action, prob, val = agent.choose_action(state) + action, prob, val = agent.choose_action(state,cfg.continuous) state_, reward, done, _ = env.step(action) ep_reward += reward state = state_ @@ -100,23 +109,23 @@ def eval(cfg,env,agent): 0.9*ma_rewards[-1]+0.1*ep_reward) else: ma_rewards.append(ep_reward) - print(f"Episode:{i_ep+1}/{cfg.eval_eps}, Reward:{ep_reward:.3f}") - print('Complete evaling!') + print('回合:{}/{}, 奖励:{}'.format(i_ep+1, cfg.eval_eps, ep_reward)) + print('完成训练!') return rewards,ma_rewards if __name__ == '__main__': cfg = PPOConfig() - # train + plot_cfg = PlotConfig() + # 训练 env,agent = env_agent_config(cfg,seed=1) rewards, ma_rewards = train(cfg, env, agent) - make_dir(cfg.result_path, cfg.model_path) - agent.save(path=cfg.model_path) - save_results(rewards, ma_rewards, tag='train', path=cfg.result_path) - plot_rewards(rewards, ma_rewards, tag="train", - algo=cfg.algo, path=cfg.result_path) - # eval + make_dir(plot_cfg.result_path, plot_cfg.model_path) # 创建保存结果和模型路径的文件夹 + agent.save(path=plot_cfg.model_path) + save_results(rewards, ma_rewards, tag='train', path=plot_cfg.result_path) + plot_rewards(rewards, ma_rewards, plot_cfg, tag="train") + # 测试 env,agent = env_agent_config(cfg,seed=10) - agent.load(path=cfg.model_path) + agent.load(path=plot_cfg.model_path) rewards,ma_rewards = eval(cfg,env,agent) - save_results(rewards,ma_rewards,tag='eval',path=cfg.result_path) - plot_rewards(rewards,ma_rewards,tag="eval",env=cfg.env,algo = cfg.algo,path=cfg.result_path) + save_results(rewards,ma_rewards,tag='eval',path=plot_cfg.result_path) + plot_rewards(rewards,ma_rewards,plot_cfg,tag="eval") diff --git a/codes/PPO/task1_train.py b/codes/PPO/task1_train.py new file mode 100644 index 0000000..ff2a6b2 --- /dev/null +++ b/codes/PPO/task1_train.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python +# coding=utf-8 +''' +Author: John +Email: johnjim0816@gmail.com +Date: 2021-03-22 16:18:10 +LastEditor: John +LastEditTime: 2021-09-26 22:05:00 +Discription: +Environment: +''' +import sys,os +curr_path = os.path.dirname(os.path.abspath(__file__)) # 当前文件所在绝对路径 +parent_path = os.path.dirname(curr_path) # 父路径 +sys.path.append(parent_path) # 添加路径到系统路径 + +import gym +import torch +import datetime +from PPO.agent import PPO +from common.plot import plot_rewards +from common.utils import save_results,make_dir + +curr_time = datetime.datetime.now().strftime("%Y%m%d-%H%M%S") # obtain current time + +class PPOConfig: + def __init__(self) -> None: + self.algo = "PPO" # 算法名称 + self.env_name = 'Pendulum-v1' # 环境名称 + self.continuous = True # 环境是否为连续动作 + self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # 检测GPU + self.train_eps = 200 # 训练的回合数 + self.eval_eps = 20 # 测试的回合数 + self.batch_size = 5 + self.gamma=0.99 + self.n_epochs = 4 + self.actor_lr = 0.0003 + self.critic_lr = 0.0003 + self.gae_lambda=0.95 + self.policy_clip=0.2 + self.hidden_dim = 256 + self.update_fre = 20 # frequency of agent update + +class PlotConfig: + def __init__(self) -> None: + self.algo = "PPO" # 算法名称 + self.env_name = 'Pendulum-v1' # 环境名称 + self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # 检测GPU + self.result_path = curr_path+"/outputs/" + self.env_name + \ + '/'+curr_time+'/results/' # 保存结果的路径 + self.model_path = curr_path+"/outputs/" + self.env_name + \ + '/'+curr_time+'/models/' # 保存模型的路径 + self.save = True # 是否保存图片 + +def env_agent_config(cfg,seed=1): + env = gym.make(cfg.env_name) + env.seed(seed) + state_dim = env.observation_space.shape[0] + action_dim = env.action_space.shape[0] + agent = PPO(state_dim,action_dim,cfg) + return env,agent + +def train(cfg,env,agent): + print('开始训练!') + print(f'环境:{cfg.env_name}, 算法:{cfg.algo}, 设备:{cfg.device}') + rewards = [] # 记录所有回合的奖励 + ma_rewards = [] # 记录所有回合的滑动平均奖励 + steps = 0 + for i_ep in range(cfg.train_eps): + state = env.reset() + done = False + ep_reward = 0 + while not done: + action, prob, val = agent.choose_action(state,continuous=cfg.continuous) + print(action) + state_, reward, done, _ = env.step(action) + steps += 1 + ep_reward += reward + agent.memory.push(state, action, prob, val, reward, done) + if steps % cfg.update_fre == 0: + agent.update() + state = state_ + rewards.append(ep_reward) + if ma_rewards: + ma_rewards.append(0.9*ma_rewards[-1]+0.1*ep_reward) + else: + ma_rewards.append(ep_reward) + if (i_ep+1)%10 == 0: + print(f"回合:{i_ep+1}/{cfg.train_eps},奖励:{ep_reward:.2f}") + print('完成训练!') + return rewards,ma_rewards + +def eval(cfg,env,agent): + print('开始测试!') + print(f'环境:{cfg.env_name}, 算法:{cfg.algo}, 设备:{cfg.device}') + rewards = [] # 记录所有回合的奖励 + ma_rewards = [] # 记录所有回合的滑动平均奖励 + for i_ep in range(cfg.eval_eps): + state = env.reset() + done = False + ep_reward = 0 + while not done: + action, prob, val = agent.choose_action(state,continuous=False) + state_, reward, done, _ = env.step(action) + ep_reward += reward + state = state_ + rewards.append(ep_reward) + if ma_rewards: + ma_rewards.append( + 0.9*ma_rewards[-1]+0.1*ep_reward) + else: + ma_rewards.append(ep_reward) + print('回合:{}/{}, 奖励:{}'.format(i_ep+1, cfg.eval_eps, ep_reward)) + print('完成训练!') + return rewards,ma_rewards + +if __name__ == '__main__': + cfg = PPOConfig() + plot_cfg = PlotConfig() + # 训练 + env,agent = env_agent_config(cfg,seed=1) + rewards, ma_rewards = train(cfg, env, agent) + make_dir(plot_cfg.result_path, plot_cfg.model_path) # 创建保存结果和模型路径的文件夹 + agent.save(path=plot_cfg.model_path) + save_results(rewards, ma_rewards, tag='train', path=plot_cfg.result_path) + plot_rewards(rewards, ma_rewards, plot_cfg, tag="train") + # 测试 + env,agent = env_agent_config(cfg,seed=10) + agent.load(path=plot_cfg.model_path) + rewards,ma_rewards = eval(cfg,env,agent) + save_results(rewards,ma_rewards,tag='eval',path=plot_cfg.result_path) + plot_rewards(rewards,ma_rewards,plot_cfg,tag="eval") diff --git a/codes/SAC/task0_train.ipynb b/codes/SAC/task0_train.ipynb index 9a0c43a..8148a4b 100644 --- a/codes/SAC/task0_train.ipynb +++ b/codes/SAC/task0_train.ipynb @@ -1,30 +1,4 @@ { - "metadata": { - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.10" - }, - "orig_nbformat": 2, - "kernelspec": { - "name": "python3710jvsc74a57bd0fd81e6a9e450d5c245c1a0b5da0b03c89c450f614a13afa2acb1654375922756", - "display_name": "Python 3.7.10 64-bit ('mujoco': conda)" - }, - "metadata": { - "interpreter": { - "hash": "fd81e6a9e450d5c245c1a0b5da0b03c89c450f614a13afa2acb1654375922756" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2, "cells": [ { "cell_type": "code", @@ -170,9 +144,29 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "DeprecatedEnv", + "evalue": "Env Pendulum-v0 not found (valid versions include ['Pendulum-v1'])", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m~/anaconda3/envs/py37/lib/python3.7/site-packages/gym/envs/registration.py\u001b[0m in \u001b[0;36mspec\u001b[0;34m(self, path)\u001b[0m\n\u001b[1;32m 157\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 158\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0menv_specs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 159\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mKeyError\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mKeyError\u001b[0m: 'Pendulum-v0'", + "\nDuring handling of the above exception, another exception occurred:\n", + "\u001b[0;31mDeprecatedEnv\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;31m# train\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0menv\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0magent\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0menv_agent_config\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcfg\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mseed\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 6\u001b[0m \u001b[0mrewards\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mma_rewards\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtrain\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcfg\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0menv\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0magent\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0mmake_dir\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcfg\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mresult_path\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcfg\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmodel_path\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36menv_agent_config\u001b[0;34m(cfg, seed)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0menv_agent_config\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcfg\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mseed\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0menv\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mNormalizedActions\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgym\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmake\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Pendulum-v0\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3\u001b[0m \u001b[0menv\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mseed\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mseed\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0maction_dim\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0menv\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0maction_space\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mshape\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0mstate_dim\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0menv\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mobservation_space\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mshape\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/anaconda3/envs/py37/lib/python3.7/site-packages/gym/envs/registration.py\u001b[0m in \u001b[0;36mmake\u001b[0;34m(id, **kwargs)\u001b[0m\n\u001b[1;32m 233\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 234\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mmake\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 235\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mregistry\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmake\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 236\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 237\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/anaconda3/envs/py37/lib/python3.7/site-packages/gym/envs/registration.py\u001b[0m in \u001b[0;36mmake\u001b[0;34m(self, path, **kwargs)\u001b[0m\n\u001b[1;32m 126\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 127\u001b[0m \u001b[0mlogger\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minfo\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Making new env: %s\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpath\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 128\u001b[0;31m \u001b[0mspec\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mspec\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mpath\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 129\u001b[0m \u001b[0menv\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mspec\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmake\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 130\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0menv\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/anaconda3/envs/py37/lib/python3.7/site-packages/gym/envs/registration.py\u001b[0m in \u001b[0;36mspec\u001b[0;34m(self, path)\u001b[0m\n\u001b[1;32m 185\u001b[0m raise error.DeprecatedEnv(\n\u001b[1;32m 186\u001b[0m \"Env {} not found (valid versions include {})\".format(\n\u001b[0;32m--> 187\u001b[0;31m \u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmatching_envs\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 188\u001b[0m )\n\u001b[1;32m 189\u001b[0m )\n", + "\u001b[0;31mDeprecatedEnv\u001b[0m: Env Pendulum-v0 not found (valid versions include ['Pendulum-v1'])" + ] + } + ], "source": [ "if __name__ == \"__main__\":\n", " cfg=SACConfig()\n", @@ -193,5 +187,35 @@ " plot_rewards(rewards,ma_rewards,tag=\"eval\",env=cfg.env,algo = cfg.algo,path=cfg.result_path)\n" ] } - ] -} \ No newline at end of file + ], + "metadata": { + "interpreter": { + "hash": "fe38df673a99c62a9fea33a7aceda74c9b65b12ee9d076c5851d98b692a4989a" + }, + "kernelspec": { + "display_name": "Python 3.7.10 64-bit ('mujoco': conda)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.10" + }, + "metadata": { + "interpreter": { + "hash": "fd81e6a9e450d5c245c1a0b5da0b03c89c450f614a13afa2acb1654375922756" + } + }, + "orig_nbformat": 2 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/codes/SAC/task0_train.py b/codes/SAC/task0_train.py index 1996b01..4bc7221 100644 --- a/codes/SAC/task0_train.py +++ b/codes/SAC/task0_train.py @@ -29,9 +29,9 @@ curr_time = datetime.datetime.now().strftime("%Y%m%d-%H%M%S") # obtain current t class SACConfig: def __init__(self) -> None: self.algo = 'SAC' - self.env = 'Pendulum-v0' - self.result_path = curr_path+"/outputs/" +self.env+'/'+curr_time+'/results/' # path to save results - self.model_path = curr_path+"/outputs/" +self.env+'/'+curr_time+'/models/' # path to save models + self.env_name = 'Pendulum-v1' + self.result_path = curr_path+"/outputs/" +self.env_name+'/'+curr_time+'/results/' # path to save results + self.model_path = curr_path+"/outputs/" +self.env_name+'/'+curr_time+'/models/' # path to save models self.train_eps = 300 self.train_steps = 500 self.eval_eps = 50 @@ -50,7 +50,7 @@ class SACConfig: self.device=torch.device("cuda" if torch.cuda.is_available() else "cpu") def env_agent_config(cfg,seed=1): - env = NormalizedActions(gym.make("Pendulum-v0")) + env = NormalizedActions(gym.make(cfg.env_name)) env.seed(seed) action_dim = env.action_space.shape[0] state_dim = env.observation_space.shape[0] @@ -59,7 +59,7 @@ def env_agent_config(cfg,seed=1): def train(cfg,env,agent): print('Start to train !') - print(f'Env: {cfg.env}, Algorithm: {cfg.algo}, Device: {cfg.device}') + print(f'Env: {cfg.env_name}, Algorithm: {cfg.algo}, Device: {cfg.device}') rewards = [] ma_rewards = [] # moveing average reward for i_ep in range(cfg.train_eps): @@ -86,7 +86,7 @@ def train(cfg,env,agent): def eval(cfg,env,agent): print('Start to eval !') - print(f'Env: {cfg.env}, Algorithm: {cfg.algo}, Device: {cfg.device}') + print(f'Env: {cfg.env_name}, Algorithm: {cfg.algo}, Device: {cfg.device}') rewards = [] ma_rewards = [] # moveing average reward for i_ep in range(cfg.eval_eps): diff --git a/codes/common/plot.py b/codes/common/plot.py index d14b8d4..bc9c1dd 100644 --- a/codes/common/plot.py +++ b/codes/common/plot.py @@ -11,30 +11,12 @@ Environment: ''' import matplotlib.pyplot as plt import seaborn as sns -# from matplotlib.font_manager import FontProperties # 导入字体模块 - -# def chinese_font(): -# ''' 设置中文字体 -# ''' -# return FontProperties(fname='/System/Library/Fonts/STHeiti Light.ttc',size=15) # fname系统字体路径,此处是mac的 -# def plot_rewards_cn(rewards,ma_rewards,tag="train",env='CartPole-v0',algo = "DQN",save=True,path='./'): -# ''' 中文画图 -# ''' -# sns.set() -# plt.figure() -# plt.title(u"{}环境下{}算法的学习曲线".format(env,algo),fontproperties=chinese_font()) -# plt.xlabel(u'回合数',fontproperties=chinese_font()) -# plt.plot(rewards) -# plt.plot(ma_rewards) -# plt.legend((u'奖励',u'滑动平均奖励',),loc="best",prop=chinese_font()) -# if save: -# plt.savefig(path+f"{tag}_rewards_curve_cn") -# # plt.show() +from matplotlib.font_manager import FontProperties # 导入字体模块 def plot_rewards(rewards,ma_rewards,plot_cfg,tag='train'): sns.set() plt.figure() # 创建一个图形实例,方便同时多画几个图 - plt.title("learning curve on {} of {} for {}".format(plot_cfg.device, plot_cfg.algo, plot_cfg.env)) + plt.title("learning curve on {} of {} for {}".format(plot_cfg.device, plot_cfg.algo, plot_cfg.env_name)) plt.xlabel('epsiodes') plt.plot(rewards,label='rewards') plt.plot(ma_rewards,label='ma rewards') @@ -42,17 +24,6 @@ def plot_rewards(rewards,ma_rewards,plot_cfg,tag='train'): if plot_cfg.save: plt.savefig(plot_cfg.result_path+"{}_rewards_curve".format(tag)) plt.show() -# def plot_rewards(rewards,ma_rewards,tag="train",env='CartPole-v0',algo = "DQN",save=True,path='./'): -# sns.set() -# plt.figure() # 创建一个图形实例,方便同时多画几个图 -# plt.title("average learning curve of {} for {}".format(algo,env)) -# plt.xlabel('epsiodes') -# plt.plot(rewards,label='rewards') -# plt.plot(ma_rewards,label='ma rewards') -# plt.legend() -# if save: -# plt.savefig(path+"{}_rewards_curve".format(tag)) -# plt.show() def plot_losses(losses,algo = "DQN",save=True,path='./'): sns.set() diff --git a/codes/envs/gym_info.md b/codes/envs/gym_info.md index aecac48..dd4268a 100644 --- a/codes/envs/gym_info.md +++ b/codes/envs/gym_info.md @@ -8,11 +8,12 @@ ### [Pendulum-v0](https://github.com/openai/gym/wiki/Pendulum-v0) +注:gym 0.18.0之后版本中Pendulum-v0已经改为Pendulum-v1 image-20200820174814084 钟摆以随机位置开始,目标是将其摆动,使其保持向上直立。动作空间是连续的,值的区间为[-2,2]。每个step给的reward最低为-16.27,最高为0。目前最好的成绩是100个episode的reward之和为-123.11 ± 6.86。 -### CliffWalking-v0 +### 悬崖寻路问题(CliffWalking)是指在一个4 x 12的网格中,智能体以网格的左下角位置为起点,以网格的下角位置为终点,目标是移动智能体到达终点位置,智能体每次可以在上、下、左、右这4个方向中移动一步,每移动一步会得到-1单位的奖励。