深度强化学习-Double DQN算法原理与代码

引言

1 DDQN算法简介

2 DDQN算法原理

3 DDQN算法伪代码

4 仿真验证


引言

Double Deep Q Network(DDQN)是对DQN算法的改进,有效提升了算法的性能,本文就带领大家了解一下这个算法,Double Q-learning算法的论文和代码链接见下方。

论文:Deep Reinforcement Learning with Double Q-Learning| Proceedings of the AAAI Conference on Artificial Intelligence

代码:https://github.com/indigoLovee/DDQN

喜欢的话请点个star~。

1 DDQN算法简介

Q-learning算法采用来更新动作价值,这样会导致“最大化偏差"(maximization bias),使得估计的动作价值偏大。

我们来看一个最大化偏差的例子。上图为一个回合制任务,Markov决策过程的状态空间为,回合开始时总是处在状态,可以选择的动作空间。如果选择动作,则可以到达状态,该步奖励为+0;如果选择动作,则可以达到终止状态并获得奖励+1。从状态​出发,有很多可选的动作(例如有1000个可选的动作),但是这些动作都指向终止状态,并且奖励都服从均值为0、方差为100的正态分布。从理论上说,这个例子的最优价值函数为:,最优策略应当是。但是,如果采用Q-learning算法,在中间过程中会走一些弯路:在学习过程中,从出发的某些动作会采样到比较大的奖励值,从而导致会比较大,使得在时更倾向于选择。这样的错误需要大量的数据才能纠正。(由于公式中的下标无法用中文表示,所以都用英文来代替,其中start表示开始,middle表示中间,end表示终止,goMiddle表示去中间,goEnd表示去终止)

为了解决这个问题,双重Q学习(Double Q-learning)算法使用两个独立的动作价值估计值,用来代替Q-learning中的。由于是相互独立的估计,所以,其中,这样就消除了偏差。在双重学习的过程中,都需要逐渐更新。

对于DQN算法也有同样的结论,将Double Q-learning应用于DQN,得到了双重深度Q网络(Double Deep Q Network, Double DQN)。

2 DDQN算法原理

DDQN算法相较于DQN算法,其主要针对后者的过估计问题,改变了目标值的计算方法,其他地方与DQN算法完全一致。因此,如果已经知道了DQN算法的所有理论,那么DDQN算法就非常简单啦,这篇博文的理论部分主要讲解DDQN算法的改进部分。如果有小伙伴对DQN算法不太了解,可以参考我的这篇blog:深度强化学习-DQN算法原理与代码,里面详细介绍了DQN算法的相关理论并进行了仿真验证。

由于Double Q-learning要求构建两个动作价值函数,一个用于估计动作,另外一个用于估计该动作的价值。但是考虑到DQN算法中已经有了评估网络和目标网络两个网络,所以DDQN算法在估计回报时只需要用评估网络确定动作,用目标网络确定动作价值即可,不要另外构建新的网络。因此,只需要将DQN算法中计算目标值的方法:

更换为:

其中表示目标值,表示折扣系数,表示评估网络参数,表示目标网络参数。

这样就得到了带经验回放的DDQN算法,下图是原论文中的DDQN算法与DQN算法的对比结果。

其中蓝色曲线为DDQN算法,红色曲线为DQN算法,通过Value estimates曲线可以明显看出DDQN算法的值估计较DQN算法低,有效解决了过估计问题;另外通过Score累计奖励曲线可以看出,DDQN算法的累计奖励更高,拥有更好的决策能力。

3 DDQN算法伪代码

4 仿真验证

经验回放采用集中式均匀回放,代码如下(脚本buffer.py):

import numpy as npclass ReplayBuffer:def __init__(self, state_dim, action_dim, max_size, batch_size):self.mem_size = max_sizeself.batch_size = batch_sizeself.mem_cnt = 0self.state_memory = np.zeros((self.mem_size, state_dim))self.action_memory = np.zeros((self.mem_size, ))self.reward_memory = np.zeros((self.mem_size, ))self.next_state_memory = np.zeros((self.mem_size, state_dim))self.terminal_memory = np.zeros((self.mem_size, ), dtype=np.bool)def store_transition(self, state, action, reward, state_, done):mem_idx = self.mem_cnt % self.mem_sizeself.state_memory[mem_idx] = stateself.action_memory[mem_idx] = actionself.reward_memory[mem_idx] = rewardself.next_state_memory[mem_idx] = state_self.terminal_memory[mem_idx] = doneself.mem_cnt += 1def sample_buffer(self):mem_len = min(self.mem_size, self.mem_cnt)batch = np.random.choice(mem_len, self.batch_size, replace=False)states = self.state_memory[batch]actions = self.action_memory[batch]rewards = self.reward_memory[batch]states_ = self.next_state_memory[batch]terminals = self.terminal_memory[batch]return states, actions, rewards, states_, terminalsdef ready(self):return self.mem_cnt > self.batch_size

