精华内容
下载资源
问答
  • 详细分析莫烦DQN代码

    2021-01-06 22:43:02
    详细分析莫烦DQN代码 Python入门,莫烦是很好的选择,快去b站搜视频吧! 作为一只渣渣白,去看了莫烦的强化学习入门, 现在来回忆总结下DQN,作为笔记记录下来。 主要是对代码做了详细注释 DQN有两个网络,一个eval...
  • 内心深处 DeepMind 的 DQN 代码副本
  • my_PDQN:我的纸张Parameterized-DQN代码
  • 该项目包含 DQN 3.0 的源代码,这是一种基于 Lua 的深度强化学习架构,需要重现论文“通过深度强化学习进行人类级控制”,Nature 518, 529–533(2015 年 2 月 26 日)中描述的实验doi:10.1038/nature14236。...
  • DQN代码注释

    2020-05-11 17:18:40
    ** Intelligent Power Control for ...简介:(1)主要为文献《Intelligent Power Control for Spectrum Sharing in Cognitive Radios:A Deep Reinforcement Learning Approach》在github上的开源代码进行了注释,

    Intelligent Power Control for Spectrum Sharing in Cognitive Radios:A Deep Reinforcement Learning Approach——源码和注释

    简介:(1)主要为文献《Intelligent Power Control for Spectrum Sharing in Cognitive Radios:A Deep Reinforcement Learning Approach》在github上的开源代码进行了注释,注释中添加了自己对于问题的一些理解和疑问。这个Python源码的程序入口为main函数。main函数中调用了DQN、Power_control两个作者独立完成的包和numpy、random两个python自带的包。对于这个源码,还有部分位置没有添加好注释,需要再花一点时间理解和消化代码的意义。(2)除了为这部分源码添加注释以外,最近还学习了教材《Python编程:从入门到实践》的一部分,尝试构造几个DQN和深度学习的模板,方便以后使用。

    第一部分:DQN 包

    import tensorflow as tf 
    import numpy as np 
    import random
    from collections import deque #后面的部分可以直接调用,不在需要使用collections.deque了
    
    GAMMA = 0.8  #在更新Q函数时的折损参数
    OBSERVE = 300 
    EXPLORE = 100000 
    FINAL_EPSILON = 0.0 
    INITIAL_EPSILON = 0.8 
    REPLAY_MEMORY = 400 
    BATCH_SIZE = 256 
    
    class BrainDQN:
    
       def __init__(self,actions,Sensor):
            
          self.replayMemory = deque()  #replayMemory是一个双向进出的队列
          self.timeStep = 0
          self.epsilon = INITIAL_EPSILON
          self.recording = EXPLORE
          self.sensor_dim = Sensor
          self.actions = actions
          self.hidden1 = 256
          self.hidden2 = 256
          self.hidden3 = 512
            
          self.createQNetwork()
    
       def createQNetwork(self):  #以tf的方式搭建graph,然后完成初始化,这里的步骤只是在create,并没有开始跑
    
          W_fc1 = self.weight_variable([self.sensor_dim,self.hidden1])
          b_fc1 = self.bias_variable([self.hidden1])
    
          W_fc2 = self.weight_variable([self.hidden1,self.hidden2])
          b_fc2 = self.bias_variable([self.hidden2])
            
          W_fc3 = self.weight_variable([self.hidden2,self.hidden3])
          b_fc3 = self.bias_variable([self.hidden3])
            
          W_fc4 = self.weight_variable([self.hidden3,self.actions])
          b_fc4 = self.bias_variable([self.actions])  #设置Q网络的权重
    
          self.stateInput = tf.placeholder("float",[None,self.sensor_dim])      #DQN的输入值:是这10个感应器的结果
                                                                                #placeholder和feed_dic是一对相互搭配使用的函数,前者申请了一个必要的空间来放置需要的数据。后者则实时输入数据
    
          h_fc1 = tf.nn.relu(tf.matmul(self.stateInput,W_fc1) + b_fc1)
          h_fc2 = tf.nn.relu(tf.matmul(h_fc1,W_fc2) + b_fc2)
          h_fc3 = tf.nn.tanh(tf.matmul(h_fc2,W_fc3) + b_fc3)               #前面是在赋初始值,现在便把这个网络的隐藏层搭建好了
            
          self.QValue = tf.matmul(h_fc3,W_fc4) + b_fc4                     #现在是确定了输出值,输出值是QValue
    
          self.actionInput = tf.placeholder("float",[None,self.actions])
          self.yInput = tf.placeholder("float", [None])     #????这里又定义了yInput和actionInput
          Q_action = tf.reduce_sum(tf.multiply(self.QValue, self.actionInput), reduction_indices = 1) #reduce_sum函数求和,后面的reduction_indices代表了求和的维度
          self.cost = tf.reduce_mean(tf.square(self.yInput - Q_action))  #这个self.cost就是我们使用adam算法要优化的东西,这里采用了一个求平均值函数
          self.trainStep = tf.train.AdamOptimizer(learning_rate=10**-5).minimize(self.cost)  #攒够数据调参的时候,使用的就是这个地方的Adam函数,learning_rate定义的是学习率,后面的minimize是指明了优化的变量是self.cost???那么在这里,调谁的参?
    
          self.session = tf.InteractiveSession()
          self.session.run(tf.global_variables_initializer())  #初始化语句
    
       def trainQNetwork(self):  #数据攒够了以后,就开始调用这个部分来训练q网络,没有输入的参数,故直接调用。返回值是self.loss,这里已经开始跑这个程序了
          minibatch = random.sample(self.replayMemory,BATCH_SIZE)  #随机地从replayMemory中取出BATCH_SIZE的数据,minibatch是一个BATCH_SIZE的数组,这里还没有对replayMemory进行赋值
          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]  #猜测:定义了data数组,这个数组的0.1.2.3号元素分别存放了从minibatch中取得的s、a、r、s+1
    
          y_batch = []
          QValue_batch = self.QValue.eval(feed_dict={self.stateInput:nextState_batch})  #????这里的.eval是什么意思
          for i in range(0,BATCH_SIZE):
    
             y_batch.append(reward_batch[i] + GAMMA * np.max(QValue_batch[i]))
    
          _, self.loss = self.session.run([self.trainStep,self.cost],feed_dict={
             self.yInput : y_batch,
             self.actionInput : action_batch,
             self.stateInput : state_batch
             })   #self.loss = self.session.run([self.trainStep,self.cost],feed_dict={self.yInput : y_batch,self.actionInput : action_batch,self.stateInput : state_batch}) ,要run的是trainStep和self.cost,但输入数据的数组不明白为什么是三组,且使用冒号连接在了一起。
          return self.loss
           #这之前的两个大函数trainqnetwork和creatqnetwork都没有调用后面的函数。
    
       def setPerception(self,nextObservation,action,reward):
          loss = 0
          newState = nextObservation
          self.replayMemory.append((self.currentState,action,reward,newState))
          if len(self.replayMemory) > REPLAY_MEMORY:
             self.replayMemory.popleft()
          if self.timeStep > OBSERVE:
                
             loss = self.trainQNetwork()    #如果时间步大于300,那么训练一次q网络???如何和积攒够400个数据联系在一起
    
          self.currentState = newState
          self.timeStep += 1  #时间步加1
          return loss  #loss=self.loss
            
    
       def getAction(self):  #Q网络已经生成结果,此时便是要决定是选择Q网络的输出结果还是按照某种概率去探索新的世界:得到当前时刻应该采取的动作
          QValue = self.QValue.eval(feed_dict= {self.stateInput:[self.currentState]})  #????这里的.eval是什么意思
          action = np.zeros(self.actions)
          if random.random() <= self.epsilon:
             action_index = random.randrange(self.actions)  #rando.randrange函数返回递增集合中的一个随机数,这里的action_index相当于从action许用的下标中随机选中了一个,然后在下面的语句action[action_index]中调用
             action[action_index] = 1  #action集中某个元素被置为1表示选用这个集合中的这个元素作为当前的动作
          else:
             action_index = np.argmax(QValue)  #这时选用Q网络输出值中最大值作为当前的动作
             action[action_index] = 1
             
          if self.epsilon > FINAL_EPSILON and self.timeStep > OBSERVE:
             self.epsilon -= (INITIAL_EPSILON - FINAL_EPSILON)/EXPLORE   #根据步骤来更新epsilon
             self.recording = self.recording-1  #迭代的次数加一。换言之,每次迭代都需要调用这个getaction函数一次,迭代标志就是在这里改变的。
    
          return action,self.recording  #返回action表(本质上就是告知了应该选择哪个动作,然后再参照这个动作和环境进行互动)和剩余迭代次数。
        
       def getAction_test(self,observation):  #这里是所有的迭代次数已经完成,把agent投放到了环境当中实打实地去进行工作时的调用
          QValue = self.QValue.eval(feed_dict= {self.stateInput:[observation]})
          action = np.zeros(self.actions)
          action_index = np.argmax(QValue)   #这里不再采用贪心算法,直接采用q网络输出值中的最好动作即可
          action[action_index] = 1
    
          return action
        
       def setInitState(self,observation):  #把观察到的情况输入到这个函数中作为当前时间的状态值,然后参与到上面replayMemory的工作中去。
          self.currentState = observation
    
       def weight_variable(self,shape):  #和下面的bias_variable一样,都是在createQnetwork时使用的辅助函数,需要调用TF包中的相关函数
          initial = tf.truncated_normal(shape)
          return tf.Variable(initial)
    
       def bias_variable(self,shape):
          initial = tf.constant(0.01, shape = shape)
          return tf.Variable(initial
    
    

    第二部分:power_control包

    import random
    import numpy as np
    import math
    
    class GameState: #环境生成模拟器
        def __init__(self,P_1,P_2,Noise,Sensor):  #初始化函数,将需要的增益、噪声、数量等参数输入
            
            self.P_1 = P_1
            self.P_2 = P_2
            self.length_P_1 = len(self.P_1)
            self.length_P_2 = len(self.P_2)
            
            self.h_11 = 1;self.h_12 = 1
            self.h_21 = 1;self.h_22 = 1
            
            self.ita_1 = 1.2
            self.ita_2 = 0.7
            
            self.sigma_sq_1 = 0.01
            self.sigma_sq_2 = 0.01
            
            self.lam = 0.1
            self.alpha = 0.5  #这个alpha是什么?
    
            self.num_sensor = Sensor
            self.noise = Noise
            
            self.P,self.sigma = self.dis()  #初始化时级调用了self.dis
            
        def dis(self):  #猜测:这个diss就是代表了discover,就是这个sense node感知到的具体结果,具体结果的表达式就是38行的sigma数组,这个数组中存放了感知的所有结果
            d = [[random.uniform(100,300) for _ in range(self.num_sensor)] for _ in range(2)]  #d阵代表了距离
            P = np.zeros((2,self.num_sensor))
            for i in range(0,self.num_sensor):
                P[0][i] = ((self.lam/(4*math.pi)/d[0][i])**2)  #P阵是一个二维数组,第一行存放的是主用户的信道衰落,第二行存放的是次用户的信道衰落
                P[1][i] = ((self.lam/(4*math.pi)/d[1][i])**2)
            sigma = np.zeros((self.num_sensor))
            for i in range(0,self.num_sensor):
                sigma[i] = ( P[0][i]*self.P_1[0]+P[1][i]*self.P_2[0] )/ self.noise
            return P,sigma
    
            # 猜测,感知节点也不知道PUS和SUS使用功率的具体情况,它所能测到的其实就是这个表达是的“值”,只不过我们在仿真的过程中是知道这个值诞生的原因的。
            # 换言之,我们在构造DQN时,这种输入的state是可以很模糊的。在这里我们不需要知道PUS和SUs具体的情况,我们只需要知道当前固定频段上的噪声结果(包括了环境噪声和使用这个频段所有用户通信功率造成的噪声)。
            # 再进一步考虑,我们可不可以设置这样一种并行的DQN?比如现在我们面临5个频段,在每个时刻,都在每个频段上去训练这个DQN网络,单个的DQN网络输出的结果是在这个频段上最优的许用功率,然后在这个DQN外面
            # 再嵌套一个?优先级列表?选择器来决定使用哪个频段。从而把这个问题分解为了2个步骤,第一步先求每个频段的最优功率,第二步再选择最优频段
            # 需要解决的问题:
            # (1)可否参考ADMM算法,分步解决需要解决的问题,细化更多的部分
            # (2)分步在每个频段上做DQN时,环境的变化如何处理?换言之,这个MDP过程需要输出一个结果,但这个action结果是需要优先级选择器来决定的
            # (3)是否存在分部DQN的东西?
        def ini(self):
            self.p_1 = self.P_1[random.randint(0,self.length_P_1-1)]
            self.p_2 = self.P_2[random.randint(0,self.length_P_2-1)]
    
        def ini_test(self):
            self.p_1_test = self.P_1[random.randint(0,self.length_P_1-1)]
            self.p_2_test = self.P_2[random.randint(0,self.length_P_2-1)]
        
        def frame_step(self, input_actions, policy, i):
            if i == True:
                if policy == 1:
                    self.p_1 = self.update_p1_v1(self.p_2)
                if policy == 2:
                    self.p_1 = self.update_p1_v2(self.p_1,self.p_2)
                action = np.flatnonzero(input_actions)[0]   # Return indices that are non-zero in the flattened version of a.
                # 返回非零元素的下标。
                self.p_2 = self.P_2[action]
            observation = self.compute_observation(self.p_1,self.p_2)
            reward = self.compute_reward(self.p_1,self.p_2)
            
            terminal = (reward==10)
            
            return observation,reward,terminal
        
        def frame_step_test(self, input_actions, policy, i):
            if i == True:
                if policy == 1:
                    self.p_1_test = self.update_p1_v1(self.p_2_test)
                if policy == 2:
                    self.p_1_test = self.update_p1_v2(self.p_1_test,self.p_2_test)
                action = np.flatnonzero(input_actions)[0]
                self.p_2_test = self.P_2[action]
            observation = self.compute_observation(self.p_1_test,self.p_2_test)
            reward = self.compute_reward(self.p_1_test,self.p_2_test)
            
            terminal = (reward==10)  # 当reward==10时,作为terminal的标志。
            
            return observation,reward,terminal
        
        def compute_observation(self,x,y):   # 感知器观察到的环境状态,存储在observation数组中,输入是PU和SU正在使用的功率
            observation = np.zeros((self.num_sensor))
            for i in range(0,self.num_sensor):
                observation[i] = self.P[0][i] * x + self.P[1][i] * y + random.gauss(0,self.sigma[i])  #Pnrk表示目前的状态空间
                if observation[i]<0:
                    observation[i] =0
                observation[i] = observation[i]*(10**7)#乘了10的七次方
            return observation
        
        def compute_reward(self,x,y):
            success_1,success_2 = self.compute_SINR(x,y)
            reward = self.alpha * success_1 + (1-self.alpha) * success_2
            if reward == 0.5:
                reward = 0
            if reward == 1:
                reward = 10
            return reward
        
        def update_p1_v1(self,y):  #主用户的第一种更新方式
            p_1_n = self.ita_1/((abs(self.h_11)**2)/((abs(self.h_21)**2)*y + self.sigma_sq_1))
            v = []
            for ind in range(self.length_P_1):
                v.append(max(p_1_n-self.P_1[ind],0))
            p_1_new = self.P_1[v.index(min(v))]
            return p_1_new
        
        def update_p1_v2(self,x,y):  #主用户的第二种更新方式
            ind_p_1 = self.P_1.index(x)
            tSINR_1 = ((abs(self.h_11)**2)*x/((abs(self.h_21)**2)*y + self.sigma_sq_1))
            tao = x * self.ita_1 / tSINR_1
            if tao>=x and ind_p_1+1<=self.length_P_1-1 and tao<=self.P_1[ind_p_1+1] :
                x = self.P_1[ind_p_1+1]
            elif ind_p_1-1>=0 and tao<=self.P_1[ind_p_1-1] :
                x = self.P_1[ind_p_1-1]
            return x
        
        def compute_SINR(self,x,y):
            success_1 = ( (abs(self.h_11)**2)*x/((abs(self.h_21)**2)*y + self.sigma_sq_1)) >= self.ita_1
            success_2 = ( (abs(self.h_22)**2)*y/((abs(self.h_12)**2)*x + self.sigma_sq_2)) >= self.ita_2
            return success_1,success_2
    
    

    第三部分:main函数

    from power_control import GameState
    from DQN import BrainDQN
    import numpy as np
    import matplotlib.pyplot as plt
    
    P_1 = [round(0.1*i/2.0,2) for i in range(1,9)]
    P_2 = [round(0.1*i/2.0,2) for i in range(1,9)]  #round返回浮点数的四舍五入值
    actions = len(P_2) #返回数组P_2的长度,这里的长度是9
    
    Loss = []
    Success = []
    Fre = []
    
    noise = 3      
    num_sensor = 10  # N 
    policy = 2      # choose power change policy for PU, it should be 1(Multi-step) or 2(Single step)
    
    brain = BrainDQN(actions,num_sensor) #DQN模拟生成器,需要告知动作空间的个数、感知器的数量
    com = GameState(P_1,P_2,noise,num_sensor)#一个环境模拟生成器,告知PU和SU的功率表,噪声,感知器的数量。
    terminal =True
    recording = 100000  #迭代次数标记,这里需要迭代100000次,然后使用下面的100000-recording来表示当前正在迭代的次数
    
    while(recording>0):   #每次迭代都需要执行这个函数段
        # initialization
        if(terminal ==True):
            com.ini() #环境模拟生成器的初始化
            observation0, reward0, terminal = com.frame_step(np.zeros(actions),policy,False)
            brain.setInitState(observation0)
    
        # train  train和test是分开的,train完以后,Q网络就训练完了,这时候可以直接投放到环境中去运作
        action,recording = brain.getAction()  #每次迭代都按照DQN包中的函数来得到本次迭代选择的action(返回值是一个数组,这个数组里面被赋值为1的action便是本次迭代选择的动作)
        nextObservation,reward,terminal = com.frame_step(action,policy,True)  #取得了action列表,我们就要按照和这个action和环境互动了
        loss = brain.setPerception(nextObservation,action,reward)  #得到本次的loss值
    
        # test  只有在500的整数倍时,才test一次。????那么攒够一定的次数才调参是怎样呢?
        if (recording+1)%500==0:
            
            Loss.append(loss) #append把元素添加到列表末尾
            print("iteration : %d , loss : %f ." %(100000-recording, loss))
            
            success = 0.0
            fre = 0
            num = 1000.0
            for ind in range(1000):   #每次test阶段,都使用1000个时间点,来得到把agent放到环境里时的实际情况,这里的run和最终的test不一样,这里只是验证当前训练步骤下能不能收敛(通过成功率、平均步数来衡量成功率)
                T = 0
                com.ini_test()
                observation0_test, reward_test, terminal_test = com.frame_step_test(np.zeros(actions),policy,False)
                while (terminal_test !=True) and T<20:
                    action_test = brain.getAction_test(observation0_test)
                    observation0_test,reward_test,terminal_test = com.frame_step_test(action_test,policy,True)
                    T +=1
                if terminal_test==True:
                    success +=1
                    fre +=T
            if success == 0:
                fre = 0
            else:
                fre = fre/success
            success = success/num
            Success.append(success)
            Fre.append(fre)
            print("success : %f , step : %f ." %(success , fre))
            
    plt.plot(Loss)    #最后一次test时生成Loss的结果生成图片
    plt.show()
    
    plt.plot(Success)
    plt.show()
    
    plt.plot(Fre)
    plt.show()
    
    展开全文
  • pytorch版DQN代码逐行分析 前言 如强化学习这个坑有一段时间了,之前一直想写一个系列的学习笔记,但是打公式什么的太麻烦了,就不了了之了。 最近深感代码功底薄弱,于是重新温习了一遍几种常用的RL算法,并打算做...

    pytorch版DQN代码逐行分析

    前言

    入强化学习这个坑有一段时间了,之前一直想写一个系列的学习笔记,但是打公式什么的太麻烦了,就不了了之了。
    最近深感代码功底薄弱,于是重新温习了一遍几种常用的RL算法,并打算做一个代码库,以便之后使用。

    正文

    这是第一站-----DQN的代码解读
    源代码:https://github.com/higgsfield/RL-Adventure
    无奈,这个代码库里的代码实在有点古老-_-!!,而且存在一些小错误,加上代码结构我很不喜欢。。。于是重新整理了一下,同时添加了大量注释,供新手以及本人食用。

    import math, random
    import gym
    import numpy as np
    import torch
    import torch.nn as nn
    import torch.optim as optim
    import torch.autograd as autograd
    import torch.nn.functional as F
    from collections import  Counter
    from collections import deque
    import matplotlib.pyplot as plt
    
    USE_CUDA = torch.cuda.is_available()
    #将变量放到cuda上
    Variable = lambda *args, **kwargs: autograd.Variable(*args, **kwargs).cuda() if USE_CUDA else autograd.Variable(*args, **kwargs)
    
    class DQN(nn.Module):
        def __init__(self, observation_space, action_sapce):
            super(DQN, self).__init__()
    
            self.layers = nn.Sequential(
                nn.Linear(observation_space,128),
                nn.ReLU(),
                nn.Linear(128,128),
                nn.ReLU(),
                nn.Linear(128, action_sapce)
            )
            self.observation_space = observation_space
            self.action_sapce = action_sapce
    
        def forward(self, x):
            return self.layers(x)
    
        def act(self, state, epsilon):
            if random.random() > epsilon:
                #如果使用的是GPU,这里需要把数据丢到GPU上
                state   = Variable(torch.FloatTensor(state).unsqueeze(0), volatile=True)#volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。
                #.squeeze() 把数据条目中维度为1 的删除掉
                q_value = self.forward(state)
                action = q_value.max(1)[1].data[0]
                #max(1)返回每一行中最大值的那个元素,且返回其索引,max(0)是列
                #max()[1]只返回最大值的每个索引,max()[0], 只返回最大值的每个数
    
                action = action.cpu().numpy()#从网络中得到的tensor形式,因为之后要输入给gym环境中,这里把它放回cpu,转为数组形式
                action =int(action)
            else:
                action = random.randrange(self.action_sapce)#返回指定递增基数集合中的一个随机数,基数默认值为1。
            return action
    
    class ReplayBuffer(object):
        def __init__(self, capacity):
            #deque模块是python标准库collections中的一项,它提供了两端都可以操作的序列,其实就是双向队列,
            #可以从左右两端增加元素,或者是删除元素。如果设置了最大长度,非输入端的数据会逐步移出窗口。
            self.buffer = deque (maxlen = capacity)
    
        def push (self, state ,aciton, reward, next_state, done):
            state = np.expand_dims(state,0)
            #这里增加维度的操作是为了便于之后使用concatenate进行拼接
            next_state = np.expand_dims(next_state,0)
            self.buffer.append((state, aciton, reward, next_state, done))
    
        def sample(self, batch_size):
            # 将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表
            state , action, reward, next_state, done = zip(*random.sample(self.buffer, batch_size))
            #最后使用concatenate对数组进行拼接,相当于少了一个维度
            return np.concatenate(state),  action, reward, np.concatenate(next_state), done
    
    
    def compute_td_loss(model,optimizer, replay_buffer, gamma, batch_size):
        state, action, reward, next_state, done = replay_buffer.sample(batch_size)
        #通通丢到GPU上去
        state = Variable(torch.FloatTensor(np.float32(state)))
        next_state = Variable(torch.FloatTensor(np.float32(next_state)), volatile=True)
        action = Variable(torch.LongTensor(action))
        reward = Variable(torch.FloatTensor(reward))
        done = Variable(torch.FloatTensor(done))
    
        q_values = model(state)
        next_q_values = model(next_state)
    
        q_value = q_values.gather(1, action.unsqueeze(1)).squeeze(1)
        #gather可以看作是对q_values的查询,即元素都是q_values中的元素,查询索引都存在action中。输出大小与action.unsqueeze(1)一致。
        #dim=1,它存放的都是第1维度的索引;dim=0,它存放的都是第0维度的索引;
        #这里增加维度主要是为了方便gather操作,之后再删除该维度
        next_q_value = next_q_values.max(1)[0]
    
        expected_q_value = reward + gamma * next_q_value * (1 - done)
    
        loss = (q_value - Variable(expected_q_value.data)).pow(2).mean()
    
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        return loss
    
    def main():
        env_id = "CartPole-v0"
        env = gym.make(env_id)
        observation_space = env.observation_space.shape[0]
        action_sapce = env.action_space.n
        model = DQN (observation_space, action_sapce)
        if USE_CUDA:
            model = model.cuda()
        optimizer = optim.Adam(model.parameters())
        replay_buffer = ReplayBuffer(1000)
    
        batch_size = 32
        gamma = 0.99
        num_frames = 10000
    
        losses = []
        all_rewards = []
        x_axis1 = []
        episode_reward = 0
    
        epsilon_start = 1.0
        epsilon_final = 0.01
        epsilon_decay = 500
    
        #要求探索率随着迭代次数增加而减小
        epsilon_by_frame = lambda frame_idx: epsilon_final + (epsilon_start - epsilon_final) * math.exp( -1. * frame_idx / epsilon_decay)
    
        state = env.reset()
        for frame_idx in range(1, num_frames + 1):
            epsilon = epsilon_by_frame(frame_idx)
            action = model.act(state, epsilon)
            next_state, reward, done, _ = env.step(action)
            replay_buffer.push(state, action, reward, next_state, done)
            state = next_state
            episode_reward += reward
    
            if done:
                state = env.reset()
                x_axis1.append(frame_idx)
                all_rewards.append(episode_reward)
                episode_reward = 0
    
            if frame_idx+1 > batch_size:
                loss = compute_td_loss(model, optimizer, replay_buffer, gamma, batch_size)
                losses.append(np.array(loss.data.cpu()))
    
    
            if frame_idx % 200 == 0:
                plt.plot(x_axis1, all_rewards)
                #plt.plot(x_axis1, losses)
                plt.show()
    
    if __name__ == '__main__':
        main()
    

    reward
    对于Atari这种游戏来说,因为输入的是一张像素图像,因此我们需要对原来的框架稍作改动,改动部分如下

    class CnnDQN(nn.Module):
        def __init__(self, observation_space, action_sapce):
            super(CnnDQN, self).__init__()
    
            self.observation_space = observation_space
            self.action_sapce = action_sapce
    
            self.features = nn.Sequential(
                nn.Conv2d(self.observation_space[0], 32, kernel_size=8, stride=4),
                nn.ReLU(),
                nn.Conv2d(32, 64, kernel_size=4, stride=2),
                nn.ReLU(),
                nn.Conv2d(64, 64, kernel_size=3, stride=1),
                nn.ReLU()
            )
    
            self.fc = nn.Sequential(
                nn.Linear(7 * 7 * 64, 512),
                nn.ReLU(),
                nn.Linear(512,self.action_sapce)
            )
    
        def forward(self,x):
            x = self.features(x)
            x = x.view(x.size(0), -1)#将多维度的Tensor展平成一维
            # x.size(0)指batchsize的值,x = x.view(x.size(0), -1)简化x = x.view(batchsize, -1),view()函数的功能根reshape类似,用来转换size大小。
            # x = x.view(batchsize, -1)中batchsize指转换后有几行,而-1指在不告诉函数有多少列的情况下,根据原tensor数据和batchsize自动分配列数。
            x = self.fc(x)
            return x
    
        # def feature_size(self):
        #     #这里就很粗暴,先建立一个大小和预期输入的全0tensor,送入features中运行,最后得到输出,展平,读取长度。这里是7 * 7 * 64
        #     return self.features(autograd.Variable(torch.zeros(1, *self.observation_space))).view(1, -1).size(1)
    
        def act(self, state, epsilon):
            if random.random() > epsilon:
                state = Variable(torch.FloatTensor(np.float32(state)).unsqueeze(0), volatile=True)#(1,1,84,84)
                q_value = self.forward(state)
                action = q_value.max(1)[1].data[0]
                action = action.cpu().numpy()  # 从网络中得到的tensor形式,因为之后要输入给gym环境中,这里把它放回cpu,转为数组形式
                action = int(action)
    
            else:
                action = random.randrange(self.action_sapce)
            return action
    

    其它部分不用改变,调整下迭代次数即可

    展开全文
  • DQN代码问题

    2020-07-27 16:48:14
    1.env.render()函数用于渲染出当前的智能体以及环境的状态。 2.env.reset()为重新初始化函数 3.observation_, reward, done = env.step(action) 第一个为当前屏幕图像的像素值,经过彩色转灰度、缩放等变换最终送入...

    1.env.render()函数用于渲染出当前的智能体以及环境的状态。

    2.env.reset()为重新初始化函数

    3.observation_, reward, done = env.step(action)
    第一个为当前屏幕图像的像素值,经过彩色转灰度、缩放等变换最终送入我们上一篇文章中介绍的 CNN 中,得到下一步“行为”;

    第二个值为奖励,每当游戏得分增加时,该返回值会有一个正反馈;

    第三个值 gameover 为布尔值,如果游戏结束,返回 True;

    展开全文
  • DQN代码中关于tf.stop_gradient的认识

    千次阅读 2018-10-24 15:28:09
    在学习莫烦python强化学习中DQN这一节时,莫烦大佬给出了两种DQN代码,大致框架都是一致的,但是仔细一读就会发现在DQN_modified.py文件中对于target_net训练出的结果直接加入到loss值的计算,而RL_brain.py中单独...

    记录一下今天遇到的这个问题,方便以后反过头来继续查阅。

    在学习莫烦python强化学习中DQN这一节时,莫烦大佬给出了两种DQN代码,大致框架都是一致的,但是仔细一读就会发现在DQN_modified.py文件中对于target_net训练出的结果直接加入到loss值的计算,而RL_brain.py中单独添加了placeholder,将target_net的结果通过placeholder传入eval_net中的loss值进行计算。这些只是说了大概的思路,接下来通过代码详细介绍一下这两的具体区别。

    首先来介绍RL_brain.py中比较简单而且容易理解的no stop_gradient写法。代码如下:

    ##################### eval_net #############################
    ...
    self.q_target = tf.placeholder(tf.float32, [None, self.n_actions], name='Q_target') 
    ...
    with tf.variable_scope('loss'):
        self.loss = tf.reduce_mean(tf.squared_difference(self.q_target, self.q_eval))
    with tf.variable_scope('train'):
        self._train_op = tf.train.RMSPropOptimizer(self.lr).minimize(self.loss)
    ##################### target_net #############################
    self.s_ = tf.placeholder(tf.float32, [None, self.n_features], name='s_')    # input
    with tf.variable_scope('target_net'):
    
        c_names = ['target_net_params', tf.GraphKeys.GLOBAL_VARIABLES]
    
        # first layer. collections is used later when assign to target net
        with tf.variable_scope('l1'):
            w1 = tf.get_variable('w1', [self.n_features, n_l1], initializer=w_initializer, collections=c_names)
            b1 = tf.get_variable('b1', [1, n_l1], initializer=b_initializer, collections=c_names)
            l1 = tf.nn.relu(tf.matmul(self.s_, w1) + b1)
    
        # second layer. collections is used later when assign to target net
        with tf.variable_scope('l2'):
            w2 = tf.get_variable('w2', [n_l1, self.n_actions], initializer=w_initializer, collections=c_names)
            b2 = tf.get_variable('b2', [1, self.n_actions], initializer=b_initializer, collections=c_names)
            self.q_next = tf.matmul(l1, w2) + b2
    
    ...
    ...
    q_target[batch_index, eval_act_index] = reward + self.gamma * np.max(q_next, axis=1)

    从上述代码中可以看出在DQN中会维持两个网络,一个eval net,一个target net。我们对eval net的参数更新是通过MSE + GD来更新的,而target_net的参数是之前eval_net的参数。loss函数中有一个参数q_target,而这个参数是target_net对下一状态的估值。no stop_gradient的做法是对eval net设置一个placeholder,也即引入一个输入,用这个placeholder计算loss。

    接下来说一下stop_gradient的写法。

    ...
    # ------------------ build evaluate_net ------------------
    with tf.variable_scope('eval_net'):
    #此网络一共两层
        e1 = tf.layers.dense(self.s, 20, tf.nn.relu, kernel_initializer=w_initializer,
                                     bias_initializer=b_initializer, name='e1')
        self.q_eval = tf.layers.dense(e1, self.n_actions, kernel_initializer=w_initializer,
                                              bias_initializer=b_initializer, name='q')
    
    # ------------------ build target_net ------------------
    with tf.variable_scope('target_net'):
        t1 = tf.layers.dense(self.s_, 20, tf.nn.relu, kernel_initializer=w_initializer,
                                     bias_initializer=b_initializer, name='t1')
        self.q_next = tf.layers.dense(t1, self.n_actions, kernel_initializer=w_initializer,
                                              bias_initializer=b_initializer, name='t2')
    with tf.variable_scope('q_target'):
        q_target = self.r + self.gamma * tf.reduce_max(self.q_next, axis=1, name='Qmax_s_')    # shape=(None, )
        self.q_target = tf.stop_gradient(q_target)
    with tf.variable_scope('q_eval'):
        a_indices = tf.stack([tf.range(tf.shape(self.a)[0], dtype=tf.int32), self.a], axis=1)
        self.q_eval_wrt_a = tf.gather_nd(params=self.q_eval, indices=a_indices)    # shape=(None, )
    with tf.variable_scope('loss'):
        self.loss = tf.reduce_mean(tf.squared_difference(self.q_target, self.q_eval_wrt_a, name='TD_error'))
    with tf.variable_scope('train'):
        self._train_op = tf.train.RMSPropOptimizer(self.lr).minimize(self.loss)

    在这段代码中可以看到self.q_target = tf.stop_gradient(q_target)这句,这就是对q_target的反向传递进行截断,在得到target_net的参数后,进行有关计算就得到q_target这个op(运行时就是Tensor了),然后利用通过截断反传得到的self.q_target来计算loss,并没有使用feed_dict。这样可以节约代码的结构,简单清晰明了。

    在tensorlfow计算图中,要给那些op注入数据后成为常量tensor才能进行计算。第一种方法中placeholder输入的本身就是计算好了的q_target,也就是说我们通过feed_dict,将对target net进行计算得到的一个q_target,Tensor传入placeholder中,当做常量来对待,我们可以把一次计算(eval/run)看作是一次截图,得到当时各个op的值。这样的话,我们对于eval net中loss的反传就不会影响到target net了。 第二种方法中直接拿target net中的q_target这个op来计算eval net中的loss显然是不妥的,因为我们对loss进行反传时将会影响到target net,这不是我们想看到的结果。因为我们要保持target_net的参数不能实时更新,所以,这里引入stop_gradient来对从loss到target net的反传进行截断,换句话说,通过self.q_target = tf.stop_gradient(q_target),将原本为TensorFlow计算图中的一个op(节点)转为一个常量self.q_target,这时候对于loss的求导反传就不会传到target net去了。 这两种方法要实现的效果是一样的。

    参考链接https://blog.csdn.net/u013745804/article/details/79589514,感谢这位博主解释了这个我困惑了很久的问题。

    展开全文
  • 阅读莫凡python中的DQN代码遇到的基础知识障碍的解决 tf.get_collection() tf.add_to_collection:把变量放入一个集合,把很多变量变成一个列表 tf.get_collection:从一个结合中取出全部变量,是一个列表 tf.add...
  • 莫烦老师,DQN代码学习笔记

    千次阅读 多人点赞 2018-05-27 09:36:13
    详情请见莫烦老师DQN主页:DQN 算法更新 (Tensorflow) - 强化学习 Reinforcement Learning | 莫烦Python莫烦老师代码(没有我繁琐注释代码直通车):MorvanZhou/Reinforcement-learning-with-tensorflow参考文献:...
  • 这篇文章我们就用代码来实现 DQN 算法 一、环境介绍 1、Gym 介绍 本算法以及以后文章要介绍的算法都会使用 由 OpenAIOpenAIOpenAI 推出的GymGymGym仿真环境, GymGymGym 是一个研究和开发强化学习相关算法的仿真平台...
  • 莫烦老师,DQN代码学习笔记(图片版)

    千次阅读 2018-05-27 09:38:56
    详情请见莫烦老师DQN主页:DQN 算法更新 (Tensorflow) - 强化学习 Reinforcement Learning | 莫烦Python 莫烦老师代码(没有我繁琐注释代码直通车):MorvanZhou/Reinforcement-learning-with-tensorflow 上一篇...
  • 强化学习算法已经有各种实现平台,譬如基于tensorflow的OpenAI Baselines,rllib,基于...实现简洁,轻巧:1500行代码搞定 模块化:多种不同API可供调用,轮子多就是好 调用方便,速度快,3秒钟实现一个PG算法 RL算...
  • # tenserboard --logdir=logs ,然后打开网页127.0.1.1:6006,可以查看定义网络的神经结构。 # 两个神经网络,结构相同,但是参数不一样。 # 走多少步再更新,可以自己定义 # target_net 是...每走一步,更新一步...

空空如也

空空如也

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

dqn代码