dqn 订阅
DQN,网络流行语,来自日语发音的缩写“ドキュン”(读作dokyun,缩写为dqn),贬义词,指横蛮无理、爱用暴力甚至反社会的人,或指缺乏常识、学历低下的人,如奇葩不良少年等。 展开全文
DQN,网络流行语,来自日语发音的缩写“ドキュン”(读作dokyun,缩写为dqn),贬义词,指横蛮无理、爱用暴力甚至反社会的人,或指缺乏常识、学历低下的人,如奇葩不良少年等。
信息
性    质
网络流行语
中文名
DQN
DQN词语来源
起源于朝日电视台帮助委托人寻人的节目《目撃!ドキュン》,但登场的“不良少年”过多。后来日本网民用dqn嘲笑低学历者。2ch网站上的变体为“ドキュソ”,谐音日语“大便”,更加贬义。 [1] 
收起全文
精华内容
下载资源
问答
  • DQN

    2020-12-28 19:58:05
    - [x] DQN - [x] Double Q Learning - [x] Dueling DQN - [x] Prioritized Replay - [x] Noisy Nets (Noisy DQN) - [x] Distributional DQN - [ ] Adapt multi step bootstrapped return from A3C <p>Refer #6 </p>...
  • dqn

    千次阅读 2019-05-28 15:58:47
    前面提到DQN中的CNN作用是对在高维且连续状态下的Q-Table做函数拟合,而对于函数优化问题,监督学习的一般方法是先确定Loss Function,然后求梯度,使用随机梯度下降等方法更新参数。DQN则基于Q-Learning来确定Loss ...
    1. DL与RL结合的问题
      DL需要大量带标签的样本进行监督学习;RL只有reward返回值,而且伴随着噪声,延迟(过了几十毫秒才返回),稀疏(很多State的reward是0)等问题;
      DL的样本独立;RL前后state状态相关;
      DL目标分布固定;RL的分布一直变化,比如你玩一个游戏,一个关卡和下一个关卡的状态分布是不同的,所以训练好了前一个关卡,下一个关卡又要重新训练;
      过往的研究表明,使用非线性网络表示值函数时出现不稳定等问题。
    2. DQN解决问题方法
      通过Q-Learning使用reward来构造标签(对应问题1)
      通过experience replay(经验池)的方法来解决相关性及非静态分布问题(对应问题2、3)
      使用一个CNN(MainNet)产生当前Q值,使用另外一个CNN(Target)产生Target Q值(对应问题4)
      1.构造标签
      前面提到DQN中的CNN作用是对在高维且连续状态下的Q-Table做函数拟合,而对于函数优化问题,监督学习的一般方法是先确定Loss Function,然后求梯度,使用随机梯度下降等方法更新参数。DQN则基于Q-Learning来确定Loss Function。

    Q-Learning
    有关RL的基础知识不再啰嗦,直接看Q-Learning的更新公式:
    Q∗(s,a)=Q(s,a)+α(r+γmaxa′Q(s′,a′)−Q(s,a))

    而DQN的Loss Function为
    L(θ)=E[(TargetQ−Q(s,a;θ))2]

    其中 θ 是网络参数,目标为
    TargetQ=r+γmaxa′Q(s′,a′;θ)
    显然Loss Function是基于Q-Learning更新公式的第二项确定的,两个公式意义相同,都是使当前的Q值逼近Target Q值。

    接下来,求 L(θ) 关于 θ 的梯度,使用SGD等方法更新网络参数 θ。

    2.经验池(experience replay)
    经验池的功能主要是解决相关性及非静态分布问题。具体做法是把每个时间步agent与环境交互得到的转移样本 (st,at,rt,st+1) 储存到回放记忆单元,要训练时就随机拿出一些(minibatch)来训练。(其实就是将游戏的过程打成碎片存储,训练时随机抽取就避免了相关性问题)

    3.目标网络
    在Nature 2015版本的DQN中提出了这个改进,使用另一个网络(这里称为TargetNet)产生Target Q值。具体地,Q(s,a;θi) 表示当前网络MainNet的输出,用来评估当前状态动作对的值函数;Q(s,a;θ−i) 表示TargetNet的输出,代入上面求 TargetQ 值的公式中得到目标Q值。根据上面的Loss Function更新MainNet的参数,每经过N轮迭代,将MainNet的参数复制给TargetNet。

    引入TargetNet后,再一段时间里目标Q值使保持不变的,一定程度降低了当前Q值和目标Q值的相关性,提高了算法稳定性。

    MainNet TargetNet 是2个神经网络,TargetNet相当于某段时间的更新锚点,以此锚点为基准更新MainNet。更新一定次数后,将MainNet复制到TargetNet

    可以这样理解:某只青蛙从-y轴跳到0轴,如果没有TargetNet这个分身作为锚点,可能跳到+y轴,再跳到更远的-y轴,发生振荡行为。如果有了TargetNet,可以保证不跳到更远的-y轴,离0轴越来越近。

    展开全文
  • 用Tensorflow基于Deep Q Learning DQN 玩Flappy Bird

    万次阅读 多人点赞 2016-03-22 00:11:57
    前言2013年DeepMind 在NIPS上发表Playing Atari with Deep Reinforcement Learning 一文,提出了DQN(Deep Q Network)算法,实现端到端学习玩Atari游戏,即只有像素输入,看着屏幕玩游戏。Deep Mind就凭借这个应用...

    前言

    2013年DeepMind 在NIPS上发表Playing Atari with Deep Reinforcement Learning 一文,提出了DQN(Deep Q Network)算法,实现端到端学习玩Atari游戏,即只有像素输入,看着屏幕玩游戏。Deep Mind就凭借这个应用以6亿美元被Google收购。由于DQN的开源,在github上涌现了大量各种版本的DQN程序。但大多是复现Atari的游戏,代码量很大,也不好理解。

    Flappy Bird是个极其简单又困难的游戏,风靡一时。在很早之前,就有人使用Q-Learning 算法来实现完Flappy Bird。http://sarvagyavaish.github.io/FlappyBirdRL/
    但是这个的实现是通过获取小鸟的具体位置信息来实现的。

    能否使用DQN来实现通过屏幕学习玩Flappy Bird是一个有意思的挑战。(话说本人和朋友在去年年底也考虑了这个idea,但当时由于不知道如何截取游戏屏幕只能使用具体位置来学习,不过其实也成功了)

    最近,github上有人放出使用DQN玩Flappy Bird的代码,https://github.com/yenchenlin1994/DeepLearningFlappyBird【1】
    该repo通过结合之前的repo成功实现了这个想法。这个repo对整个实现过程进行了较详细的分析,但是由于其DQN算法的代码基本采用别人的repo,代码较为混乱,不易理解。
    这里写图片描述
    为此,本人改写了一个版本https://github.com/songrotek/DRL-FlappyBird

    对DQN代码进行了重新改写。本质上对其做了类的封装,从而使代码更具通用性。可以方便移植到其他应用。

    当然,本文的目的是借Flappy Bird DQN这个代码来详细分析一下DQN算法极其使用。

    DQN 伪代码

    这个是NIPS13版本的伪代码:

    Initialize replay memory D to size N
    Initialize action-value function Q with random weights
    for episode = 1, M do
        Initialize state s_1
        for t = 1, T do
            With probability ϵ select random action a_t
            otherwise select a_t=max_a  Q($s_t$,a; $θ_i$)
            Execute action a_t in emulator and observe r_t and s_(t+1)
            Store transition (s_t,a_t,r_t,s_(t+1)) in D
            Sample a minibatch of transitions (s_j,a_j,r_j,s_(j+1)) from D
            Set y_j:=
                r_j for terminal s_(j+1)
                r_j+γ*max_(a^' )  Q(s_(j+1),a'; θ_i) for non-terminal s_(j+1)
            Perform a gradient step on (y_j-Q(s_j,a_j; θ_i))^2 with respect to θ
        end for
    end for

    基本的分析详见Paper Reading 1 - Playing Atari with Deep Reinforcement Learning
    基础知识详见Deep Reinforcement Learning 基础知识(DQN方面)

    本文主要从代码实现的角度来分析如何编写Flappy Bird DQN的代码

    编写FlappyBirdDQN.py

    首先,FlappyBird的游戏已经编写好,是现成的。提供了很简单的接口:

    nextObservation,reward,terminal = game.frame_step(action)

    即输入动作,输出执行完动作的屏幕截图,得到的反馈reward,以及游戏是否结束。

    那么,现在先把DQN想象为一个大脑,这里我们也用BrainDQN类来表示,这个类只需获取感知信息也就是上面说的观察(截图),反馈以及是否结束,然后输出动作即可。

    完美的代码封装应该是这样。具体DQN里面如何存储。如何训练是外部不关心的。
    因此,我们的FlappyBirdDQN代码只有如下这么短:

    # -------------------------
    # Project: Deep Q-Learning on Flappy Bird
    # Author: Flood Sung
    # Date: 2016.3.21
    # -------------------------
    
    import cv2
    import sys
    sys.path.append("game/")
    import wrapped_flappy_bird as game
    from BrainDQN import BrainDQN
    import numpy as np
    
    # preprocess raw image to 80*80 gray image
    def preprocess(observation):
        observation = cv2.cvtColor(cv2.resize(observation, (80, 80)), cv2.COLOR_BGR2GRAY)
        ret, observation = cv2.threshold(observation,1,255,cv2.THRESH_BINARY)
        return np.reshape(observation,(80,80,1))
    
    def playFlappyBird():
        # Step 1: init BrainDQN
        brain = BrainDQN()
        # Step 2: init Flappy Bird Game
        flappyBird = game.GameState()
        # Step 3: play game
        # Step 3.1: obtain init state
        action0 = np.array([1,0])  # do nothing
        observation0, reward0, terminal = flappyBird.frame_step(action0)
        observation0 = cv2.cvtColor(cv2.resize(observation0, (80, 80)), cv2.COLOR_BGR2GRAY)
        ret, observation0 = cv2.threshold(observation0,1,255,cv2.THRESH_BINARY)
        brain.setInitState(observation0)
    
        # Step 3.2: run the game
        while 1!= 0:
            action = brain.getAction()
            nextObservation,reward,terminal = flappyBird.frame_step(action)
            nextObservation = preprocess(nextObservation)
            brain.setPerception(nextObservation,action,reward,terminal)
    
    def main():
        playFlappyBird()
    
    if __name__ == '__main__':
        main()

    核心部分就在while循环里面,由于要讲图像转换为80x80的灰度图,因此,加了一个preprocess预处理函数。

    这里,显然只有有游戏引擎,换一个游戏是一样的写法,非常方便。

    接下来就是编写BrainDQN.py 我们的游戏大脑

    编写BrainDQN

    基本架构:

    class BrainDQN:
        def __init__(self):
            # init replay memory
            self.replayMemory = deque()
            # init Q network
            self.createQNetwork()
        def createQNetwork(self):
    
        def trainQNetwork(self):
    
        def setPerception(self,nextObservation,action,reward,terminal):
        def getAction(self):
        def setInitState(self,observation):

    基本的架构也就只需要上面这几个函数,其他的都是多余了,接下来就是编写每一部分的代码。

    CNN代码

    也就是createQNetwork部分,这里采用如下图的结构(转自【1】):
    这里写图片描述

    这里就不讲解整个流程了。主要是针对具体的输入类型和输出设计卷积和全连接层。

    代码如下:

        def createQNetwork(self):
            # network weights
            W_conv1 = self.weight_variable([8,8,4,32])
            b_conv1 = self.bias_variable([32])
    
            W_conv2 = self.weight_variable([4,4,32,64])
            b_conv2 = self.bias_variable([64])
    
            W_conv3 = self.weight_variable([3,3,64,64])
            b_conv3 = self.bias_variable([64])
    
            W_fc1 = self.weight_variable([1600,512])
            b_fc1 = self.bias_variable([512])
    
            W_fc2 = self.weight_variable([512,self.ACTION])
            b_fc2 = self.bias_variable([self.ACTION])
    
            # input layer
    
            self.stateInput = tf.placeholder("float",[None,80,80,4])
    
            # hidden layers
            h_conv1 = tf.nn.relu(self.conv2d(self.stateInput,W_conv1,4) + b_conv1)
            h_pool1 = self.max_pool_2x2(h_conv1)
    
            h_conv2 = tf.nn.relu(self.conv2d(h_pool1,W_conv2,2) + b_conv2)
    
            h_conv3 = tf.nn.relu(self.conv2d(h_conv2,W_conv3,1) + b_conv3)
    
            h_conv3_flat = tf.reshape(h_conv3,[-1,1600])
            h_fc1 = tf.nn.relu(tf.matmul(h_conv3_flat,W_fc1) + b_fc1)
    
            # Q Value layer
            self.QValue = tf.matmul(h_fc1,W_fc2) + b_fc2
    
            self.actionInput = tf.placeholder("float",[None,self.ACTION])
            self.yInput = tf.placeholder("float", [None]) 
            Q_action = tf.reduce_sum(tf.mul(self.QValue, self.actionInput), reduction_indices = 1)
            self.cost = tf.reduce_mean(tf.square(self.yInput - Q_action))
            self.trainStep = tf.train.AdamOptimizer(1e-6).minimize(self.cost)

    记住输出是Q值,关键要计算出cost,里面关键是计算Q_action的值,即该state和action下的Q值。由于actionInput是one hot vector的形式,因此tf.mul(self.QValue, self.actionInput)正好就是该action下的Q值。

    training 部分。

    这部分是代码的关键部分,主要是要计算y值,也就是target Q值。

        def trainQNetwork(self):
            # Step 1: obtain random minibatch from replay memory
            minibatch = random.sample(self.replayMemory,self.BATCH_SIZE)
            state_batch = [data[0] for data in minibatch]
            action_batch = [data[1] for data in minibatch]
            reward_batch = [data[2] for data in minibatch]
            nextState_batch = [data[3] for data in minibatch]
    
            # Step 2: calculate y 
            y_batch = []
            QValue_batch = self.QValue.eval(feed_dict={self.stateInput:nextState_batch})
            for i in range(0,self.BATCH_SIZE):
                terminal = minibatch[i][4]
                if terminal:
                    y_batch.append(reward_batch[i])
                else:
                    y_batch.append(reward_batch[i] + GAMMA * np.max(QValue_batch[i]))
    
            self.trainStep.run(feed_dict={
                self.yInput : y_batch,
                self.actionInput : action_batch,
                self.stateInput : state_batch
                })

    其他部分

    其他部分就比较容易了,这里直接贴出完整的代码:

    # -----------------------------
    # File: Deep Q-Learning Algorithm
    # Author: Flood Sung
    # Date: 2016.3.21
    # -----------------------------
    
    import tensorflow as tf 
    import numpy as np 
    import random
    from collections import deque 
    
    class BrainDQN:
    
        # Hyper Parameters:
        ACTION = 2
        FRAME_PER_ACTION = 1
        GAMMA = 0.99 # decay rate of past observations
        OBSERVE = 100000. # timesteps to observe before training
        EXPLORE = 150000. # frames over which to anneal epsilon
        FINAL_EPSILON = 0.0 # final value of epsilon
        INITIAL_EPSILON = 0.0 # starting value of epsilon
        REPLAY_MEMORY = 50000 # number of previous transitions to remember
        BATCH_SIZE = 32 # size of minibatch
    
        def __init__(self):
            # init replay memory
            self.replayMemory = deque()
            # init Q network
            self.createQNetwork()
            # init some parameters
            self.timeStep = 0
            self.epsilon = self.INITIAL_EPSILON
    
        def createQNetwork(self):
            # network weights
            W_conv1 = self.weight_variable([8,8,4,32])
            b_conv1 = self.bias_variable([32])
    
            W_conv2 = self.weight_variable([4,4,32,64])
            b_conv2 = self.bias_variable([64])
    
            W_conv3 = self.weight_variable([3,3,64,64])
            b_conv3 = self.bias_variable([64])
    
            W_fc1 = self.weight_variable([1600,512])
            b_fc1 = self.bias_variable([512])
    
            W_fc2 = self.weight_variable([512,self.ACTION])
            b_fc2 = self.bias_variable([self.ACTION])
    
            # input layer
    
            self.stateInput = tf.placeholder("float",[None,80,80,4])
    
            # hidden layers
            h_conv1 = tf.nn.relu(self.conv2d(self.stateInput,W_conv1,4) + b_conv1)
            h_pool1 = self.max_pool_2x2(h_conv1)
    
            h_conv2 = tf.nn.relu(self.conv2d(h_pool1,W_conv2,2) + b_conv2)
    
            h_conv3 = tf.nn.relu(self.conv2d(h_conv2,W_conv3,1) + b_conv3)
    
            h_conv3_flat = tf.reshape(h_conv3,[-1,1600])
            h_fc1 = tf.nn.relu(tf.matmul(h_conv3_flat,W_fc1) + b_fc1)
    
            # Q Value layer
            self.QValue = tf.matmul(h_fc1,W_fc2) + b_fc2
    
            self.actionInput = tf.placeholder("float",[None,self.ACTION])
            self.yInput = tf.placeholder("float", [None]) 
            Q_action = tf.reduce_sum(tf.mul(self.QValue, self.actionInput), reduction_indices = 1)
            self.cost = tf.reduce_mean(tf.square(self.yInput - Q_action))
            self.trainStep = tf.train.AdamOptimizer(1e-6).minimize(self.cost)
    
            # saving and loading networks
            saver = tf.train.Saver()
            self.session = tf.InteractiveSession()
            self.session.run(tf.initialize_all_variables())
            checkpoint = tf.train.get_checkpoint_state("saved_networks")
            if checkpoint and checkpoint.model_checkpoint_path:
                    saver.restore(self.session, checkpoint.model_checkpoint_path)
                    print "Successfully loaded:", checkpoint.model_checkpoint_path
            else:
                    print "Could not find old network weights"
    
        def trainQNetwork(self):
            # Step 1: obtain random minibatch from replay memory
            minibatch = random.sample(self.replayMemory,self.BATCH_SIZE)
            state_batch = [data[0] for data in minibatch]
            action_batch = [data[1] for data in minibatch]
            reward_batch = [data[2] for data in minibatch]
            nextState_batch = [data[3] for data in minibatch]
    
            # Step 2: calculate y 
            y_batch = []
            QValue_batch = self.QValue.eval(feed_dict={self.stateInput:nextState_batch})
            for i in range(0,self.BATCH_SIZE):
                terminal = minibatch[i][4]
                if terminal:
                    y_batch.append(reward_batch[i])
                else:
                    y_batch.append(reward_batch[i] + GAMMA * np.max(QValue_batch[i]))
    
            self.trainStep.run(feed_dict={
                self.yInput : y_batch,
                self.actionInput : action_batch,
                self.stateInput : state_batch
                })
    
            # save network every 100000 iteration
            if self.timeStep % 10000 == 0:
                saver.save(self.session, 'saved_networks/' + 'network' + '-dqn', global_step = self.timeStep)
    
    
        def setPerception(self,nextObservation,action,reward,terminal):
            newState = np.append(nextObservation,self.currentState[:,:,1:],axis = 2)
            self.replayMemory.append((self.currentState,action,reward,newState,terminal))
            if len(self.replayMemory) > self.REPLAY_MEMORY:
                self.replayMemory.popleft()
            if self.timeStep > self.OBSERVE:
                # Train the network
                self.trainQNetwork()
    
            self.currentState = newState
            self.timeStep += 1
    
        def getAction(self):
            QValue = self.QValue.eval(feed_dict= {self.stateInput:[self.currentState]})[0]
            action = np.zeros(self.ACTION)
            action_index = 0
            if self.timeStep % self.FRAME_PER_ACTION == 0:
                if random.random() <= self.epsilon:
                    action_index = random.randrange(self.ACTION)
                    action[action_index] = 1
                else:
                    action_index = np.argmax(QValue)
                    action[action_index] = 1
            else:
                action[0] = 1 # do nothing
    
            # change episilon
            if self.epsilon > self.FINAL_EPSILON and self.timeStep > self.OBSERVE:
                self.epsilon -= (self.INITIAL_EPSILON - self.FINAL_EPSILON)/self.EXPLORE
    
            return action
    
        def setInitState(self,observation):
            self.currentState = np.stack((observation, observation, observation, observation), axis = 2)
    
        def weight_variable(self,shape):
            initial = tf.truncated_normal(shape, stddev = 0.01)
            return tf.Variable(initial)
    
        def bias_variable(self,shape):
            initial = tf.constant(0.01, shape = shape)
            return tf.Variable(initial)
    
        def conv2d(self,x, W, stride):
            return tf.nn.conv2d(x, W, strides = [1, stride, stride, 1], padding = "SAME")
    
        def max_pool_2x2(self,x):
            return tf.nn.max_pool(x, ksize = [1, 2, 2, 1], strides = [1, 2, 2, 1], padding = "SAME")
    
    

    一共也只有160代码。
    如果这个任务不使用深度学习,而是人工的从图像中找到小鸟,然后计算小鸟的轨迹,然后计算出应该怎么按键,那么代码没有好几千行是不可能的。深度学习大大减少了代码工作。

    小结

    本文从代码角度对于DQN做了一定的分析,对于DQN的应用,大家可以在此基础上做各种尝试。

    展开全文
  • DQN-源码

    2021-03-13 03:55:12
    DQN和DUEN DQN算法的实现 这两种算法都是在开放式健身房的更严寒的环境中执行的。 在FROGGER-v0环境中实施DQN,在FROGGER-v1环境中实施DQN
  • DQN-Pytorch 在Pytorch中实现DQN
  • Nature DQN与Double DQN

    2020-02-03 09:08:00
    在前面的文章强化学习DQN算法中,介绍了经典的DQN算法,然而DQN也存在一些问题。Nature DQN与Double DQN的提出就是为了解决这些问题,下面笔者将介绍这个两种改进的算法...

    在前面的文章强化学习DQN算法中,介绍了经典的DQN算法,然而DQN也存在一些问题。Nature DQN与Double DQN的提出就是为了解决这些问题,下面笔者将介绍这个两种改进的算法。

    Nature DQN算法

    Nature DQN的提出是为了提升原版DQN的收敛速度。在原版DQN中,计算目标Q值的公式

    如下:

    由于在计算目标时,使用的是当前要训练的网络,而网络的更新使用的又是目标,两者的相关性太强了,不利于收敛。

    为了解决这个问题,Nature DQN提出了一个改进,使用两个网络来计算目标值。这两个网络的结构一样,但是参数不一样。其中一个网络被称为当前网络,主要用来选择动作,并更新模型参数。另一个网络被称为目标网络,仅仅用来计算目标值,它的参数是直接从当前网络复制而来,且目标网络的参数会比当前网络延迟。因此,新的计算目标值的公式如下:

    除了目标值的计算不一样之外,其他的流程和原版DQN没什么区别。

    Nature DQN算法流程

    1. 初始化一个存储容量为的记忆池,同时随机初始化一个当前网络,参数为,一个目标网络,参数为,并令

    2. 按以下流程迭代轮。

      1. 通过-greedy算法根据状态从动作空间中得到动作,通过网络选择动作的公式如下。

      2. 执行动作并观测环境,得到奖励以及图片

      3. 设置并对提取特征得到

      4. 将状态存储到记忆池中。

      5. 随机从记忆池中选择一组的记忆,并根据以下公式计算收获:

      6. 通过来计算损失,并通过梯度更新网络的参数。

      7. 每隔个迭代,更新

      8. 初始化状态为第一个状态,并进行处理得到特征向量

      9. 按以下流程迭代轮。

    Double DQN算法

    Double DQN算法的提出是为了解决Q-learning,DQN包括Nature DQN的一个通病,即过拟合。过拟合发生的原因,是因为在更新值时,使用的是算法。以Nature DQN为例,目标值的计算公式如下:

    由于每次估计下一个状态值时都选择最大的,因此会导致高估的情况,不一定是最接近真实的值,对应的动作不一定是最佳的动作。如此,就会在每一次迭代中产生一些误差,这些误差不断累积,最终产生了过拟合。

    为了解决这个问题,Double DQN提出两个网络解耦目标值动作的选择和目标值的计算这两步,并修改了值的计算方式。

    Double DQN的两个网络和Nature DQN的一致,目标值的计算方式如下:

    对于非终结状态的值的计算可以分为一下两个步骤:

    1. 通过当前网络估计在状态下的最大的值对应的动作,用公式表示如下。

    2. 带入以下公式计算

    由于动作是由当前网络估计出的,在一定程度上,更接近最佳的动作,因此对应的值也会更接近真实值,从而避免了过高的估计。Double DQN的其他流程和Nature DQN没有什么太大的区别,就不过多介绍了。

    关注【Java与Android技术栈】

    更多精彩内容请关注扫码

    展开全文
  • Duling DQN

    2018-08-20 22:01:10
    基于Tensorflow实现的深度强化学习算法(Dueling DQN),python3.0及以上,依赖库:Gym、Numpy、Tensorflow
  • pytorch-DQN DQN的Pytorch实现 DQN 最初的Q学习使用表格方法(有关更多详细信息和实现,请参见 )来解决,但是,表格Q学习的问题在状态增长时出现因为table不足以存储环境中给定的数亿个状态。 例如,环境为210...
  • dqn_agent dqn大脑和记忆供私人使用
  • DQN变体:Dueling DQN

    2019-12-17 12:13:04
    本篇文章主要讲解Dueling DQN的结构。 解决问题 对比之前的DQN,Dueling DQN主要对结构进行了优化。Dueling DQN考虑将QQQ网络分成两部分,第一部分是仅仅与状态S有关,与具体要采用的动作AAA无关,这部分我们叫做价值...

    本篇文章主要讲解Dueling DQN的结构。

    解决问题

    对比之前的DQN,Dueling DQN主要对结构进行了优化。Dueling DQN考虑将QQ网络分成两部分,第一部分是仅仅与状态S有关,与具体要采用的动作AA无关,这部分我们叫做价值函数部分,记做V(S,w,α)V(S,w,α),第二部分同时与状态状态SS和动作AA有关,这部分叫做优势函数(Advantage Function)部分,记为A(S,A,w,β)A(S,A,w,β),那么最终我们的价值函数可以重新表示为:
    Q(S,A,w,α,β)=V(S,w,α)+A(S,A,w,β)Q(S,A,w,α,β)=V(S,w,α)+A(S,A,w,β)
    其中,w是公共部分的网络参数,而α是价值函数独有部分的网络参数,而β是优势函数独有部分的网络参数。图结构如下:
    在这里插入图片描述
    左边为之前的结构,右边修改之后的结构。
    我们可以直接使用上一节的价值函数的组合公式得到我们的动作价值,但是这个式子无法辨识最终输出里面V(S,w,α)V(S,w,α)A(S,A,w,β)A(S,A,w,β)各自的作用,为了可以体现这种可辨识性(identifiability),实际使用的组合公式如下:
    Q(S,A,w,α,β)=V(S,w,α)+(A(S,A,w,β)1AaAA(S,a,w,β))Q(S,A, w, \alpha, \beta) = V(S,w,\alpha) + (A(S,A,w,\beta) - \frac{1}{\mathcal{A}}\sum\limits_{a' \in \mathcal{A}}A(S,a', w,\beta))
    可辨识性:可辨识性是指能否通过输入输出数据确定模型的性质的性质,我的理解是可解释性吧。

    代码

    然后接下来,就是参考tensorflow的代码,写的pytorch。我是在Prioritized Replay DQN的基础上写的。tensorflow的代码是在Nature DQN的基础上写的。

    # -*- coding: utf-8 -*-
    """
    Created on Tue Dec 17 10:59:02 2019
    
    @author: asus
    """
    
    import gym
    import torch
    import torch.nn.functional as F
    import numpy as np
    import random
    
    GAMMA = 0.9
    INITIAL_EPSILON = 0.5
    FINAL_EPSILON = 0.01
    REPLAY_SIZE = 10000
    BATCH_SIZE = 32
    ENV_NAME = 'CartPole-v0'
    EPISODE = 3000 # Episode limitation
    STEP = 300 # Step limitation in an episode
    TEST = 10 # The number of experiment test every 100 episode
    
    class SumTree(object):
        """
        This SumTree code is a modified version and the original code is from:
        https://github.com/jaara/AI-blog/blob/master/SumTree.py
        Story data with its priority in the tree.
        """
        data_pointer = 0
    
        def __init__(self, capacity):
            self.capacity = capacity  # for all priority values
            self.tree = np.zeros(2 * capacity - 1)
            # [--------------Parent nodes-------------][-------leaves to recode priority-------]
            #             size: capacity - 1                       size: capacity
            self.data = np.zeros(capacity, dtype=object)  # for all transitions
            # [--------------data frame-------------]
            #             size: capacity
    
        def add(self, p, data):
            tree_idx = self.data_pointer + self.capacity - 1
            self.data[self.data_pointer] = data  # update data_frame
            self.update(tree_idx, p)  # update tree_frame
    
            self.data_pointer += 1
            if self.data_pointer >= self.capacity:  # replace when exceed the capacity
                self.data_pointer = 0
    
        def update(self, tree_idx, p):
            change = p - self.tree[tree_idx]
            self.tree[tree_idx] = p
            # then propagate the change through tree
            while tree_idx != 0:    # this method is faster than the recursive loop in the reference code
                tree_idx = (tree_idx - 1) // 2
                self.tree[tree_idx] += change
    
        def get_leaf(self, v):
            """
            Tree structure and array storage:
            Tree index:
                 0         -> storing priority sum
                / \
              1     2
             / \   / \
            3   4 5   6    -> storing priority for transitions
            Array type for storing:
            [0,1,2,3,4,5,6]
            """
            parent_idx = 0
            while True:     # the while loop is faster than the method in the reference code
                cl_idx = 2 * parent_idx + 1         # this leaf's left and right kids
                cr_idx = cl_idx + 1
                if cl_idx >= len(self.tree):        # reach bottom, end search
                    leaf_idx = parent_idx
                    break
                else:       # downward search, always search for a higher priority node
                    if v <= self.tree[cl_idx]:
                        parent_idx = cl_idx
                    else:
                        v -= self.tree[cl_idx]
                        parent_idx = cr_idx
    
            data_idx = leaf_idx - self.capacity + 1
            return leaf_idx, self.tree[leaf_idx], self.data[data_idx]
    
        @property
        def total_p(self):
            return self.tree[0]  # the root
    
    
    class Memory(object):  # stored as ( s, a, r, s_ ) in SumTree
        """
        This Memory class is modified based on the original code from:
        https://github.com/jaara/AI-blog/blob/master/Seaquest-DDQN-PER.py
        """
        epsilon = 0.01  # small amount to avoid zero priority
        alpha = 0.6  # [0~1] convert the importance of TD error to priority
        beta = 0.4  # importance-sampling, from initial value increasing to 1
        beta_increment_per_sampling = 0.001
        abs_err_upper = 1.  # clipped abs error
    
        def __init__(self, capacity):
            self.tree = SumTree(capacity)
    
        def store(self, transition):
            max_p = np.max(self.tree.tree[-self.tree.capacity:])
            if max_p == 0:
                max_p = self.abs_err_upper
            self.tree.add(max_p, transition)   # set the max p for new p
    
        def sample(self, n):
            b_idx, b_memory, ISWeights = np.empty((n,), dtype=np.int32), np.empty((n, self.tree.data[0].size)), np.empty((n, 1))
            pri_seg = self.tree.total_p / n       # priority segment 均匀区间
            #belta不断变大
            self.beta = np.min([1., self.beta + self.beta_increment_per_sampling])  # max = 1
            #最小概率
            min_prob = np.min(self.tree.tree[-self.tree.capacity:]) / self.tree.total_p     # for later calculate ISweight
            if min_prob == 0:
                min_prob = 0.00001
            for i in range(n):
                a, b = pri_seg * i, pri_seg * (i + 1)
                #取均匀分布
                v = np.random.uniform(a, b)
                #p为误差
                idx, p, data = self.tree.get_leaf(v)
                prob = p / self.tree.total_p
                ISWeights[i, 0] = np.power(prob/min_prob, -self.beta)
                b_idx[i], b_memory[i, :] = idx, data
            return b_idx, b_memory, ISWeights
    
        def batch_update(self, tree_idx, abs_errors):
            abs_errors += self.epsilon  # convert to abs and avoid 0
    
            clipped_errors = np.minimum(abs_errors.detach().numpy(), self.abs_err_upper)
            ps = np.power(clipped_errors, self.alpha)
            for ti, p in zip(tree_idx, ps):
                self.tree.update(ti, p)
    
    class MODEL(torch.nn.Module):
        def __init__(self, env):
            super(MODEL, self).__init__()
            self.state_dim = env.observation_space.shape[0]
            self.action_dim = env.action_space.n
            self.fc1 = torch.nn.Linear(self.state_dim, 20)
            self.fc1.weight.data.normal_(0, 0.6)
            self.advantage_fc = torch.nn.Linear(20, self.action_dim)
            self.advantage_fc.weight.data.normal_(0, 0.2)
            self.value_fc = torch.nn.Linear(20, 1)
            self.value_fc.weight.data.normal_(0, 0.2)
            
        def create_Q_network(self, x):
            x = F.relu(self.fc1(x))
            self.a_value = self.advantage_fc(x)
            self.v_value = self.value_fc(x)
            Q_value = self.v_value + (self.a_value - torch.mean(self.a_value, axis=-1, keepdim=True))
            return Q_value
        
        def forward(self, x, action_input):
            Q_value = self.create_Q_network(x)
            Q_action = torch.mul(Q_value, action_input).sum(dim=1)
            return Q_action
        
    class DQN():
        def __init__(self, env):
            self.replay_total = 0
            self.target_Q_net = MODEL(env)
            self.current_Q_net = MODEL(env)
            self.memory = Memory(capacity=REPLAY_SIZE)
            self.time_step = 0
            self.epsilon = INITIAL_EPSILON
            self.optimizer = torch.optim.Adam(params=self.current_Q_net.parameters(), lr=0.0001)
    #        self.loss = torch.nn.MSELoss()
        
        def store_transition(self, s, a, r, s_, done):
            transition = np.hstack((s, a, r, s_, done))
            self.memory.store(transition)
        
        def perceive(self,state,action,reward,next_state,done):
            one_hot_action = np.zeros(self.current_Q_net.action_dim)
            one_hot_action[action] = 1
            self.store_transition(state,one_hot_action,reward,next_state,done)
            self.replay_total += 1
            if self.replay_total > BATCH_SIZE:
                self.train_Q_network()
        
        def train_Q_network(self):
            self.time_step += 1
            # Step 1: obtain random minibatch from replay memory
            tree_idx, minibatch, ISWeights = self.memory.sample(BATCH_SIZE)
            
            state_batch = torch.tensor(minibatch[:,0:4], dtype=torch.float32)
            action_batch =  torch.tensor(minibatch[:,4:6], dtype=torch.float32)
            reward_batch = [data[6] for data in minibatch]
            next_state_batch = torch.tensor(minibatch[:,7:11], dtype=torch.float32)
    
            # Step 2: calculate y
            y_batch = []
            
            current_a = self.current_Q_net.create_Q_network(next_state_batch)
            max_current_action_batch = torch.argmax(current_a, axis=1)
    
            Q_value_batch = self.target_Q_net.create_Q_network(next_state_batch)
            
            for i in range(0,BATCH_SIZE):
                done = minibatch[i][11]
                if done:
                    y_batch.append(reward_batch[i])
                else:
                    max_current_action = max_current_action_batch[i]
                    y_batch.append(reward_batch[i] + GAMMA * Q_value_batch[i,max_current_action])
                    
            y = self.current_Q_net(torch.FloatTensor(state_batch), torch.FloatTensor(action_batch))
            y_batch = torch.FloatTensor(y_batch)
            cost = self.loss(y, y_batch, torch.tensor(ISWeights))
            self.optimizer.zero_grad()
            cost.backward()
            self.optimizer.step()
            y = self.current_Q_net(torch.FloatTensor(state_batch), torch.FloatTensor(action_batch))
            abs_errors = torch.abs(y_batch - y)
            self.memory.batch_update(tree_idx, abs_errors)
            
        def egreedy_action(self,state):
            Q_value = self.current_Q_net.create_Q_network(torch.FloatTensor(state))
            if random.random() <= self.epsilon:
                self.epsilon -= (INITIAL_EPSILON - FINAL_EPSILON) / 10000
                return random.randint(0, self.current_Q_net.action_dim - 1)
            else:
                self.epsilon -= (INITIAL_EPSILON - FINAL_EPSILON) / 10000
                return torch.argmax(Q_value).item()     
                    
        def action(self,state):
            return torch.argmax(self.target_Q_net.create_Q_network(torch.FloatTensor(state))).item()
                   
        def update_target_params(self):
            torch.save(self.current_Q_net.state_dict(), 'net_params.pkl')
            self.target_Q_net.load_state_dict(torch.load('net_params.pkl'))
        
        def loss(self, y_output, y_true, ISWeights):
            value = y_output - y_true
            return torch.mean(value*value*ISWeights)
        
    def main():
      # initialize OpenAI Gym env and dqn agent
      env = gym.make(ENV_NAME)
      agent = DQN(env)
    
      for episode in range(EPISODE):
        # initialize task
        state = env.reset()
        # Train
        for step in range(STEP):
        
          action = agent.egreedy_action(state) # e-greedy action for train
          next_state,reward,done,_ = env.step(action)
          # Define reward for agent
          reward = -1 if done else 0.1
          agent.perceive(state,action,reward,next_state,done)
          state = next_state
          if done:
            break
        # Test every 100 episodes
        if episode % 100== 0:
          total_reward = 0
          for i in range(TEST):
            state = env.reset()
            for j in range(STEP):
    #          env.render()
              action = agent.action(state) # direct action for test
              state,reward,done,_ = env.step(action)
              total_reward += reward
              if done:
                break
          ave_reward = total_reward/TEST
          print ('episode: ',episode,'Evaluation Average Reward:',ave_reward)
        agent.update_target_params()
    if __name__ == '__main__':
      main()
    

    参考文献:https://www.cnblogs.com/pinard/p/9923859.html

    展开全文
  • 本篇介绍三种DQN的变体,分别是从参数更新部分改进的Double DQN,从经验回放部分改进的Prioritized DQN,从神经网络结构部分改进的Dueling DQN。 Double DQN 上一篇提到的DQN是基于Q-learning,更新也是基于贪婪...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,015
精华内容 1,206
关键字:

dqn