目标网络采用软更新方式更新网络权值,DDQN算法的实现代码如下(脚本DDQN.py):

import torch as T
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
import numpy as np
from buffer import ReplayBufferdevice = T.device("cuda:0" if T.cuda.is_available() else "cpu")class DeepQNetwork(nn.Module):def __init__(self, alpha, state_dim, action_dim, fc1_dim, fc2_dim):super(DeepQNetwork, self).__init__()self.fc1 = nn.Linear(state_dim, fc1_dim)self.fc2 = nn.Linear(fc1_dim, fc2_dim)self.q = nn.Linear(fc2_dim, action_dim)self.optimizer = optim.Adam(self.parameters(), lr=alpha)self.to(device)def forward(self, state):x = T.relu(self.fc1(state))x = T.relu(self.fc2(x))q = self.q(x)return qdef save_checkpoint(self, checkpoint_file):T.save(self.state_dict(), checkpoint_file, _use_new_zipfile_serialization=False)def load_checkpoint(self, checkpoint_file):self.load_state_dict(T.load(checkpoint_file))class DDQN:def __init__(self, alpha, state_dim, action_dim, fc1_dim, fc2_dim, ckpt_dir,gamma=0.99, tau=0.005, epsilon=1.0, eps_end=0.01, eps_dec=5e-7,max_size=1000000, batch_size=256):self.gamma = gammaself.tau = tauself.epsilon = epsilonself.eps_min = eps_endself.eps_dec = eps_decself.batch_size = batch_sizeself.checkpoint_dir = ckpt_dirself.action_space = [i for i in range(action_dim)]self.q_eval = DeepQNetwork(alpha=alpha, state_dim=state_dim, action_dim=action_dim,fc1_dim=fc1_dim, fc2_dim=fc2_dim)self.q_target = DeepQNetwork(alpha=alpha, state_dim=state_dim, action_dim=action_dim,fc1_dim=fc1_dim, fc2_dim=fc2_dim)self.memory = ReplayBuffer(state_dim=state_dim, action_dim=action_dim,max_size=max_size, batch_size=batch_size)self.update_network_parameters(tau=1.0)def update_network_parameters(self, tau=None):if tau is None:tau = self.taufor q_target_params, q_eval_params in zip(self.q_target.parameters(), self.q_eval.parameters()):q_target_params.data.copy_(tau * q_eval_params + (1 - tau) * q_target_params)def remember(self, state, action, reward, state_, done):self.memory.store_transition(state, action, reward, state_, done)def choose_action(self, observation, isTrain=True):state = T.tensor([observation], dtype=T.float).to(device)actions = self.q_eval.forward(state)action = T.argmax(actions).item()if (np.random.random() < self.epsilon) and isTrain:action = np.random.choice(self.action_space)return actiondef decrement_epsilon(self):self.epsilon = self.epsilon - self.eps_dec \if self.epsilon > self.eps_min else self.eps_mindef learn(self):if not self.memory.ready():returnstates, actions, rewards, next_states, terminals = self.memory.sample_buffer()batch_idx = np.arange(self.batch_size)states_tensor = T.tensor(states, dtype=T.float).to(device)rewards_tensor = T.tensor(rewards, dtype=T.float).to(device)next_states_tensor = T.tensor(next_states, dtype=T.float).to(device)terminals_tensor = T.tensor(terminals).to(device)with T.no_grad():q_ = self.q_eval.forward(next_states_tensor)next_actions = T.argmax(q_, dim=-1)q_ = self.q_target.forward(next_states_tensor)q_[terminals_tensor] = 0.0target = rewards_tensor + self.gamma * q_[batch_idx, next_actions]q = self.q_eval.forward(states_tensor)[batch_idx, actions]loss = F.mse_loss(q, target.detach())self.q_eval.optimizer.zero_grad()loss.backward()self.q_eval.optimizer.step()self.update_network_parameters()self.decrement_epsilon()def save_models(self, episode):self.q_eval.save_checkpoint(self.checkpoint_dir + 'Q_eval/DDQN_q_eval_{}.pth'.format(episode))print('Saving Q_eval network successfully!')self.q_target.save_checkpoint(self.checkpoint_dir + 'Q_target/DDQN_Q_target_{}.pth'.format(episode))print('Saving Q_target network successfully!')def load_models(self, episode):self.q_eval.load_checkpoint(self.checkpoint_dir + 'Q_eval/DDQN_q_eval_{}.pth'.format(episode))print('Loading Q_eval network successfully!')self.q_target.load_checkpoint(self.checkpoint_dir + 'Q_target/DDQN_Q_target_{}.pth'.format(episode))print('Loading Q_target network successfully!')

