当前位置: 首页 > 后端技术 > Python

尝试使用强化学习算法玩FlappyBird?

时间:2023-03-25 23:17:42 Python

原文链接https://mp.weixin.qq.com/s/nm...效果展示见:https://zhuanlan.zhihu.com/p/...原理介绍原理其实就是在这篇文章中提到过:长文本警告|使用DQN来玩吃豆子(Pacman)这个小游戏,但是今天我们就来尝试只用Q-Learning算法来代替DQN来玩经典小游戏FlappyBird。有些懒癌患者可能不想点击上面的超链接阅读这么长的文章,所以这里简单介绍一下Q-Learning算法,然后说说如何使用这个算法玩FlappyBird游戏以及我们的代码实现。1、Q-Learning算法直接说算法好像有点尴尬,先举一个网上的经典例子:假设你需要创建一个智能体,他需要捡起右上角的蓝宝石,并且agent一次可以移动一个方块如果agent踩到陷阱就会死,那么怎么设计策略让agent学会捡起右上角的蓝宝石而不死因为途中踩到陷阱?更直观的解决方案是创建一个如下表:即对于当前状态S(即agent现在处于哪个方格),我们可以计算每个动作A的最大值(即移动上、下、左、右)代理。未来期望奖励R以便知道每个状态应该采取的最佳行动。把上表做成这样,就是我们熟悉的Q-table(Q代表动作的质量):那么我们如何获取Q-table中的值呢?这时候,Q-Learning算法就需要登场了。算法的基本流程如下:Q-Learning的思想是基于价值迭代,直观理解为每次用新得到的奖励和原来的Q值来更新当前的Q值。其数学形式表示为:其中Q为当前表,Q*为更新后的表,r为在状态s采取动作a后获得的奖励,α可看作学习率,γ一般称为折扣因子,用来定义未来奖励的重要性。max{Q(s',a')}用于计算执行动作a后进入新状态s'时所能获得的最大奖励。至于这个公式是怎么来的,可以参考:长文预警|用DQN玩吃豆人(Pacman)小游戏,感觉继续玩还是自己跳着看比较好。2.FlappyBird游戏实现学习了Q-learning算法之后,我们需要先实现我们的FlappyBird游戏,然后考虑如何在这个游戏中使用算法?很明显,我们之前写过这样的游戏:重做之前的flappybird~当然为了方便后面算法的实现,我们对游戏做了一些小改动,就是改变了小鸟移动的速度上下四舍五入(即速度每次增加或减少1,假设每一帧的时间为1,仍然假设小鸟在这一帧的速度保持不变)。3.如何用Q-Learning玩FlappyBird?其实很简单。你只需要明确状态、动作和奖励,然后将其应用到算法中。对于状态,我们假设小鸟的当前状态定义为:s=(delta_x,delta_y,speed)--delta_x:小鸟与将通过它的一组管道的下部之间的水平距离--delta_y:小鸟和将要穿过它的管道组下部之间的垂直距离--speed:小鸟的当前速度方法,没关系。动作无非就是这样:a=1,飞起来a=0,如果没有奖励,可以这样:reward=1,什么都没发生,reward=-1000000,小鸟死了,reward=5,小鸟顺利通过了一组反正只要管线合理,应该还不错。接下来的事情就是套算法了,具体而言,其核心代码实际如下:'''qlearningagent'''classQLearningAgent():def__init__(self,mode,**kwargs):self.mode=mode#learningrateself.learning_rate=0.7#discountfactor(也叫discountrate)self.discount_factor=0.95#存储必要的历史数据,格式为[previous_state,previous_action,state,reward]self.history_storage=[]#存储q值,最后一个维度是[value_for_do_nothing,value_for_flappy]self.qvalues_storage=np.zeros((130,130,20,2))#存储每一集的分数self.scores_storage=[]#之前的状态self.previous_state=[]#0表示什么都不做,1表示松散self.previous_action=0#集数self.num_episode=0#到目前为止的最高分数self.max_score=0'''做出决定'''defact(self,delta_x、delta_y、bird_speed):如果不是self.previous_state:self.previous_state=[delta_x,delta_y,bird_speed]返回self.previous_action如果self.mode=='train':state=[delta_x,delta_y,bird_speed]self.history_storage.append([self.previous_state,self.previous_action,state,0])self.previous_state=state#根据qvalues做决定ifself.qvalues_storage[delta_x,delta_y,bird_speed][0]>=self.qvalues_storage[delta_x,delta_y,bird_speed][1]:self.previous_action=0else:self.previous_action=1returnself.previous_action'''setreward'''defsetReward(self,reward):ifself.history_storage:self.history_storage[-1][3]=reward'''在一集之后更新qvalues_storage'''defupdate(self,score,is_logging=True):self.num_episode+=1self.max_score=max(self.max_score,score)self.scores_storage.append(score)ifis_logging:print('Episode:%s,Score:%s,MaxScore:%s'%(self.num_episode,score,self.max_score))ifself.mode==‘train':history=list(reversed(self.history_storage))#惩罚崩溃前的最后num_penalization状态num_penalization=2foriteminhistory:previous_state,previous_action,state,reward=itemifnum_penalization>0:num_penalization-=1reward=-1000000x_0,y_0,z_0=previous_statex_1,y_1,z_1=stateself.qvalues_storage[x_0,y_0,z_0,previous_action]=(1-self.learning_rate)*self.qvalues_storage[x_0,y_0,z_0,previous_action]+\self.learning_rate*(reward+self.discount_factor*max(self.qvalues_storage[x_1,y_1,z_1]))self.history_storage=[]'''保存模型'''defsaveModel(self,modelpath):data={'num_episode':self.num_episode,'max_score':self.max_score,'scores_storage':self.scores_storage,'qvalues_storage':self.qvalues_storage}withopen(modelpath,'wb')asf:pickle.dump(data,f)print('[INFO]:在%s中保存检查点...'%modelpath)'''加载模型'''defloadModel(self,modelpath):print('[INFO]:从%s加载检查点...'%modelpath)withopen(modelpath,'rb')asf:data=pickle.load(f)self.num_episode=data.get('num_episode')self.qvalues_storage=data.get('qvalues_storage')OK,大功告成,完整源码见相关文档~参考文献:[1].https://blog.csdn.net/qq_30615903/article/details/80739243[2].https://www.zhihu.com/question/26408259[3].https://zhuanlan.zhihu.com/p/35724704相关文件https://github.com/CharlesPik...