Files
easy-rl/codes/DQN-series/DQN/task0_train.ipynb
johnjim0816 442e307b01 update codes
2021-11-17 14:36:51 +08:00

380 lines
68 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"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": [
"<Figure size 432x288 with 1 Axes>"
]
},
"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": [
"<Figure size 432x288 with 1 Axes>"
]
},
"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
}