当前位置: 首页 > 科技观察

Keras+OpenAI强化学习实践:DeepQ-Network

时间:2023-03-12 16:49:56 科技观察

在之前的Keras/OpenAI教程中,我们讨论了将深度学习应用于强化学习设置的基本案例,并且效果很好。想象一个完全随机的序列(系列)作为训练数据。任何两个序列都不可能高度重复,因为它们是随机生成的。然而,成功试验之间存在相同的关键特征,例如在CartPole游戏中,当杆子向右倾斜时,需要将小车推向右侧,反之亦然。因此,通过在所有这些试验数据上训练我们的神经网络,我们提取了有助于成功的常见模式,并能够消除导致其独立失败的细节。话虽如此,我们认为这次的环境比上次要困难得多,即游戏:MountainCar。1.更复杂的环境尽管看起来我们应该能够应用与上周相同的技术,但有一个关键特征使其无法实现:我们无法生成训练数据。与简单的CartPole示例不同,随机移动通常只会导致实验结果不佳(低谷)。也就是说,我们的实验结果完全一样-200。这作为训练数据用处不大。想象一下,如果无论您在考试中给出什么答案,您都会得到0%,您将如何从这些经验中学习?“MountainCar-v0”环境的随机输入不会产生任何对训练有用的输出。由于这些问题,我们必须找到一种方法来逐步改进以前的实验。为此,我们使用强化学习最基本的方法:Q-learning!2.DQN的理论背景Q-learning的本质是创建一个“虚拟表”,其中包含当前环境状态下的每一个可能的动作。奖励多少。详细说明:可以将网络想象成内置了一个电子表格,其中包含在给定当前环境状态的情况下可以采取的每项可能行动的价值。“虚拟表”是什么意思?想象一下,对于输入空间中的每个可能的动作,您可以为每个可能的动作分配一个分数。如果这可行,那么您就可以非常轻松地“打败”环境:只需选择得分最高的动作即可!但有2点需要注意:第一,这个分数通常被称为“Q-score”,这个算法也是以此命名开发的。其次,与任何其他分数一样,这些Q分数在它们模拟的情况之外没有任何意义。也就是说,它们没有确定的意义,但没关系,因为我们只需要进行比较即可。为什么每个输入都需要一个虚拟表?没有统一的桌子吗?原因是不合逻辑:在底部采取什么行动是最好的,而左倾时应该做什么第一点与讨论采取什么行动是最好的是一样的。现在,我们的主要问题(为每个输入构建虚拟表)是不可能的:我们有一个连续的(***)输入空间!我们可以通过将输入空间离散化来解决这个问题,但是对于这个问题来说,这似乎是一个非常棘手的解决方案,我们以后会一次又一次地遇到这种解决方案。那么,我们如何解决呢?那就是通过将神经网络应用到这种情况:这就是DQN中D的来源!3.DQNagent现在,我们现在把问题集中在:找到一种为不同的动作分配Q-scores的方法。这是使用任何神经网络时一个非常自然的第一个问题的答案:我们模型的输入和输出是什么?在这个模型中你需要理解的数学方程式是以下方程式(别担心,我们将在下面的解释中介绍):如上所述,Q表示我们的模型在给定当前状态(s)和(a)采取的行动。但是,目标是确定状态值的总和。这意味着什么?该职位的即时奖励与未来将获得的预期奖励之和。也就是说,我们考虑到这样一个事实,即一个国家的价值往往不仅反映了它的眼前利益,还反映了它未来的利益。无论如何,我们都会对未来的奖励进行打折,因为对于同样的两种收到100的情况(一种是未来的,一种是现在的),我总是会选择当前的交易,因为未来是有变化的。伽玛因子反映了国家预期未来收益的贬值。这就是我们需要的所有数学!这是实际代码的演示!4.DQN代理将深度Q网络实现为持续学习,这意味着不是简单地积累一批实验/训练数据并将其输入模型。相反,我们从之前运行的实验中创建训练数据,并直接将运行后创建的数据提供给模型。如果现在看起来有点模糊,请不要担心,是时候查看代码了。代码主要是定义一个DQN类,所有算法逻辑都将在其中实现,我们将定义一组简单的函数来进行实际训练。1.DQN超参数首先,我们将讨论一些与DQN相关的参数。其中大部分是实现主流神经网络的标准参数:classDQN:def__init__(self,env):self.env=envself.memory=deque(maxlen=2000)self.gamma=0.95self.epsilon=1.0self.epsilon_min=0.01self.epsilon_decay=0.995self.learning_rate=0.01让我们逐步了解这些超参数。首先是环境(env),它只是为了方便在构建模型时引用矩阵的形状。“内存”是DQN的关键组成部分:如前所述,我们通过实验不断训练模型。然而,与直接训练实验的数据不同,我们首先将它们添加到内存中并随机采样。为什么要这样做,直接用***x实验数据作为样本训练不就好了吗?原因有点微妙。想象一下,我们只使用最近的实验数据进行训练:在这种情况下,我们的结果只会学习其最近的动作,这可能与未来的预测没有直接关系。特别是,在这种情况下,如果我们沿着斜坡的右侧向下移动,则对最新实验数据的训练将需要对向斜坡右侧向上移动的数据进行训练。然而,这对于坡道左侧的场景决定采取何种行动是无关紧要的。因此,通过随机抽取样本,我们将保证不偏离训练集,但理想情况下会学习我们将遇到的所有环境。我们现在讨论模型的超参数:gamma、epsilon和epsilon衰减和学习率。第一个是前面方程中讨论的未来奖励的折扣因子(<1),最后一个是标准学习率参数,我们在这里不讨论。第二个是RL的一个有趣的方面,值得一谈。在任何一种学习经历中,我们总是要在探索和开发之间做出选择。这不仅限于计算机科学或学术界:我们每天都在这样做!考虑你家附近的餐馆。您最后一次尝试新餐厅是什么时候?可能是很久以前的事了。这对应于您从探索到利用的转变:您不是试图寻找新的更好的机会,而是通过根据自己过去的经验找到最佳解决方案来最大化效用。与您刚搬家时相比:当时您不知道哪些餐厅好吃,因此很想探索新的选择。换句话说,这里有一个明确的学习趋势:在您不了解所有选项时探索它们,一旦您对其中一些选项建立了看法,就逐渐转向利用它们。同样,我们希望我们的模型能够捕捉到这种自然的学习模式,而epsilon扮演着这个角色。Epsilon代表我们将用于探索的时间的一小部分。也就是说,对于实验的分数self.epsilon,我们将简单地采取随机行动,而不是我们在这种情况下预测最好的行动。如上所述,我们希望在开始形成稳定估计之前更频繁地采取随机行动:因此在开始时将ε初始化为接近1.0,并在每个后续时间步以小于1的速率衰减它。2.DQN模型在上面DQN的初始化中,排除了一个关键环节:用于预测的实际模型!在最初的KerasRL教程中,我们直接以数值向量的形式给出了输入和输出。因此,除了全连接层之外,没有必要在网络中使用更复杂的层。具体来说,我们将模型定义为:defcreate_model(self):model=Sequential()state_shape=self.env.observation_space.shapemodel.add(Dense(24,input_dim=state_shape[0],activation="relu"))模型。添加(密集(48,activation=“relu”))model.add(密集(24,activation=“relu”))model.add(密集(self.env.action_space.n))model.compile(loss=“mean_squared_error",optimizer=Adam(lr=self.learning_rate))返回模型并用它来定义模型和目标模型(如下所述):def__init__(self,env):self.env=envself.memory=deque(maxlen=2000)self.gamma=0.95self.epsilon=1.0self.epsilon_min=0.01self.epsilon_decay=0.995self.learning_rate=0.01self.tau=.05selfself.model=self.create_model()#“hack”由DeepMind实现以提高收敛selfself.target.create_model()事实上,有两个独立的模型,一个用于进行预测,一个用于跟踪“目标值”,这是违反直觉的。具体来说,模型(self.model)的作用是对要采取的动作做出实际预测,而目标模型(self.target_model)的作用是跟踪我们希望模型采取的动作.为什么不只拥有一个兼具两者的模型呢?毕竟,如果它预测要采取的行动,那不是间接决定了我们希望模型采取的模式吗?这其实就是DeepMindOne发明的深度学习“魔术”,在DQN算法中就是用来求收敛的。如果使用单个模型,它可以(并且通常会)在像CartPole这样的简单环境中收敛。然而,它在这些更复杂的环境中不收敛的原因是我们训练模型的方式:如前所述,我们正在“即时”训练模型。因此,在每个时间步训练模型,如果我们使用单个网络,实际上也会改变每个时间步的“目标”。想想这将是多么混乱!就像,开始老师告诉你完成课本的第6页,当你完成一半时,她将其更改为第9页,当你完成一半时,她告诉你完成第21页!因此,由于缺乏利用优化器的明确方向,将导致收敛不足,即梯度变化太快而无法稳定收敛。因此,作为补偿,我们有一个变化更慢的网络来跟踪我们的最终目标,以及一个最终实现这些目标的网络。3.DQN训练训练包括三个主要步骤:记忆、学习和重定向。第一步基本上只是随着实验的进行向内存中添加数据:defremember(self,state,action,reward,new_state,done):self.memory.append([state,action,reward,new_state,done])没有这里有很多需要注意的地方,除了我们必须存储“完成”阶段,以便我们以后如何更新奖励函数。转到DQNbody的训练功能。这是使用存储的记忆并从我们过去所见中积极学习的地方。首先,从整个存储的内存中抽取样本。我们认为每个样本都是不同的。正如我们在前面的等式中看到的,我们希望将Q函数更新为当前奖励和预期未来奖励(退化为伽马)之和。在实验结束时,不会有未来的奖励,所以状态的价值是我们在这一点上收到的奖励的总和。然而,在非终端状态下,如果我们可以采取任何可能的行动,最好的奖励是什么?我们得到:defreplay(self):batch_size=32iflen(self.memory)=199:print("Failedtocompletetrial")else:print("Completedin{}trials".format(trial))break5.完整代码这是使用DQNFULL的“MountainCar-v0”环境CODE!importgymimportnumpyasnpimportrandomfromkeras.modelsimportSequentialfromkeras.layersimportDense,Dropoutfromkeras.optimizersimportAdamfromcollectionsimportdequeclassDQN:def__init__(self,env):self.env=envself.memory=deque(maxlen=2000)self.gamma=0.85self.epsilon=1.0self.epsilon_min=0.01self.epsilon_decay=0.995self.learning_rate=0.005self.tau=.125selfself.model=self.create_model()selfself.target_model=self.create_model()defcreate_model(self):model=Sequential()state_shape=self.env.observation_space.shapemodel.add(密集(24,input_dim=state_shape[0],activation="relu"))model.add(密集(48,activation="relu"))model.add(Dense(24,activation="relu"))model.add(Dense(self.env.action_space.n))model.compile(loss="mean_squared_error",optimizer=Adam(lr=self.learning_rate))returnmodeldefact(self,state):self.epsilon*=self.epsilon_decayself.epsilon=max(self.epsilon_min,self.epsilon)ifnp.random.random()