使用增强学习技术为游戏Flappy Bird构建AI机器人
概述:
Flappy Bird是一款非常受欢迎和让人上瘾的游戏,它的原则挑战是让小鸟在空隙中不断闪避。
学习玩游戏是当今人工智能研究的热门话题之一。使用游戏理论、搜索算法等来解决这些问题需要特定领域的特征定义,使得他们反对可扩展性。这里的目标是开发一个更通用的框架来学习游戏特定的特性并解决问题。
这个项目的目标是探索简单的Q-learning技术和Deep Q-Network (DQN),为游戏建立一个学习的代理,让它能够在尽可能长的时间内发挥作用。
强化学习:
人工智能的一个方面,在这个方面,一个代理通过执行动作和从相应的奖励中学习来学习如何在一个环境中行为。
强化学习与标准监督学习的不同之处在于,不需要给出正确的输入/输出对,并且不需要明确地修正次最优动作。相反,重点是顺序决策。这意味着,给定当前输入,你做出一个决定,下一个输入取决于你的这个决定。在有监督的学习中,你做出的决定要么是在批量设置中,要么是在在线设置中,不影响你在未来看到的内容。
在继续之前,您可能需要阅读Wikipedia关于Markov决策过程(MDP)的内容。
Q-Learning:
它是一种技术,它根据一个行动价值函数来评估应该采取什么行动,它决定了处于某种状态的价值,并在那个状态下采取某种行动。
在开始学习之前, Q被初始化为一个可能的任意固定值(由程序员选择)。然后,在每个时间 t,代理选择一个动作 a_t,观察奖励 r_t,进入一个新的状态 s_t + 1(这可能取决于之前的状态 s_t和所选择的动作),并且 Q被更新。算法的核心是一个简单的 值迭代更新,使用旧值和新信息的加权平均值。
在我们的上下文中,可以使用3个独立参数来定义每个状态:
鸟距管道的水平距离
鸟距管子的垂直距离
鸟的垂直速度
只有两种可能的合法行为Flap or Not.。
直观奖励:
崩溃 - 负面(苛刻)
生存 - 极少积极
穿过管道 - 积极
目标:最大化长期奖励
Deep Q-Network (DQN):
这种学习算法最初是由Google DeepMind开发的AtARI 2600视频游戏(2015)。他们演示了一个基于人工智能的模型如何通过观察屏幕像素并在游戏得分递增时接收奖励来学会玩这些游戏。结果是非常显著的,并证明这样的算法是通用的足以玩各种游戏。
这里,Q函数可以用神经网络来表示(因为它对于高度结构化数据的学习特性特别好),它以状态(最后四个游戏屏幕堆叠)和动作作为输入,返回相应的Q值。这种方法的优点是,如果我们想要执行Q值更新或选择具有最高Q值的动作,我们只需要做一次向前通过网络,并立即获得所有动作的所有Q值。
经验回放:这可以在DQNs的背景下创造出最重要的诀窍,让网络以更稳定的学习收敛。由于游戏的随机性,使用非线性函数(神经网络)近似Q值不是非常稳定的。经验重放可以理解为某种特定大小的固定存储器(超参数),从中随机采样小批量作为神经网络的输入。这有助于打破连续训练样本的相似性,确保某种多样性,有助于将网络驱动到局部最小值。此外,该重放存储器也使得训练过程类似于监督学习,并且在某种意义上简化了算法的调试和测试。
探索与开发:
这是RL中使用的一种生产力策略,用于决定利用目前学习模型所花费的迭代次数,还是探索其他新的可能性,以确保融合到全局最优而非本地最优化。
代理倾向于进行探索,尝试各种行动并观察这些行为的回报。随着Q函数收敛,它返回更一致的Q值,并且探索量减少。Q-learning将探索作为算法的一部分。然而,这种探索是贪婪的,它发现了第一个有效的策略。一个简单而有效的解决这个问题的方法是引入一个超参数ε,它决定了在勘探或开采(ε-贪婪政策)之间进行选择的概率。
Q-Learning :
将状态空间从管道的水平和垂直距离分散到5x5网格(更快的学习)。
在每个时间步之后,将转换存储到移动中(当前游戏的所有转换的列表)
# store the transition from previous state to current state
state = [xdist,ydist,vely]
self.moves.append([self.previous_state,self.previous_action,state,0])
self.previous_state = state
奖励状态:
Crash
Penultimate state
Last Flap
Alive
Crossing
这对应于更新Q值的主要训练函数。
# reverse the list of moves (last to first)
history = list(reversed(self.moves))
# flag corresponding to the last state
first = True
# flag corresponding to the penultimate state
second = True
#flag corresponding to the last jump before crash
jump = True
if history[0][1] < 69:
jump = False
for move in history:
[x,y,v] = move[0]
action = move[1]
[x1,y1,z1] = move[2]
reward = move[3]
# penalize last 2 states before crash
if first or second:
reward = -1000000
if first:
first = False
else:
second = False
# penalize last jump before crash
if jump and action:
reward = -1000000
jump = False
# update the Q-value
self.qvalues[x,y,v,action] = (1- self.learning_rate) * (self.qvalues[x,y,v,action]) + (self.learning_rate) * ( reward + (self.discount_factor)*max(self.qvalues[x1,y1,z1,0],self.qvalues[x1,y1,z1,1]))
self.moves = []
我们还在Q-Learning模型中实施了ε-greedy策略,在DQN的下一部分中也加入了相关的片段。
Deep Q网络:
游戏设置(更改为更快的收敛):
删除(变黑)动态彩色背景
删除了分数读数,保持每个框架简单明了
消除了声音的影响
删除了管道的底部(每个框架都是一样的,因此是冗余的)
修正了鸟的颜色(与之前的随机分配颜色相反)
图像预处理:
def process(input):
# convert the input from rgb to grey
image = skimage.color.rgb2gray(input)
# resize image to 80x80 from 288x404
image = skimage.transform.resize(image,(80,80), mode='constant')
# return image after stretching/shrinking its intensity levels
image = skimage.exposure.rescale_intensity(image,out_range=(0,255))
# scale down pixels values to (0,1)
image = image / 255.0
return image
状态:4个预处理图像的堆栈
# preprocess the image and stack to 80x80x4 pixels
image1 = process(image1)
image1 = image1.reshape(1, image1.shape[0], image1.shape[1], 1) #1x80x80x1
input_image1 = np.append(image1, input_image[:, :, :, :3], axis=3)
网络架构:
我们已经使用卷积神经网络在训练期间进行更高水平的特征学习。
输入:状态(4张图像(80x80尺寸))
model = Sequential()
model.add(Conv2D(32, (8, 8), padding='same', strides=(4, 4), input_shape=(80,80,4)))
model.add(Activation('relu'))
model.add(Conv2D(64, (4, 4), padding='same', strides=(2, 2))) model.add(Activation('relu'))
model.add(Conv2D(64, (3, 3), padding='same', strides=(1, 1))) model.add(Activation('relu'))
model.add(Flatten())
model.add(Dense(512))
model.add(Activation('relu'))
model.add(Dense(num_actions))
第一个状态被4个相同的图像从相同的帧中初始化,这是由于4个不同的图像。
这只鸟采取的行动是根据贪婪的政策,如下所示。
# get an action (epsilon greedy policy)
if random.random() <= epsilon:
action = random.randrange(num_actions)
else:
q = model.predict(input_image)
action = np.argmax(q)
ε值以线性方式下降从3000000年的0.1到0.001的步伐。
下一个(动作)返回鸟的下一个图像,提供动作。
# take selected action and get resultant state
image1, score, reward, alive = game.next(action)
预处理此图像以获得下一个状态。
注意:在不更新模型的情况下,在回放缓冲区中添加前几千(我们的例子中的3200),这样就可以确保在训练真正开始之前在重播缓冲区中进行最少的转换。
将上述转换添加到回放缓冲区,并更新模型,如下所示。
# sample a minibatch of size 32 from replay memory minibatch = random.sample(replay, 32)
s, a, r, s1, alive = zip(*minibatch)
s = np.concatenate(s)
s1 = np.concatenate(s1)
targets = model.predict(s)
targets[range(32), a] = r + discount*np.max(model.predict(s1), axis=1)*alive
loss += model.train_on_batch(s, targets)
结果与推论:
我们尝试绘制一些图,揭示了模型的健壮性。
Simple Q-Learning:
左图:
奖励:+ 2,1,- 1000
训练时间:约33小时。
平均分:185(最后100次迭代)
右图:
奖励:+5,1,-100000(更苛刻)
训练时间:约16小时。
平均分:760(最后100次迭代)
从上面的图,我们可以推断出更严厉的政策提供了更好的结果与较小的训练。
Q-Learning (ε-greedy policy):
奖励:+2,1,-1000。
训练时间,大约12小时
平均分:106(最后100次迭代)
与之前的相同奖励策略的图相比,我们可以推断出,虽然收敛速度比前一个的更快(~20000次迭代)。同时,该模型与前一个模型收敛到较低的平均值,直接矛盾的是使用上述对DQN模型的贪婪策略的动机。我们对这种比较没有答案。
Deep Q-Network:
奖励:+ 1,0,1
训练:GPU约13 hrs(约0.15M timesteps)
平均得分(训练模式):26。
一些参考文献表明,训练DQN模型的时间约为300万次,可以取得很好的效果。