算法仿真环境为gym库中的LunarLander-v2,因此需要先配置好gym库。进入Anaconda3中对应的Python环境中,执行下面的指令

pip install gym

但是,这样安装的gym库只包括少量的内置环境,如算法环境、简单文字游戏和经典控制环境,无法使用LunarLander-v2。因此还需要安装一些其他依赖项,具体可以参考我的这篇blog:AttributeError: module ‘gym.envs.box2d‘ has no attribute ‘LunarLander‘ 解决办法

让智能体在环境中训练500轮,训练代码如下(脚本train.py):

import gym
import numpy as np
import argparse
from DDQN import DDQN
from utils import plot_learning_curve, create_directoryparser = argparse.ArgumentParser()
parser.add_argument('--max_episodes', type=int, default=500)
parser.add_argument('--ckpt_dir', type=str, default='./checkpoints/DDQN/')
parser.add_argument('--reward_path', type=str, default='./output_images/avg_reward.png')
parser.add_argument('--epsilon_path', type=str, default='./output_images/epsilon.png')args = parser.parse_args()def main():env = gym.make('LunarLander-v2')agent = DDQN(alpha=0.0003, state_dim=env.observation_space.shape[0], action_dim=env.action_space.n,fc1_dim=256, fc2_dim=256, ckpt_dir=args.ckpt_dir, gamma=0.99, tau=0.005, epsilon=1.0,eps_end=0.05, eps_dec=5e-4, max_size=1000000, batch_size=256)create_directory(args.ckpt_dir, sub_dirs=['Q_eval', 'Q_target'])total_rewards, avg_rewards, eps_history = [], [], []for episode in range(args.max_episodes):total_reward = 0done = Falseobservation = env.reset()while not done:action = agent.choose_action(observation, isTrain=True)observation_, reward, done, info = env.step(action)agent.remember(observation, action, reward, observation_, done)agent.learn()total_reward += rewardobservation = observation_total_rewards.append(total_reward)avg_reward = np.mean(total_rewards[-100:])avg_rewards.append(avg_reward)eps_history.append(agent.epsilon)print('EP:{} reward:{} avg_reward:{} epsilon:{}'.format(episode + 1, total_reward, avg_reward, agent.epsilon))if (episode + 1) % 50 == 0:agent.save_models(episode + 1)episodes = [i for i in range(args.max_episodes)]plot_learning_curve(episodes, avg_rewards, 'Reward', 'reward', args.reward_path)plot_learning_curve(episodes, eps_history, 'Epsilon', 'epsilon', args.epsilon_path)if __name__ == '__main__':main()

训练时还会用到画图函数和创建文件夹函数,它们均放置在utils.py脚本中,具体代码如下:

import os
import matplotlib.pyplot as pltdef plot_learning_curve(episodes, records, title, ylabel, figure_file):plt.figure()plt.plot(episodes, records, linestyle='-', color='r')plt.title(title)plt.xlabel('episode')plt.ylabel(ylabel)plt.show()plt.savefig(figure_file)def create_directory(path: str, sub_dirs: list):for sub_dir in sub_dirs:if os.path.exists(path + sub_dir):print(path + sub_dir + ' is already exist!')else:os.makedirs(path + sub_dir, exist_ok=True)print(path + sub_dir + ' create successfully!')

仿真结果如下图所示:

通过平均奖励曲线可以看出,大概迭代到300步左右时DDQN算法趋于收敛。下图为DQN算法的平均奖励曲线:

通过对比可以明显看出DDQN算法较DQN算法,具有更快的收敛速度,算法的性能得到有效提升。

