强化学习Q_Learning
Q_learning是一个基于值的强化学习算法,利用 Q 函数寻找最优的「动作—选择」策略。强化学习是机器学习的一个分支,是指在某个环境下,一个个体通过和环境的互动,而不断改进他行为的方法。
最常见的强化学习的例子就是我们经常玩的游戏,比如贪吃蛇游戏,在这个游戏中,
输入的内容是:
状态(States)
=环境,贪吃蛇的蛇头的位置,食物的位置;
动作(Actions)
=任何可以执行的操作,上下左右的移动;
奖励(Rewards)
=每个动作得到的奖励,吃掉食物得到的分数,蛇死掉扣掉的分数等;
输出的内容:
方案(Policy)
=在当前状态下,应该选哪个行动。它是一个状态到一个行动的函数。(S,A,R)是用户输入的,P是函数生成的。
以上4个元素通过tuple方法定义结构,tuple(S,A,R,P) 构成了强化学习系统。
Q_learning 的目的就是最大化Q函数的值(给定一个状态和动作时的未来奖励期望),贪吃蛇走怎么样的路线,才能得到最高的分数。
对于在每个状态下的每个动作产生的结果(得到的分数),我们可以用下面的表
这里,我们引入一个路径规划的概念: Bellman condition, 这个概念的中心思想是说:如果从最佳选择的路径的末端截除一小部分,余下的路径仍然是最佳路径。举例:有个最优路径经过了ABCDE五个点,那么BCDE路径肯定是最优的。所以,我们想获得最优的路径,只需要获得每个分割成的更短路径的最优解。上图是最简单的Q_table的例子, Q-table是Q-learning的核心。它是一个表格,每一列代表一个动作,每一行表示一个状态。则每个格子的值就是此状态下采取此动作获得的最大长期奖励期望。通过此,就可以知道每一步的最佳动作是什么。
将这个概念用到寻找最佳路径上称作temporal difference learning。
为便于计算,将Q-Table表示为Bellman递推等式,拆分为当前回报和未来最大回报的和,即,其中表示状态在行为作用下的下一状态,而为状态后所有可能的行为,为价值累积过程中的打折系数,决定了未来回报相对于当前回报的重要程度。
在训练过程中,初始为0,训练中每行动一次,通过Bellman等式计算,优化目标是使得Agent根据Q函数执行动作能获得训练过程中的最大价值回报,即与的差异最小。
我们的训练步骤是:首先我们设置一个探索速率「epsilon」,它的值在0和1之间。一开始时候我们将它设定为1。它处于最大值,因为我们不知道 Q-table 中任何的值,所以我们需要走出随机的行动。随着训练次数的增加,我们将会进行借助前面训练得到的经验,于是我们逐渐减小「epsilon」值,做到探索和贪心的平衡。
选择了动作Action后, 并且观察输出的状态和奖励。接着我们使用Bellman方程去更新,:
Q_learning 训练过程如上图所示,推荐给大家我看到的两张有趣的图,可以更清楚的理解Q_Learning.
Deep Q-Learning深度学习
在前面介绍中,我们用矩阵来表示,但是在现实情况下,这个只是个理想状态,因为状态实在是太多。使用表格的方式根本存不下,那么怎么处理遇到的上面的问题呢?
对于如同贪吃蛇或者更复杂的场景这种情况,需要输入的state包含的信息会很多(高维),而输出的内容比较少(低维,比如上下左右)。这种情况又怎么处理呢?
Q-learning无法解决的这些问题,而被与神经网络结合的DQN完美的解决了。DQN和Q_learning相比,还有突出的几个改进:
1) DQN使用了卷积神经网络来逼近行为值函数
什么是价值函数近似呢?说起来很简单,就是如果用一个函数来表示Q(s,a)。理论上对于任意的(s,a)我们都可以由公式求出它的值函数。但是当state或action的个数过多时,分别去求每一个值函数会很慢。因此我们用函数近似的方式去估计值函数,这样,对于未在Q_Table中出现的state action也可以估计值函数。
值函数网络与贪心策略之间的联系是这样的:首先环境会给出一个state,根据值函数网络得到关于这个state的所有Q(s,a),然后利用贪心策略选择action并做出决策,环境接收到此action后会给出一个奖励Rew及下一个state。这是一个step,此时我们根据Rew去更新值函数网络的参数,接着进入下一个step。如此循环下去。
最优化一个损失函数loss function,也就是标签和网络输出的偏差,目标是让损失函数最小化。然后我们用Q_table处理巨量的有标签数据,然后通过反向传播使用梯度下降的方法来更新神经网络的参数。
2) DQN 设计了memory储存经验,并利用经验回放训练强化学习过程
由于在强化学习中,我们得到的观测数据是有顺序的,用这样的不独立数据使整个网络局限于一小块状态区域,用它们去更新神经网络的参数有很大的局限性,为了得到独立的数据, 用一个Memory来存储经历过的数据,每次更新参数的时候从Memory中随机抽取一部分的数据来用于更新,这样打破数据间的关联。
3) DQN的探索
在开始训练的时候,所有的参数都是随机的,有最高Q值的Action也是随机的,这是神经网络刚开始的探索过程。但是随着训练次数的增加,随着Q值的收敛,选择的Action会趋于一致。这时候,我们选择一个合适的概率,使一部分Action不按照最大Q值行动,也就是寻找一个好奇心和贪婪心之间的平衡。这个概率一般是从开始训练时的1逐步减少到0.1。也就是说开始训练拥有最大的好奇心,然后逐步向利用经验侧重。
4) 引入了一个target Q网络
为了解决这个问题, DQN在原来的Q网络的基础上又引入了一个target Q网络,即用来计算target的网络。它和Q网络结构一样,初始的权重也一样,只是Q网络每次迭代都会更新,而target Q网络是每隔一段时间才会更新。
下图是DQN的基本架构
DQN的基本架构
DQN的基本算法流程:
- 首先初始化Memory,定义它的容量为D;
- 初始化本地神经网络和目标神经网络,随机生成权重,本地神经网络和目标神经网络的权重相同;
- 循环遍历训练次数episode =1, 2, …, M;
- 初始化环境变量;
- 循环遍历step =1,2,…, T:
- 用贪心策略生成action
- 执行action 计算得到的分数,获取next state;
- 分action后是terminal和不是terminal两种情况计算reward;
- 对模型权重使用梯度下降法进行更新;
- 每经过N步,对目标模型进行更新;
- 将相应内容存入memory中(state, action, reward, next state)
利用DQN开发的贪吃蛇程序
说明:为了更快地学习和验证DQN在贪吃蛇程序中的应用,我借鉴了齐浩洋学长的源代码。为了程序的呈现效果,我把部分源程序(训练部分)重新组合了一下,并将原来的程序里的各个模块进行了一下整合。
此处,先介绍贪吃蛇训练的过程,完整的程序在后续推文中进行介绍。
贪吃蛇训练的过程(DQN实现方法)
** 注:在本例中每个批次取出的数据 self.BATCH_SIZE=64 For.. to 迭代次数 #环境初始化 # ... # 蛇,食物位置;界面大小,边界位置 # while 贪吃蛇 处于活的状态 每次循环是走一个step. # memory 中如果有足够的样本,则随机取出批次量的数据。 if self.memory.__len__() > self.BATCH_SIZE: # experiences = random.sample(self.memory, k=self.BATCH_SIZE) #提取反馈信息 取出的每个变量为长度为64的数组,18是state组合的环境因素个数. states(当前状态 tensor[64,18]), actions(动作 tensor[64,4]), rewards(分数), next_states(下一状态, tensor[64,18]), dones(是否活着) = zip(*experiences) #设置本地模型和目标模型(解决参数不收敛的问题) #使用本地模型估计下一个动作 target 为tensor(64,4) target = self.qnetwork_local.predict(states, self.BATCH_SIZE) #使用目标模型估计下一个动作。 target_val = self.qnetwork_target.predict( next_states, self.BATCH_SIZE) target_next = self.qnetwork_local.predict( next_states, self.BATCH_SIZE) #Double DQN需要从中取出有最大值的下一步做为action max_action_values = np.argmax(target_next, axis=1) #计算target 的奖励分数(或许样本的最大分数) for i in range(self.BATCH_SIZE): if dones[i]: target[i][actions[i]] = rewards[i] else: target[i][actions[i]]= rewards[i] + self.GAMMA target_val[i][max_action_values[i]] # 训练模型 对本地模型权重进行更新 self.qnetwork_local.train( states, target, batch_size=self.BATCH_SIZE) # 每训练UPDATE_EVERY次更新目标模型的权重 目标模型不是每次都进行更新,保持参数的收敛。 if self.t == self.UPDATE_EVERY: self.update_target_weights() self.t = 0 else: self.t += 1 #按照训练选择下一步 (代码3,随机走出随机的动作,使样本内容更加全面) state = state.reshape((1,)+state.shape) action_values = self.qnetwork_local.predict(state) if random.random() > epsilon: #选择最好的行动 action = np.argmax(action_values) else: #选择随机的行动 action = random.randint(0, self.nA-1) #在环境中走下一步,并且判断贪吃蛇是否触发死的条件 ...... if 死掉 break; #将经验存入memory self.memory.add(state, action, reward, next_state, done) Endfor
系统设置参数,循环进行上述训练,从环境初始到贪吃蛇死掉.为一个过程。系统训练这个过程多次,训练结果放入****.h文件中。
下图为贪吃蛇训练部分的程序运行展示:
上述就是人工智能贪吃蛇的基本入门知识,希望对大家有所帮助,后续,我还会进一步完整的分析代码,方便大家理解。