diff --git a/codes/DQN/README.md b/codes/DQN-series/DQN/README.md similarity index 100% rename from codes/DQN/README.md rename to codes/DQN-series/DQN/README.md diff --git a/codes/DQN/agent.py b/codes/DQN-series/DQN/agent.py similarity index 73% rename from codes/DQN/agent.py rename to codes/DQN-series/DQN/agent.py index 66c11d7..27845d2 100644 --- a/codes/DQN/agent.py +++ b/codes/DQN-series/DQN/agent.py @@ -12,9 +12,6 @@ LastEditTime: 2021-09-15 13:35:36 '''off-policy ''' - - - import torch import torch.nn as nn import torch.optim as optim @@ -24,9 +21,9 @@ import numpy as np from common.memory import ReplayBuffer from common.model import MLP class DQN: - def __init__(self, state_dim, action_dim, cfg): + def __init__(self, n_states, n_actions, cfg): - self.action_dim = action_dim # 总的动作个数 + self.n_actions = n_actions # 总的动作个数 self.device = cfg.device # 设备,cpu或gpu等 self.gamma = cfg.gamma # 奖励的折扣因子 # e-greedy策略相关参数 @@ -35,15 +32,15 @@ class DQN: (cfg.epsilon_start - cfg.epsilon_end) * \ math.exp(-1. * frame_idx / cfg.epsilon_decay) self.batch_size = cfg.batch_size - self.policy_net = MLP(state_dim, action_dim,hidden_dim=cfg.hidden_dim).to(self.device) - self.target_net = MLP(state_dim, action_dim,hidden_dim=cfg.hidden_dim).to(self.device) + self.policy_net = MLP(n_states, n_actions,hidden_dim=cfg.hidden_dim).to(self.device) + self.target_net = MLP(n_states, n_actions,hidden_dim=cfg.hidden_dim).to(self.device) for target_param, param in zip(self.target_net.parameters(),self.policy_net.parameters()): # 复制参数到目标网路targe_net target_param.data.copy_(param.data) self.optimizer = optim.Adam(self.policy_net.parameters(), lr=cfg.lr) # 优化器 - self.memory = ReplayBuffer(cfg.memory_capacity) + self.memory = ReplayBuffer(cfg.memory_capacity) # 经验回放 def choose_action(self, state): - '''选择动作 + ''' 选择动作 ''' self.frame_idx += 1 if random.random() > self.epsilon(self.frame_idx): @@ -52,13 +49,7 @@ class DQN: q_values = self.policy_net(state) action = q_values.max(1)[1].item() # 选择Q值最大的动作 else: - action = random.randrange(self.action_dim) - return action - def predict(self,state): - with torch.no_grad(): - state = torch.tensor([state], device=self.device, dtype=torch.float32) - q_values = self.policy_net(state) - action = q_values.max(1)[1].item() + action = random.randrange(self.n_actions) return action def update(self): if len(self.memory) < self.batch_size: # 当memory中不满足一个批量时,不更新策略 @@ -67,16 +58,11 @@ class DQN: state_batch, action_batch, reward_batch, next_state_batch, done_batch = self.memory.sample( self.batch_size) # 转为张量 - state_batch = torch.tensor( - state_batch, device=self.device, dtype=torch.float) - action_batch = torch.tensor(action_batch, device=self.device).unsqueeze( - 1) - reward_batch = torch.tensor( - reward_batch, device=self.device, dtype=torch.float) - next_state_batch = torch.tensor( - next_state_batch, device=self.device, dtype=torch.float) - done_batch = torch.tensor(np.float32( - done_batch), device=self.device) + state_batch = torch.tensor(state_batch, device=self.device, dtype=torch.float) + action_batch = torch.tensor(action_batch, device=self.device).unsqueeze(1) + reward_batch = torch.tensor(reward_batch, device=self.device, dtype=torch.float) + next_state_batch = torch.tensor(next_state_batch, device=self.device, dtype=torch.float) + done_batch = torch.tensor(np.float32(done_batch), device=self.device) q_values = self.policy_net(state_batch).gather(dim=1, index=action_batch) # 计算当前状态(s_t,a)对应的Q(s_t, a) next_q_values = self.target_net(next_state_batch).max(1)[0].detach() # 计算下一时刻的状态(s_t_,a)对应的Q值 # 计算期望的Q值,对于终止状态,此时done_batch[0]=1, 对应的expected_q_value等于reward diff --git a/codes/DQN/assets/eval_rewards_curve.png b/codes/DQN-series/DQN/assets/eval_rewards_curve.png similarity index 100% rename from codes/DQN/assets/eval_rewards_curve.png rename to codes/DQN-series/DQN/assets/eval_rewards_curve.png diff --git a/codes/DQN/assets/image-20210507162813393.png b/codes/DQN-series/DQN/assets/image-20210507162813393.png similarity index 100% rename from codes/DQN/assets/image-20210507162813393.png rename to codes/DQN-series/DQN/assets/image-20210507162813393.png diff --git a/codes/DQN/assets/rewards_curve_train.png b/codes/DQN-series/DQN/assets/rewards_curve_train.png similarity index 100% rename from codes/DQN/assets/rewards_curve_train.png rename to codes/DQN-series/DQN/assets/rewards_curve_train.png diff --git a/codes/DQN/assets/train_rewards_curve.png b/codes/DQN-series/DQN/assets/train_rewards_curve.png similarity index 100% rename from codes/DQN/assets/train_rewards_curve.png rename to codes/DQN-series/DQN/assets/train_rewards_curve.png diff --git a/codes/DQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70.png b/codes/DQN-series/DQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70.png similarity index 100% rename from codes/DQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70.png rename to codes/DQN-series/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/20211109-200235/models/dqn_checkpoint.pth b/codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/models/dqn_checkpoint.pth new file mode 100644 index 0000000..0686337 Binary files /dev/null and b/codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/models/dqn_checkpoint.pth differ diff --git a/codes/DQN/outputs/CartPole-v0/20210915-145623/results/eval_ma_rewards.npy b/codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/results/eval_ma_rewards.npy similarity index 100% rename from codes/DQN/outputs/CartPole-v0/20210915-145623/results/eval_ma_rewards.npy rename to codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/results/eval_ma_rewards.npy diff --git a/codes/DQN/outputs/CartPole-v0/20210915-145623/results/eval_rewards.npy b/codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/results/eval_rewards.npy similarity index 100% rename from codes/DQN/outputs/CartPole-v0/20210915-145623/results/eval_rewards.npy rename to codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/results/eval_rewards.npy diff --git a/codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/results/eval_rewards_curve.png b/codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/results/eval_rewards_curve.png new file mode 100644 index 0000000..a260f79 Binary files /dev/null and b/codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/results/eval_rewards_curve.png 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 new file mode 100644 index 0000000..952fab3 Binary files /dev/null and b/codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/results/train_ma_rewards.npy differ diff --git a/codes/DQN/outputs/CartPole-v0/20210915-145623/results/train_rewards.npy b/codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/results/train_rewards.npy similarity index 59% rename from codes/DQN/outputs/CartPole-v0/20210915-145623/results/train_rewards.npy rename to codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/results/train_rewards.npy index 5f8371f..43e4be6 100644 Binary files a/codes/DQN/outputs/CartPole-v0/20210915-145623/results/train_rewards.npy and b/codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/results/train_rewards.npy 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 new file mode 100644 index 0000000..d4b6789 Binary files /dev/null and b/codes/DQN-series/DQN/outputs/CartPole-v0/20211109-200235/results/train_rewards_curve.png differ diff --git a/codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/models/dqn_checkpoint.pth b/codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/models/dqn_checkpoint.pth new file mode 100644 index 0000000..a0b6ef9 Binary files /dev/null and b/codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/models/dqn_checkpoint.pth differ diff --git a/codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/results/eval_ma_rewards.npy b/codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/results/eval_ma_rewards.npy new file mode 100644 index 0000000..343fcc6 Binary files /dev/null and b/codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/results/eval_ma_rewards.npy differ diff --git a/codes/DQN/outputs/CartPole-v0/20210912-013122/results/eval_rewards.npy b/codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/results/eval_rewards.npy similarity index 65% rename from codes/DQN/outputs/CartPole-v0/20210912-013122/results/eval_rewards.npy rename to codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/results/eval_rewards.npy index 6498864..343fcc6 100644 Binary files a/codes/DQN/outputs/CartPole-v0/20210912-013122/results/eval_rewards.npy and b/codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/results/eval_rewards.npy 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 new file mode 100644 index 0000000..a260f79 Binary files /dev/null and b/codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/results/eval_rewards_curve.png differ diff --git a/codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/results/train_ma_rewards.npy b/codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/results/train_ma_rewards.npy new file mode 100644 index 0000000..1e0ab6c Binary files /dev/null and b/codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/results/train_ma_rewards.npy differ diff --git a/codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/results/train_rewards.npy b/codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/results/train_rewards.npy new file mode 100644 index 0000000..88c137f Binary files /dev/null and b/codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/results/train_rewards.npy differ diff --git a/codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/results/train_rewards_curve.png b/codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/results/train_rewards_curve.png new file mode 100644 index 0000000..4c14b8d Binary files /dev/null and b/codes/DQN-series/DQN/outputs/CartPole-v0/20211111-165800/results/train_rewards_curve.png differ diff --git a/codes/DQN-series/DQN/task0_train.ipynb b/codes/DQN-series/DQN/task0_train.ipynb new file mode 100644 index 0000000..b9a04fc --- /dev/null +++ b/codes/DQN-series/DQN/task0_train.ipynb @@ -0,0 +1,379 @@ +{ + "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/task0_train.py b/codes/DQN-series/DQN/task0_train.py similarity index 53% rename from codes/DQN/task0_train.py rename to codes/DQN-series/DQN/task0_train.py index 70b5b69..6827bb0 100644 --- a/codes/DQN/task0_train.py +++ b/codes/DQN-series/DQN/task0_train.py @@ -19,19 +19,14 @@ import torch import datetime from common.utils import save_results, make_dir -from common.plot import plot_rewards,plot_rewards_cn +from common.plot import plot_rewards from DQN.agent import DQN 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.result_path = curr_path+"/outputs/" + self.env + \ - '/'+curr_time+'/results/' # 保存结果的路径 - self.model_path = curr_path+"/outputs/" + self.env + \ - '/'+curr_time+'/models/' # 保存模型的路径 self.train_eps = 200 # 训练的回合数 self.eval_eps = 30 # 测试的回合数 self.gamma = 0.95 # 强化学习中的折扣因子 @@ -42,42 +37,53 @@ 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 # hidden size of net - + 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 + \ + '/'+curr_time+'/results/' # 保存结果的路径 + self.model_path = curr_path+"/outputs/" + self.env + \ + '/'+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.seed(seed) - n_states = env.observation_space.shape[0] - n_actions = env.action_space.n - agent = DQN(n_states,n_actions,cfg) + ''' 创建环境和智能体 + ''' + env = gym.make(cfg.env) # 创建环境 + env.seed(seed) # 设置随机种子 + n_states = env.observation_space.shape[0] # 状态数 + n_actions = env.action_space.n # 动作数 + agent = DQN(n_states,n_actions,cfg) # 创建智能体 return env,agent def train(cfg, env, agent): + ''' 训练 + ''' print('开始训练!') print(f'环境:{cfg.env}, 算法:{cfg.algo}, 设备:{cfg.device}') - rewards = [] # 记录奖励 - ma_rewards = [] # 记录滑动平均奖励 + rewards = [] # 记录所有回合的奖励 + ma_rewards = [] # 记录所有回合的滑动平均奖励 for i_ep in range(cfg.train_eps): - state = env.reset() - done = False - ep_reward = 0 + ep_reward = 0 # 记录一回合内的奖励 + state = env.reset() # 重置环境,返回初始状态 while True: - action = agent.choose_action(state) - next_state, reward, done, _ = env.step(action) - ep_reward += reward - agent.memory.push(state, action, reward, next_state, done) - state = next_state - agent.update() + action = agent.choose_action(state) # 选择动作 + next_state, reward, done, _ = env.step(action) # 更新环境,返回transition + agent.memory.push(state, action, reward, next_state, done) # 保存transition + state = next_state # 更新下一个状态 + agent.update() # 更新智能体 + ep_reward += reward # 累加奖励 if done: break - if (i_ep+1) % cfg.target_update == 0: + if (i_ep+1) % cfg.target_update == 0: # 智能体目标网络更新 agent.target_net.load_state_dict(agent.policy_net.state_dict()) - if (i_ep+1)%10 == 0: + if (i_ep+1)%10 == 0: print('回合:{}/{}, 奖励:{}'.format(i_ep+1, cfg.train_eps, ep_reward)) rewards.append(ep_reward) - # save ma_rewards if ma_rewards: ma_rewards.append(0.9*ma_rewards[-1]+0.1*ep_reward) else: @@ -88,16 +94,19 @@ def train(cfg, env, agent): def eval(cfg,env,agent): print('开始测试!') print(f'环境:{cfg.env}, 算法:{cfg.algo}, 设备:{cfg.device}') - rewards = [] - ma_rewards = [] # moving average rewards + # 由于测试不需要使用epsilon-greedy策略,所以相应的值设置为0 + cfg.epsilon_start = 0.0 # e-greedy策略中初始epsilon + cfg.epsilon_end = 0.0 # e-greedy策略中的终止epsilon + rewards = [] # 记录所有回合的奖励 + ma_rewards = [] # 记录所有回合的滑动平均奖励 for i_ep in range(cfg.eval_eps): - ep_reward = 0 # reward per episode - state = env.reset() + ep_reward = 0 # 记录一回合内的奖励 + state = env.reset() # 重置环境,返回初始状态 while True: - action = agent.predict(state) - next_state, reward, done, _ = env.step(action) - state = next_state - ep_reward += reward + action = agent.choose_action(state) # 选择动作 + next_state, reward, done, _ = env.step(action) # 更新环境,返回transition + state = next_state # 更新下一个状态 + ep_reward += reward # 累加奖励 if done: break rewards.append(ep_reward) @@ -111,17 +120,17 @@ def eval(cfg,env,agent): if __name__ == "__main__": cfg = DQNConfig() + 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", - 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) + 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_cn(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/DoubleDQN/README.md b/codes/DQN-series/DoubleDQN/README.md similarity index 100% rename from codes/DoubleDQN/README.md rename to codes/DQN-series/DoubleDQN/README.md diff --git a/codes/DoubleDQN/agent.py b/codes/DQN-series/DoubleDQN/agent.py similarity index 100% rename from codes/DoubleDQN/agent.py rename to codes/DQN-series/DoubleDQN/agent.py diff --git a/codes/DoubleDQN/assets/20201222145725907.png b/codes/DQN-series/DoubleDQN/assets/20201222145725907.png similarity index 100% rename from codes/DoubleDQN/assets/20201222145725907.png rename to codes/DQN-series/DoubleDQN/assets/20201222145725907.png diff --git a/codes/DoubleDQN/assets/20201222150225327.png b/codes/DQN-series/DoubleDQN/assets/20201222150225327.png similarity index 100% rename from codes/DoubleDQN/assets/20201222150225327.png rename to codes/DQN-series/DoubleDQN/assets/20201222150225327.png diff --git a/codes/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70-20210328110837128.png b/codes/DQN-series/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70-20210328110837128.png similarity index 100% rename from codes/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70-20210328110837128.png rename to codes/DQN-series/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70-20210328110837128.png diff --git a/codes/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70-20210328110837146.png b/codes/DQN-series/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70-20210328110837146.png similarity index 100% rename from codes/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70-20210328110837146.png rename to codes/DQN-series/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70-20210328110837146.png diff --git a/codes/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70-20210328110837157.png b/codes/DQN-series/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70-20210328110837157.png similarity index 100% rename from codes/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70-20210328110837157.png rename to codes/DQN-series/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70-20210328110837157.png diff --git a/codes/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70.png b/codes/DQN-series/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70.png similarity index 100% rename from codes/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70.png rename to codes/DQN-series/DoubleDQN/assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70.png diff --git a/codes/DoubleDQN/outputs/CartPole-v0/20210504-150900/models/checkpoint.pth b/codes/DQN-series/DoubleDQN/outputs/CartPole-v0/20210504-150900/models/checkpoint.pth similarity index 100% rename from codes/DoubleDQN/outputs/CartPole-v0/20210504-150900/models/checkpoint.pth rename to codes/DQN-series/DoubleDQN/outputs/CartPole-v0/20210504-150900/models/checkpoint.pth diff --git a/codes/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/eval_ma_rewards.npy b/codes/DQN-series/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/eval_ma_rewards.npy similarity index 100% rename from codes/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/eval_ma_rewards.npy rename to codes/DQN-series/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/eval_ma_rewards.npy diff --git a/codes/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/eval_rewards.npy b/codes/DQN-series/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/eval_rewards.npy similarity index 100% rename from codes/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/eval_rewards.npy rename to codes/DQN-series/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/eval_rewards.npy diff --git a/codes/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/eval_rewards_curve.png b/codes/DQN-series/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/eval_rewards_curve.png similarity index 100% rename from codes/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/eval_rewards_curve.png rename to codes/DQN-series/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/eval_rewards_curve.png diff --git a/codes/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/train_ma_rewards.npy b/codes/DQN-series/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/train_ma_rewards.npy similarity index 100% rename from codes/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/train_ma_rewards.npy rename to codes/DQN-series/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/train_ma_rewards.npy diff --git a/codes/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/train_rewards.npy b/codes/DQN-series/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/train_rewards.npy similarity index 100% rename from codes/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/train_rewards.npy rename to codes/DQN-series/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/train_rewards.npy diff --git a/codes/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/train_rewards_curve.png b/codes/DQN-series/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/train_rewards_curve.png similarity index 100% rename from codes/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/train_rewards_curve.png rename to codes/DQN-series/DoubleDQN/outputs/CartPole-v0/20210504-150900/results/train_rewards_curve.png diff --git a/codes/DoubleDQN/task0_train.ipynb b/codes/DQN-series/DoubleDQN/task0_train.ipynb similarity index 100% rename from codes/DoubleDQN/task0_train.ipynb rename to codes/DQN-series/DoubleDQN/task0_train.ipynb diff --git a/codes/DoubleDQN/task0_train.py b/codes/DQN-series/DoubleDQN/task0_train.py similarity index 100% rename from codes/DoubleDQN/task0_train.py rename to codes/DQN-series/DoubleDQN/task0_train.py diff --git a/codes/DQN-series/DuelingDQN/assets/task0_train_20211112021954.png b/codes/DQN-series/DuelingDQN/assets/task0_train_20211112021954.png new file mode 100644 index 0000000..2529311 Binary files /dev/null and b/codes/DQN-series/DuelingDQN/assets/task0_train_20211112021954.png differ diff --git a/codes/DQN-series/DuelingDQN/task0_train.ipynb b/codes/DQN-series/DuelingDQN/task0_train.ipynb new file mode 100644 index 0000000..c2cd1c3 --- /dev/null +++ b/codes/DQN-series/DuelingDQN/task0_train.ipynb @@ -0,0 +1,418 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import math, random\n", + "import gym\n", + "import numpy as np\n", + "import torch\n", + "import torch.nn as nn\n", + "import torch.optim as optim\n", + "import torch.autograd as autograd \n", + "import torch.nn.functional as F\n", + "from IPython.display import clear_output # 清空单元格输出区域\n", + "import matplotlib.pyplot as plt\n", + "# %matplotlib inline\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "USE_CUDA = torch.cuda.is_available()\n", + "Variable = lambda *args, **kwargs: autograd.Variable(*args, **kwargs).cuda() if USE_CUDA else autograd.Variable(*args, **kwargs)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "from collections import deque\n", + "\n", + "class ReplayBuffer(object):\n", + " def __init__(self, capacity):\n", + " self.buffer = deque(maxlen=capacity)\n", + " \n", + " def push(self, state, action, reward, next_state, done):\n", + " state = np.expand_dims(state, 0)\n", + " next_state = np.expand_dims(next_state, 0)\n", + " \n", + " self.buffer.append((state, action, reward, next_state, done))\n", + " \n", + " def sample(self, batch_size):\n", + " state, action, reward, next_state, done = zip(*random.sample(self.buffer, batch_size))\n", + " return np.concatenate(state), action, reward, np.concatenate(next_state), done\n", + " \n", + " def __len__(self):\n", + " return len(self.buffer)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "env_name = \"CartPole-v0\"\n", + "env = gym.make(env_name)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "epsilon_start = 1.0\n", + "epsilon_final = 0.01\n", + "epsilon_decay = 500\n", + "\n", + "epsilon_by_frame = lambda frame_idx: epsilon_final + (epsilon_start - epsilon_final) * math.exp(-1. * frame_idx / epsilon_decay)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.plot([epsilon_by_frame(i) for i in range(10000)])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Dueling DQN 网络\n", + "\n", + "DQN等算法中使用的是一个简单的三层神经网络:一个输入层,一个隐藏层和一个输出层。如下左图:\n", + "\n", + "\"image-20211112022028670\"\n", + "\n", + "而在Dueling DQN中,我们在后面加了两个子网络结构,分别对应上面上到价格函数网络部分和优势函数网络部分。对应上面右图所示。最终Q网络的输出由价格函数网络的输出和优势函数网络的输出线性组合得到。\n", + "\n", + "我们可以直接使用上一节的价值函数的组合公式得到我们的动作价值,但是这个式子无法辨识最终输出里面$V(S, w, \\alpha)$和$A(S, A, w, \\beta)$各自的作用,为了可以体现这种可辨识性(identifiability),实际使用的组合公式如下:\n", + "\n", + "$$\n", + "Q(S, A, w, \\alpha, \\beta)=V(S, w, \\alpha)+\\left(A(S, A, w, \\beta)-\\frac{1}{\\mathcal{A}} \\sum_{a^{\\prime} \\in \\mathcal{A}} A\\left(S, a^{\\prime}, w, \\beta\\right)\\right)\n", + "$$" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "class DuelingNet(nn.Module):\n", + " def __init__(self, n_states, n_actions,hidden_size=128):\n", + " super(DuelingNet, self).__init__()\n", + " \n", + " # 隐藏层\n", + " self.hidden = nn.Sequential(\n", + " nn.Linear(n_states, hidden_size),\n", + " nn.ReLU()\n", + " )\n", + " \n", + " # 优势函数\n", + " self.advantage = nn.Sequential(\n", + " nn.Linear(hidden_size, hidden_size),\n", + " nn.ReLU(),\n", + " nn.Linear(hidden_size, n_actions)\n", + " )\n", + " \n", + " # 价值函数\n", + " self.value = nn.Sequential(\n", + " nn.Linear(hidden_size, hidden_size),\n", + " nn.ReLU(),\n", + " nn.Linear(hidden_size, 1)\n", + " )\n", + " \n", + " def forward(self, x):\n", + " x = self.hidden(x)\n", + " advantage = self.advantage(x)\n", + " value = self.value(x)\n", + " return value + advantage - advantage.mean()\n", + " \n", + " def act(self, state, epsilon):\n", + " if random.random() > epsilon:\n", + " with torch.no_grad():\n", + " state = Variable(torch.FloatTensor(state).unsqueeze(0))\n", + " q_value = self.forward(state)\n", + " action = q_value.max(1)[1].item()\n", + " else:\n", + " action = random.randrange(env.action_space.n)\n", + " return action" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "unexpected EOF while parsing (, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m class DuelingDQN:\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m unexpected EOF while parsing\n" + ] + } + ], + "source": [ + "class DuelingDQN:\n", + " def __init__(self,n_states,n_actions,cfg) -> None:\n", + " self.batch_size = cfg.batch_size\n", + " self.device = cfg.device\n", + " self.loss_history = [] # 记录loss的变化\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.policy_net = DuelingNet(n_states, n_actions,hidden_dim=cfg.hidden_dim).to(self.device)\n", + " self.target_net = DuelingNet(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", + " def choose_action(self,state):\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.action_dim)\n", + " return action\n", + " def update(self):\n", + " if len(self.memory) < self.batch_size: # 当memory中不满足一个批量时,不更新策略\n", + " return\n", + " state, action, reward, next_state, done = self.memory.sample(batch_size)\n", + " state = torch.tensor(state, device=self.device, dtype=torch.float)\n", + " action = torch.tensor(action, device=self.device).unsqueeze(1) \n", + " reward = torch.tensor(reward, device=self.device, dtype=torch.float) \n", + " next_state = torch.tensor(next_state, device=self.device, dtype=torch.float)\n", + " done = torch.tensor(np.float32(done), device=self.device)\n", + " q_values = self.policy_net(state)\n", + " next_q_values = self.target_net(next_state)\n", + "\n", + " q_value = q_values.gather(1, action.unsqueeze(1)).squeeze(1)\n", + " next_q_value = next_q_values.max(1)[0]\n", + " expected_q_value = reward + gamma * next_q_value * (1 - done)\n", + " \n", + " loss = (q_value - expected_q_value.detach()).pow(2).mean()\n", + " self.loss_history.append(loss)\n", + " self.optimizer.zero_grad()\n", + " loss.backward()\n", + " self.optimizer.step()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "current_model = DuelingNet(env.observation_space.shape[0], env.action_space.n)\n", + "target_model = DuelingNet(env.observation_space.shape[0], env.action_space.n)\n", + "\n", + "if USE_CUDA:\n", + " current_model = current_model.cuda()\n", + " target_model = target_model.cuda()\n", + " \n", + "optimizer = optim.Adam(current_model.parameters())\n", + "\n", + "replay_buffer = ReplayBuffer(1000)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "def update_target(current_model, target_model):\n", + " target_model.load_state_dict(current_model.state_dict())" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "update_target(current_model, target_model)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "def compute_td_loss(batch_size):\n", + " state, action, reward, next_state, done = replay_buffer.sample(batch_size)\n", + "\n", + " state = Variable(torch.FloatTensor(np.float32(state)))\n", + " next_state = Variable(torch.FloatTensor(np.float32(next_state)))\n", + " action = Variable(torch.LongTensor(action))\n", + " reward = Variable(torch.FloatTensor(reward))\n", + " done = Variable(torch.FloatTensor(done))\n", + "\n", + " q_values = current_model(state)\n", + " next_q_values = target_model(next_state)\n", + "\n", + " q_value = q_values.gather(1, action.unsqueeze(1)).squeeze(1)\n", + " next_q_value = next_q_values.max(1)[0]\n", + " expected_q_value = reward + gamma * next_q_value * (1 - done)\n", + " \n", + " loss = (q_value - expected_q_value.detach()).pow(2).mean()\n", + " \n", + " optimizer.zero_grad()\n", + " loss.backward()\n", + " optimizer.step()\n", + " \n", + " return loss" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "def plot(frame_idx, rewards, losses):\n", + " clear_output(True) # 清空单元格输出区域,因为多次打印,每次需要清楚前面打印的图片\n", + " plt.figure(figsize=(20,5))\n", + " plt.subplot(131)\n", + " plt.title('frame %s. reward: %s' % (frame_idx, np.mean(rewards[-10:])))\n", + " plt.plot(rewards)\n", + " plt.subplot(132)\n", + " plt.title('loss')\n", + " plt.plot(losses)\n", + " plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "num_frames = 10000\n", + "batch_size = 32\n", + "gamma = 0.99\n", + "\n", + "losses = []\n", + "all_rewards = []\n", + "ep_reward = 0\n", + "\n", + "state = env.reset()\n", + "for frame_idx in range(1, num_frames + 1):\n", + " epsilon = epsilon_by_frame(frame_idx)\n", + " action = current_model.act(state, epsilon)\n", + " next_state, reward, done, _ = env.step(action)\n", + " replay_buffer.push(state, action, reward, next_state, done)\n", + " \n", + " state = next_state\n", + " ep_reward += reward\n", + " \n", + " if done:\n", + " state = env.reset()\n", + " all_rewards.append(ep_reward)\n", + " ep_reward = 0\n", + " \n", + " if len(replay_buffer) > batch_size:\n", + " loss = compute_td_loss(batch_size)\n", + " losses.append(loss.item())\n", + " \n", + " if frame_idx % 200 == 0:\n", + " plot(frame_idx, all_rewards, losses)\n", + " \n", + " if frame_idx % 100 == 0:\n", + " update_target(current_model, target_model)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 参考\n", + "\n", + "[强化学习(十二) Dueling DQN](https://www.cnblogs.com/pinard/p/9923859.html)" + ] + } + ], + "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" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/codes/HierarchicalDQN/README.md b/codes/DQN-series/HierarchicalDQN/README.md similarity index 100% rename from codes/HierarchicalDQN/README.md rename to codes/DQN-series/HierarchicalDQN/README.md diff --git a/codes/HierarchicalDQN/agent.py b/codes/DQN-series/HierarchicalDQN/agent.py similarity index 100% rename from codes/HierarchicalDQN/agent.py rename to codes/DQN-series/HierarchicalDQN/agent.py diff --git a/codes/HierarchicalDQN/assets/image-20210331153115575.png b/codes/DQN-series/HierarchicalDQN/assets/image-20210331153115575.png similarity index 100% rename from codes/HierarchicalDQN/assets/image-20210331153115575.png rename to codes/DQN-series/HierarchicalDQN/assets/image-20210331153115575.png diff --git a/codes/HierarchicalDQN/assets/image-20210331153542314.png b/codes/DQN-series/HierarchicalDQN/assets/image-20210331153542314.png similarity index 100% rename from codes/HierarchicalDQN/assets/image-20210331153542314.png rename to codes/DQN-series/HierarchicalDQN/assets/image-20210331153542314.png diff --git a/codes/HierarchicalDQN/results/20210331-134559/ma_rewards_train.npy b/codes/DQN-series/HierarchicalDQN/results/20210331-134559/ma_rewards_train.npy similarity index 100% rename from codes/HierarchicalDQN/results/20210331-134559/ma_rewards_train.npy rename to codes/DQN-series/HierarchicalDQN/results/20210331-134559/ma_rewards_train.npy diff --git a/codes/HierarchicalDQN/results/20210331-134559/rewards_curve_train.png b/codes/DQN-series/HierarchicalDQN/results/20210331-134559/rewards_curve_train.png similarity index 100% rename from codes/HierarchicalDQN/results/20210331-134559/rewards_curve_train.png rename to codes/DQN-series/HierarchicalDQN/results/20210331-134559/rewards_curve_train.png diff --git a/codes/HierarchicalDQN/results/20210331-134559/rewards_train.npy b/codes/DQN-series/HierarchicalDQN/results/20210331-134559/rewards_train.npy similarity index 100% rename from codes/HierarchicalDQN/results/20210331-134559/rewards_train.npy rename to codes/DQN-series/HierarchicalDQN/results/20210331-134559/rewards_train.npy diff --git a/codes/HierarchicalDQN/results/20210331-145852/losses_curve.png b/codes/DQN-series/HierarchicalDQN/results/20210331-145852/losses_curve.png similarity index 100% rename from codes/HierarchicalDQN/results/20210331-145852/losses_curve.png rename to codes/DQN-series/HierarchicalDQN/results/20210331-145852/losses_curve.png diff --git a/codes/HierarchicalDQN/results/20210331-145852/ma_rewards_train.npy b/codes/DQN-series/HierarchicalDQN/results/20210331-145852/ma_rewards_train.npy similarity index 100% rename from codes/HierarchicalDQN/results/20210331-145852/ma_rewards_train.npy rename to codes/DQN-series/HierarchicalDQN/results/20210331-145852/ma_rewards_train.npy diff --git a/codes/HierarchicalDQN/results/20210331-145852/rewards_curve_train.png b/codes/DQN-series/HierarchicalDQN/results/20210331-145852/rewards_curve_train.png similarity index 100% rename from codes/HierarchicalDQN/results/20210331-145852/rewards_curve_train.png rename to codes/DQN-series/HierarchicalDQN/results/20210331-145852/rewards_curve_train.png diff --git a/codes/HierarchicalDQN/results/20210331-145852/rewards_train.npy b/codes/DQN-series/HierarchicalDQN/results/20210331-145852/rewards_train.npy similarity index 100% rename from codes/HierarchicalDQN/results/20210331-145852/rewards_train.npy rename to codes/DQN-series/HierarchicalDQN/results/20210331-145852/rewards_train.npy diff --git a/codes/HierarchicalDQN/saved_model/20210331-134559/meta_checkpoint.pth b/codes/DQN-series/HierarchicalDQN/saved_model/20210331-134559/meta_checkpoint.pth similarity index 100% rename from codes/HierarchicalDQN/saved_model/20210331-134559/meta_checkpoint.pth rename to codes/DQN-series/HierarchicalDQN/saved_model/20210331-134559/meta_checkpoint.pth diff --git a/codes/HierarchicalDQN/saved_model/20210331-134559/policy_checkpoint.pth b/codes/DQN-series/HierarchicalDQN/saved_model/20210331-134559/policy_checkpoint.pth similarity index 100% rename from codes/HierarchicalDQN/saved_model/20210331-134559/policy_checkpoint.pth rename to codes/DQN-series/HierarchicalDQN/saved_model/20210331-134559/policy_checkpoint.pth diff --git a/codes/HierarchicalDQN/saved_model/20210331-145852/meta_checkpoint.pth b/codes/DQN-series/HierarchicalDQN/saved_model/20210331-145852/meta_checkpoint.pth similarity index 100% rename from codes/HierarchicalDQN/saved_model/20210331-145852/meta_checkpoint.pth rename to codes/DQN-series/HierarchicalDQN/saved_model/20210331-145852/meta_checkpoint.pth diff --git a/codes/HierarchicalDQN/saved_model/20210331-145852/policy_checkpoint.pth b/codes/DQN-series/HierarchicalDQN/saved_model/20210331-145852/policy_checkpoint.pth similarity index 100% rename from codes/HierarchicalDQN/saved_model/20210331-145852/policy_checkpoint.pth rename to codes/DQN-series/HierarchicalDQN/saved_model/20210331-145852/policy_checkpoint.pth diff --git a/codes/HierarchicalDQN/task0_train.ipynb b/codes/DQN-series/HierarchicalDQN/task0_train.ipynb similarity index 100% rename from codes/HierarchicalDQN/task0_train.ipynb rename to codes/DQN-series/HierarchicalDQN/task0_train.ipynb diff --git a/codes/HierarchicalDQN/task0_train.py b/codes/DQN-series/HierarchicalDQN/task0_train.py similarity index 100% rename from codes/HierarchicalDQN/task0_train.py rename to codes/DQN-series/HierarchicalDQN/task0_train.py diff --git a/codes/DQN-series/NoisyDQN/task0_train.ipynb b/codes/DQN-series/NoisyDQN/task0_train.ipynb new file mode 100644 index 0000000..ecd0092 --- /dev/null +++ b/codes/DQN-series/NoisyDQN/task0_train.ipynb @@ -0,0 +1,25 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "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) # 添加路径到系统路径" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/codes/DQN-series/README.md b/codes/DQN-series/README.md new file mode 100644 index 0000000..38de319 --- /dev/null +++ b/codes/DQN-series/README.md @@ -0,0 +1,3 @@ + +本目录下汇总了基础的DQN及其变种或升级,如下 + diff --git a/codes/DQN/outputs/CartPole-v0/20210912-013122/models/dqn_checkpoint.pth b/codes/DQN/outputs/CartPole-v0/20210912-013122/models/dqn_checkpoint.pth deleted file mode 100644 index 7cb626f..0000000 Binary files a/codes/DQN/outputs/CartPole-v0/20210912-013122/models/dqn_checkpoint.pth and /dev/null differ diff --git a/codes/DQN/outputs/CartPole-v0/20210912-013122/results/eval_ma_rewards.npy b/codes/DQN/outputs/CartPole-v0/20210912-013122/results/eval_ma_rewards.npy deleted file mode 100644 index 516efb9..0000000 Binary files a/codes/DQN/outputs/CartPole-v0/20210912-013122/results/eval_ma_rewards.npy and /dev/null differ diff --git a/codes/DQN/outputs/CartPole-v0/20210912-013122/results/eval_rewards_curve.png b/codes/DQN/outputs/CartPole-v0/20210912-013122/results/eval_rewards_curve.png deleted file mode 100644 index d304a19..0000000 Binary files a/codes/DQN/outputs/CartPole-v0/20210912-013122/results/eval_rewards_curve.png and /dev/null differ diff --git a/codes/DQN/outputs/CartPole-v0/20210912-013122/results/train_ma_rewards.npy b/codes/DQN/outputs/CartPole-v0/20210912-013122/results/train_ma_rewards.npy deleted file mode 100644 index 1d8f61b..0000000 Binary files a/codes/DQN/outputs/CartPole-v0/20210912-013122/results/train_ma_rewards.npy and /dev/null differ diff --git a/codes/DQN/outputs/CartPole-v0/20210912-013122/results/train_rewards.npy b/codes/DQN/outputs/CartPole-v0/20210912-013122/results/train_rewards.npy deleted file mode 100644 index 32dbedf..0000000 Binary files a/codes/DQN/outputs/CartPole-v0/20210912-013122/results/train_rewards.npy and /dev/null differ diff --git a/codes/DQN/outputs/CartPole-v0/20210912-013122/results/train_rewards_curve.png b/codes/DQN/outputs/CartPole-v0/20210912-013122/results/train_rewards_curve.png deleted file mode 100644 index 147540c..0000000 Binary files a/codes/DQN/outputs/CartPole-v0/20210912-013122/results/train_rewards_curve.png and /dev/null differ diff --git a/codes/DQN/outputs/CartPole-v0/20210915-145623/models/dqn_checkpoint.pth b/codes/DQN/outputs/CartPole-v0/20210915-145623/models/dqn_checkpoint.pth deleted file mode 100644 index ecfc662..0000000 Binary files a/codes/DQN/outputs/CartPole-v0/20210915-145623/models/dqn_checkpoint.pth and /dev/null differ diff --git a/codes/DQN/outputs/CartPole-v0/20210915-145623/results/eval_rewards_curve_cn.png b/codes/DQN/outputs/CartPole-v0/20210915-145623/results/eval_rewards_curve_cn.png deleted file mode 100644 index 1f55598..0000000 Binary files a/codes/DQN/outputs/CartPole-v0/20210915-145623/results/eval_rewards_curve_cn.png and /dev/null differ diff --git a/codes/DQN/outputs/CartPole-v0/20210915-145623/results/train_ma_rewards.npy b/codes/DQN/outputs/CartPole-v0/20210915-145623/results/train_ma_rewards.npy deleted file mode 100644 index 65ead2d..0000000 Binary files a/codes/DQN/outputs/CartPole-v0/20210915-145623/results/train_ma_rewards.npy and /dev/null differ diff --git a/codes/DQN/outputs/CartPole-v0/20210915-145623/results/train_rewards_curve_cn.png b/codes/DQN/outputs/CartPole-v0/20210915-145623/results/train_rewards_curve_cn.png deleted file mode 100644 index 617f693..0000000 Binary files a/codes/DQN/outputs/CartPole-v0/20210915-145623/results/train_rewards_curve_cn.png and /dev/null differ diff --git a/codes/DQN/task0_train.ipynb b/codes/DQN/task0_train.ipynb deleted file mode 100644 index 94ebd60..0000000 --- a/codes/DQN/task0_train.ipynb +++ /dev/null @@ -1,270 +0,0 @@ -{ - "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": "python3710jvsc74a57bd0366e1054dee9d4501b0eb8f87335afd3c67fc62db6ee611bbc7f8f5a1fefe232", - "display_name": "Python 3.7.10 64-bit ('py37': conda)" - }, - "metadata": { - "interpreter": { - "hash": "366e1054dee9d4501b0eb8f87335afd3c67fc62db6ee611bbc7f8f5a1fefe232" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2, - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "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) # add current terminal path to sys.path" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "import gym\n", - "import torch\n", - "import datetime\n", - "\n", - "from common.utils import save_results, make_dir\n", - "from common.plot import plot_rewards\n", - "from DQN.agent import DQN\n", - "\n", - "curr_time = datetime.datetime.now().strftime(\n", - " \"%Y%m%d-%H%M%S\") # obtain current time" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "class DQNConfig:\n", - " def __init__(self):\n", - " self.algo = \"DQN\" # name of algo\n", - " self.env = 'CartPole-v0'\n", - " self.result_path = curr_path+\"/outputs/\" + self.env + \\\n", - " '/'+curr_time+'/results/' # path to save results\n", - " self.model_path = curr_path+\"/outputs/\" + self.env + \\\n", - " '/'+curr_time+'/models/' # path to save results\n", - " self.train_eps = 300 # max trainng episodes\n", - " self.eval_eps = 50 # number of episodes for evaluating\n", - " self.gamma = 0.95\n", - " self.epsilon_start = 0.90 # start epsilon of e-greedy policy\n", - " self.epsilon_end = 0.01\n", - " self.epsilon_decay = 500\n", - " self.lr = 0.0001 # learning rate\n", - " self.memory_capacity = 100000 # capacity of Replay Memory\n", - " self.batch_size = 64\n", - " self.target_update = 2 # update frequency of target net\n", - " self.device = torch.device(\n", - " \"cuda\" if torch.cuda.is_available() else \"cpu\") # check gpu\n", - " self.hidden_dim = 256 # hidden size of net" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "def env_agent_config(cfg,seed=1):\n", - " env = gym.make(cfg.env) \n", - " env.seed(seed)\n", - " state_dim = env.observation_space.shape[0]\n", - " action_dim = env.action_space.n\n", - " agent = DQN(state_dim,action_dim,cfg)\n", - " return env,agent" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "def train(cfg, env, agent):\n", - " print('Start to train !')\n", - " print(f'Env:{cfg.env}, Algorithm:{cfg.algo}, Device:{cfg.device}')\n", - " rewards = []\n", - " ma_rewards = [] # moveing average reward\n", - " for i_ep in range(cfg.train_eps):\n", - " state = env.reset()\n", - " done = False\n", - " ep_reward = 0\n", - " while True:\n", - " action = agent.choose_action(state)\n", - " next_state, reward, done, _ = env.step(action)\n", - " ep_reward += reward\n", - " agent.memory.push(state, action, reward, next_state, done)\n", - " state = next_state\n", - " agent.update()\n", - " if done:\n", - " break\n", - " if i_ep % 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('Episode:{}/{}, Reward:{}'.format(i_ep+1, cfg.train_eps, ep_reward))\n", - " rewards.append(ep_reward)\n", - " # save ma rewards\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('Complete training!')\n", - " return rewards, ma_rewards" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "def eval(cfg,env,agent):\n", - " rewards = [] \n", - " ma_rewards = [] # moving average rewards\n", - " for i_ep in range(cfg.eval_eps):\n", - " ep_reward = 0 # reward per episode\n", - " state = env.reset() \n", - " while True:\n", - " action = agent.predict(state) \n", - " next_state, reward, done, _ = env.step(action) \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)%10==0:\n", - " print(f\"Episode:{i_ep+1}/{cfg.eval_eps}, reward:{ep_reward:.1f}\")\n", - " return rewards,ma_rewards" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "tags": [] - }, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Start to train !\n", - "Env:CartPole-v0, Algorithm:DQN, Device:cuda\n", - "Episode:10/300, Reward:13.0\n", - "Episode:20/300, Reward:14.0\n", - "Episode:30/300, Reward:14.0\n", - "Episode:40/300, Reward:12.0\n", - "Episode:50/300, Reward:125.0\n", - "Episode:60/300, Reward:98.0\n", - "Episode:70/300, Reward:200.0\n", - "Episode:80/300, Reward:160.0\n", - "Episode:90/300, Reward:200.0\n", - "Episode:100/300, Reward:200.0\n", - "Episode:110/300, Reward:200.0\n", - "Episode:120/300, Reward:198.0\n", - "Episode:130/300, Reward:200.0\n", - "Episode:140/300, Reward:200.0\n", - "Episode:150/300, Reward:200.0\n", - "Episode:160/300, Reward:200.0\n", - "Episode:170/300, Reward:200.0\n", - "Episode:180/300, Reward:200.0\n", - "Episode:190/300, Reward:200.0\n", - "Episode:200/300, Reward:200.0\n", - "Episode:210/300, Reward:200.0\n", - "Episode:220/300, Reward:200.0\n", - "Episode:230/300, Reward:188.0\n", - "Episode:240/300, Reward:200.0\n", - "Episode:250/300, Reward:200.0\n", - "Episode:260/300, Reward:193.0\n", - "Episode:270/300, Reward:200.0\n", - "Episode:280/300, Reward:200.0\n", - "Episode:290/300, Reward:200.0\n", - "Episode:300/300, Reward:200.0\n", - "Complete training!\n", - "results saved!\n" - ] - }, - { - "output_type": "display_data", - "data": { - "text/plain": "
", - "image/svg+xml": "\n\n\n \n \n \n \n 2021-05-04T19:04:03.044086\n image/svg+xml\n \n \n Matplotlib v3.4.1, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", - "image/png": "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/vijSE1NFSdPnqx3fz///LP44osvhM1mEwUFBWLOnDniqaee8s6v65rx/T0JIcSpU6e814WmaWLXrl1i2LBh4quvvhJvvvmmuOiii8TJkydFWVmZuOeee8T8+fNrrSdE866nhnTgySefFIsWLfJb/vLLLxcfffRRvcfhXCM9+ypmzJhBYmIiqqpy2WWX0atXL77//nsArrjiCt5//33vshs3buSKK64A4I033uD6669n+PDh6HQ6rrnmGgwGA3v27PEuP3fuXJKTkzGbzY3u68MPP+TGG28kKSmJyMhI7rjjDu928vPz2bZtG4899hgWi4XY2FhuuukmP9vqY8OGDUyaNInJkyejqioTJkxg6NChbNu2DYApU6bQs2dPFEVhzJgxTJgwgV27dnnXV1WV++67D6PR6G3HqFGjmDx5MjqdjquuusrviaYm9S27d+9enE4nN954IwaDgUsuuYTzzz+/3u383//9H7fddhvDhg1DURR69epFSkpKo+2vqw1XXHEFn376KZWVlYD7vF5++eVNOl6+7N27l4qKCu644w6MRiPjx49n6tSpTTovDZGQkEBxcTEARUVFxMfH11pGr9cTHR1NYWGh3/T77ruP1157rdb0xmhKW6ZNm8aoUaNQVRWTyeS3/tmzZ72210evXr2YMGECRqORmJgYbr75Znbu3Om3TM1rpj7GjRvHmDFjWLhwIQ8//DDjx49n48aN3HTTTfTo0YPQ0FAeeughPvjgg1ohoeZeTw3pQEVFBeHh4X7Lh4WF+T1xdTT1BxG7GOvXr+eVV17xdqpUVFRQVFQEwNixY7Farezdu5fY2FgOHTrkfeTNzMxk/fr1vPbaa95tORwOv8615OTkJu8rNzfXb/mkpCTv58zMTJxOJxdeeKF3mqZptbZfF5mZmXz00Ue1whaeTtRt27axevVqfv75ZzRNw2q1kpqa6l02Ojq61oUdFxfn/Ww2m7HZbDidzjpj0/Utm5ubS2JiIoqieOc31J6srCx69uzZaHvromYbevXqRb9+/diyZQtTp07l008/Zf369UDjx8uX3NxckpKSUNVq36lbt27k5OS0yE4POTk5REZGem3Py8urtYzT6aSoqIjo6Gi/6ampqUyZMoU1a9bQr1+/Ju+zKW1p6PxERUV5t9OjR486l8nPz2fp0qXs2rWL8vJyhBBERET4LdOU3zTA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\n" - }, - "metadata": {} - }, - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Episode:10/50, reward:188.0\n", - "Episode:20/50, reward:200.0\n", - "Episode:30/50, reward:200.0\n", - "Episode:40/50, reward:200.0\n", - "Episode:50/50, reward:171.0\n", - "results saved!\n" - ] - }, - { - "output_type": "display_data", - "data": { - "text/plain": "
", - "image/svg+xml": "\n\n\n \n \n \n \n 2021-05-04T19:04:05.465993\n image/svg+xml\n \n \n Matplotlib v3.4.1, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", - "image/png": "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\n" - }, - "metadata": {} - } - ], - "source": [ - "if __name__ == \"__main__\":\n", - " cfg = DQNConfig()\n", - "\n", - " # train\n", - " env,agent = env_agent_config(cfg,seed=1)\n", - " rewards, ma_rewards = train(cfg, env, agent)\n", - " make_dir(cfg.result_path, cfg.model_path)\n", - " agent.save(path=cfg.model_path)\n", - " save_results(rewards, ma_rewards, tag='train', path=cfg.result_path)\n", - " plot_rewards(rewards, ma_rewards, tag=\"train\",\n", - " algo=cfg.algo, path=cfg.result_path)\n", - " # eval\n", - " env,agent = env_agent_config(cfg,seed=10)\n", - " agent.load(path=cfg.model_path)\n", - " rewards,ma_rewards = eval(cfg,env,agent)\n", - " save_results(rewards,ma_rewards,tag='eval',path=cfg.result_path)\n", - " plot_rewards(rewards,ma_rewards,tag=\"eval\",env=cfg.env,algo = cfg.algo,path=cfg.result_path)" - ] - } - ] -} \ No newline at end of file diff --git a/codes/PPO/task0_train.py b/codes/PPO/task0_train.py index ccca805..04dfae0 100644 --- a/codes/PPO/task0_train.py +++ b/codes/PPO/task0_train.py @@ -100,7 +100,7 @@ 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.train_eps}, Reward:{ep_reward:.3f}") + print(f"Episode:{i_ep+1}/{cfg.eval_eps}, Reward:{ep_reward:.3f}") print('Complete evaling!') return rewards,ma_rewards diff --git a/codes/PolicyGradient/README.md b/codes/PolicyGradient/README.md index 0f9fec3..956cdbf 100644 --- a/codes/PolicyGradient/README.md +++ b/codes/PolicyGradient/README.md @@ -8,12 +8,16 @@ Policy-based方法是强化学习中与Value-based(比如Q-learning)相对的方 结合REINFORCE原理,其伪代码如下: +image-20211016004808604 + +https://pytorch.org/docs/stable/distributions.html + +加负号的原因是,在公式中应该是实现的梯度上升算法,而loss一般使用随机梯度下降的,所以加个负号保持一致性。 + ![img](assets/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pvaG5KaW0w,size_16,color_FFFFFF,t_70-20210428001336032.png) ## 实现 - - ## 参考 [REINFORCE和Reparameterization Trick](https://blog.csdn.net/JohnJim0/article/details/110230703) diff --git a/codes/PolicyGradient/agent.py b/codes/PolicyGradient/agent.py index be67601..8f349b5 100644 --- a/codes/PolicyGradient/agent.py +++ b/codes/PolicyGradient/agent.py @@ -5,7 +5,7 @@ Author: John Email: johnjim0816@gmail.com Date: 2020-11-22 23:27:44 LastEditor: John -LastEditTime: 2021-05-05 17:33:10 +LastEditTime: 2021-10-16 00:43:52 Discription: Environment: ''' @@ -56,7 +56,6 @@ class PolicyGradient: state = state_pool[i] action = Variable(torch.FloatTensor([action_pool[i]])) reward = reward_pool[i] - state = Variable(torch.from_numpy(state).float()) probs = self.policy_net(state) m = Bernoulli(probs) diff --git a/codes/PolicyGradient/assets/image-20211016004808604.png b/codes/PolicyGradient/assets/image-20211016004808604.png new file mode 100644 index 0000000..b0a56b5 Binary files /dev/null and b/codes/PolicyGradient/assets/image-20211016004808604.png differ diff --git a/codes/PolicyGradient/task0_train.py b/codes/PolicyGradient/task0_train.py index c1f4e5c..a7fb0d2 100644 --- a/codes/PolicyGradient/task0_train.py +++ b/codes/PolicyGradient/task0_train.py @@ -5,14 +5,14 @@ Author: John Email: johnjim0816@gmail.com Date: 2020-11-22 23:21:53 LastEditor: John -LastEditTime: 2021-05-05 17:35:20 +LastEditTime: 2021-10-16 00:34: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) # 添加父路径到系统路径sys.path import gym import torch @@ -23,21 +23,20 @@ from PolicyGradient.agent import PolicyGradient 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 +curr_time = datetime.datetime.now().strftime("%Y%m%d-%H%M%S") # 获取当前时间 class PGConfig: def __init__(self): - self.algo = "PolicyGradient" # name of algo - self.env = 'CartPole-v0' + self.algo = "PolicyGradient" # 算法名称 + self.env = 'CartPole-v0' # 环境名称 self.result_path = curr_path+"/outputs/" + self.env + \ - '/'+curr_time+'/results/' # path to save results + '/'+curr_time+'/results/' # 保存结果的路径 self.model_path = curr_path+"/outputs/" + self.env + \ - '/'+curr_time+'/models/' # path to save models - self.train_eps = 300 # 训练的episode数目 - self.eval_eps = 50 + '/'+curr_time+'/models/' # 保存模型的路径 + self.train_eps = 300 # 训练的回合数 + self.eval_eps = 30 # 测试的回合数 self.batch_size = 8 - self.lr = 0.01 # learning rate + self.lr = 0.01 # 学习率 self.gamma = 0.99 self.hidden_dim = 36 # dimmension of hidden layer self.device = torch.device( @@ -59,7 +58,7 @@ def train(cfg,env,agent): reward_pool = [] rewards = [] ma_rewards = [] - for i_episode in range(cfg.train_eps): + for i_ep in range(cfg.train_eps): state = env.reset() ep_reward = 0 for _ in count(): @@ -73,9 +72,9 @@ def train(cfg,env,agent): reward_pool.append(reward) state = next_state if done: - print('Episode:', i_episode, ' Reward:', ep_reward) + print('Episode:', i_ep, ' Reward:', ep_reward) break - if i_episode > 0 and i_episode % cfg.batch_size == 0: + if i_ep > 0 and i_ep % cfg.batch_size == 0: agent.update(reward_pool,state_pool,action_pool) state_pool = [] # 每个episode的state action_pool = [] @@ -95,7 +94,7 @@ def eval(cfg,env,agent): print(f'Env:{cfg.env}, Algorithm:{cfg.algo}, Device:{cfg.device}') rewards = [] ma_rewards = [] - for i_episode in range(cfg.eval_eps): + for i_ep in range(cfg.eval_eps): state = env.reset() ep_reward = 0 for _ in count(): @@ -106,7 +105,7 @@ def eval(cfg,env,agent): reward = 0 state = next_state if done: - print('Episode:', i_episode, ' Reward:', ep_reward) + print('Episode:', i_ep, ' Reward:', ep_reward) break rewards.append(ep_reward) if ma_rewards: @@ -116,6 +115,7 @@ def eval(cfg,env,agent): ma_rewards.append(ep_reward) print('complete evaling!') return rewards, ma_rewards + if __name__ == "__main__": cfg = PGConfig() diff --git a/codes/README.md b/codes/README.md index e7b9e6f..2c421ae 100644 --- a/codes/README.md +++ b/codes/README.md @@ -18,14 +18,14 @@ ## 运行环境 -python 3.7、pytorch 1.6.0-1.7.1、gym 0.17.0-0.19.0 +python 3.7、pytorch 1.6.0-1.8.1、gym 0.17.0-0.19.0 ## 使用说明 运行带有```train```的py文件或ipynb文件进行训练,如果前面带有```task```如```task0_train.py```,表示对task0任务训练, 类似的带有```eval```即为测试。 -## 算法进度 +## 内容导航 | 算法名称 | 相关论文材料 | 环境 | 备注 | | :--------------------------------------: | :----------------------------------------------------------: | ----------------------------------------- | :--------------------------------: | diff --git a/codes/common/model.py b/codes/common/model.py index 9800dbf..be03368 100644 --- a/codes/common/model.py +++ b/codes/common/model.py @@ -15,15 +15,15 @@ import torch.nn.functional as F from torch.distributions import Categorical class MLP(nn.Module): - def __init__(self, input_dim,output_dim,hidden_dim=128): + def __init__(self, n_states,n_actions,hidden_dim=128): """ 初始化q网络,为全连接网络 - input_dim: 输入的特征数即环境的状态数 - output_dim: 输出的动作维度 + n_states: 输入的特征数即环境的状态数 + n_actions: 输出的动作维度 """ super(MLP, self).__init__() - self.fc1 = nn.Linear(input_dim, hidden_dim) # 输入层 + self.fc1 = nn.Linear(n_states, hidden_dim) # 输入层 self.fc2 = nn.Linear(hidden_dim,hidden_dim) # 隐藏层 - self.fc3 = nn.Linear(hidden_dim, output_dim) # 输出层 + self.fc3 = nn.Linear(hidden_dim, n_actions) # 输出层 def forward(self, x): # 各层对应的激活函数 @@ -32,10 +32,10 @@ class MLP(nn.Module): return self.fc3(x) class Critic(nn.Module): - def __init__(self, n_obs, output_dim, hidden_size, init_w=3e-3): + def __init__(self, n_obs, n_actions, hidden_size, init_w=3e-3): super(Critic, self).__init__() - self.linear1 = nn.Linear(n_obs + output_dim, hidden_size) + self.linear1 = nn.Linear(n_obs + n_actions, hidden_size) self.linear2 = nn.Linear(hidden_size, hidden_size) self.linear3 = nn.Linear(hidden_size, 1) # 随机初始化为较小的值 @@ -51,11 +51,11 @@ class Critic(nn.Module): return x class Actor(nn.Module): - def __init__(self, n_obs, output_dim, hidden_size, init_w=3e-3): + def __init__(self, n_obs, n_actions, hidden_size, init_w=3e-3): super(Actor, self).__init__() self.linear1 = nn.Linear(n_obs, hidden_size) self.linear2 = nn.Linear(hidden_size, hidden_size) - self.linear3 = nn.Linear(hidden_size, output_dim) + self.linear3 = nn.Linear(hidden_size, n_actions) self.linear3.weight.data.uniform_(-init_w, init_w) self.linear3.bias.data.uniform_(-init_w, init_w) @@ -67,18 +67,18 @@ class Actor(nn.Module): return x class ActorCritic(nn.Module): - def __init__(self, input_dim, output_dim, hidden_dim=256): + def __init__(self, n_states, n_actions, hidden_dim=256): super(ActorCritic, self).__init__() self.critic = nn.Sequential( - nn.Linear(input_dim, hidden_dim), + nn.Linear(n_states, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, 1) ) self.actor = nn.Sequential( - nn.Linear(input_dim, hidden_dim), + nn.Linear(n_states, hidden_dim), nn.ReLU(), - nn.Linear(hidden_dim, output_dim), + nn.Linear(hidden_dim, n_actions), nn.Softmax(dim=1), ) diff --git a/codes/common/plot.py b/codes/common/plot.py index 6707ff8..d14b8d4 100644 --- a/codes/common/plot.py +++ b/codes/common/plot.py @@ -11,36 +11,52 @@ 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) # 系统字体路径,此处是mac的 -def plot_rewards(rewards,ma_rewards,tag="train",env='CartPole-v0',algo = "DQN",save=True,path='./'): - sns.set() - plt.title("average learning curve of {} for {}".format(algo,env)) +# 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() + +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.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)) + if plot_cfg.save: + plt.savefig(plot_cfg.result_path+"{}_rewards_curve".format(tag)) plt.show() - -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() +# 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() + plt.figure() plt.title("loss curve of {}".format(algo)) plt.xlabel('epsiodes') plt.plot(losses,label='rewards') diff --git a/codes/envs/README.md b/codes/envs/README.md new file mode 100644 index 0000000..e93fba0 --- /dev/null +++ b/codes/envs/README.md @@ -0,0 +1,6 @@ +## 环境汇总 + +[OpenAI Gym](./gym_info.md) +[MuJoCo](./mujoco_info.md) + +