深度强化学习-Double DQN算法原理与代码相关推荐

  1. 深度强化学习Soft-Actor Critic算法高性能Pytorch代码(改写自spinningup,低环境依赖,低阅读障碍)

    写在前面 DRL各种算法在github上各处都是,例如莫凡的DRL代码.ElegantDRL(易读性NO.1) 很多代码不是原算法的最佳实现,在具体实现细节上也存在差异,不建议直接用在科研上. 这篇博 ...

  2. 深度强化学习-DQN算法原理与代码

    DQN算法是DeepMind团队提出的一种深度强化学习算法,在许多电动游戏中达到人类玩家甚至超越人类玩家的水准,本文就带领大家了解一下这个算法,论文和代码的链接见下方. 论文:Human-level ...

  3. 【深度强化学习】DQN训练超级玛丽闯关

    上一期 MyEncyclopedia公众号文章 通过代码学Sutton强化学习:从Q-Learning 演化到 DQN,我们从原理上讲解了DQN算法,这一期,让我们通过代码来实现DQN 在任天堂经典的 ...

  4. 14. 深度强化学习(DQN)

    深度Q网络 14.1 用深度强化学习玩Atari 14.1.1 介绍 14.1.2 背景 14.1.3 相关工作 14.1.4 深度强化学习 1. 预处理和模型架构 14.1.5 实验 1. 训练和稳 ...

  5. 深度学习stride_深度强化学习成名作——DQN

    前言:其实很早之前就想开始写写深度强化学习(Deep reinforcement learning)了,但是一年前DQN没调出来,没好意思写哈哈,最近呢无意中把打砖块游戏Breakout训练到平均分接 ...

  6. dqn在训练过程中loss越来越大_深度强化学习——从DQN到DDPG

    想了解更多好玩的人工智能应用,请关注公众号"机器AI学习 数据AI挖掘","智能应用"菜单中包括:颜值检测.植物花卉识别.文字识别.人脸美妆等有趣的智能应用.. ...

  7. 【深度强化学习】DRL算法实现pytorch

    DRL Algorithms DQN (deep Q network) Policiy_Gradient 策略梯度是强化学习的一类方法,大致的原理是使用神经网络构造一个策略网络,输入是状态,输出为动作 ...

  8. 基于强化学习的五子棋算法设计-python代码完整实现

    目录 1  课程设计目的 2  设计任务与要求 3  设计原理 3.1  强化学习 3.2  蒙特卡洛树搜索 4  模型介绍 4.1  模拟 4.2  走子 4.3  神经网络 5  仿真过程与结果 ...

  9. 【深度强化学习】DDPG算法

    1 DDPG简介 确定性策略梯度(Deterministic Policy Gradient,DPG):确定性策略是和随机策略相对而言的.作为随机策略,在同一个状态处,采用的动作是基于一个概率分布,即 ...

最新文章

  1. MySQL mysqldump命令
  2. DotNetCore跨平台~xUnit和测试报告
  3. OPENCV背景细分background segmentation的实例(附完整代码)
  4. 【设计模式之美】<Reading Notes>抽象类与接口
  5. STM32 - 定时器的设定 - 基础 - 05 - Arbitrary waveform generation using timer DMAburst feature - 任意波形的序列产生
  6. updating error reports database解决方案
  7. 用机器学习拯救“智障”聊天机器人,谷歌开放分析平台Chatbase
  8. mysql neatbean_使用MySQL的NetBeans中的SQL语法错误
  9. 聊聊mac系统的 secoclient和iTerm2
  10. MySQL (十四) 两阶段提交
  11. 惯用过程模型_惯用代码
  12. Maven settings.xml 文件详解
  13. QT+OSG/osgEarth编译之四十三:jasper+Qt编译(一套代码、一套框架,跨平台编译,版本:basisjasper-2.0.12)
  14. 各省市县-人口与区域面积(1949-2019年)
  15. 什么是用计算机的主存,计算机的主存储器是指什么
  16. windows平台下的oracle ORA-01031的解决方法
  17. Xshell安装Docker并安装mysql5.7
  18. Numpy和spicy函数复习1
  19. html event 传递数据,Vue 事件的$event参数=事件的值案例
  20. 搭建企业知识库的意义

热门文章

  1. 计算机英语课件,计算机英语 课件
  2. Postman之接口测试
  3. The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 3B4
  4. python 股票分析api,2020-04-21 用python3 按周分析新浪接口股票数据(篇一:下载、存储篇)...
  5. matlab 移位操作基础
  6. 苹果 iOS/iPadOS 14.7.1正式版发布
  7. wsus服务器推送指定的补丁,【补丁更新】组策略实现WSUS客户端自动更新
  8. android 4k拍摄,荣耀30S视频体验:超清4K拍摄,新一代Vlog神器
  9. 镜像画笔-第13届蓝桥杯Scratch国赛真题第2题
  10. 架构师成长系列 - 能力认知(2)