嗨各位,最近花了一些时间学习机器学习相关主题,想说来用python设计一个会玩21点的
机器人好了,以下会简单介绍我使用的游戏环境,深度神经网络,和相关成果。
一、游戏环境:openAI的gym,简单程式码如下
https://imgur.com/wCFHI5t
可以看到要在python搭建Blackjack环境其实非常简单,import完gym之后用gym.make就能
搭建各种游戏环境。
二、规则:
而我们这个游戏是我们要担任player的角色和庄家(dealer)对赌,我们起始会有两张
手牌,庄家则会有一张。点数方面2~10都以牌面作为点数,J,Q,K都算10点,ace可算1或1
1?
我们只有两种动作可以选择:拿牌(hit)或不拿(stand)。
目标是让手牌点数和最靠近21点,若超过21点(爆牌)则直接判定输了。
在我们决定不拿牌后,换庄家开始拿牌,庄家若手牌和低于17点,必须继续要牌。
最后比谁的牌点数和最靠近21谁就赢了。
后续我还有改写gym的环境让我们可以做双倍加注(double),让玩家可以采取
更多策略,更符合实际情况。
总之这张图的重点在env.step(action)那行,简单说就是
我们随机采取了一个action(0代表stand, 1代表hit)然后observation就是游戏当前状态
,会回传一组数字:(我方当前手牌和, 庄家手牌, 我方是否有ace),那reward就是强化
学习当中很重要的机制,可以让我们透过这种奖惩机制去训练模型,-1代表输了;0代表
我们还没爆牌或是平手;1代表我们赢了。done回传True或False表示当局结束与否。
在完全采取随机action的情况下玩10万场的胜率约27%。
三、训练方法:Deep Q Learning
我是用keras搭建神经网络的,因为比tensorflow简洁直观。附上我使用的超参。
https://imgur.com/Fbv8Nwp
我搭建了三层fully connected layer,activation function都用relu没问题,
但output layer的activation function就不能用relu了,因为在DQN中我们的input
是游戏的state也就是我们上面提到的observation,我们的output则是在碰到这样的
state,我们“采取各个动作预期的reward们”
打个比方:我们可能input [11, 6, 0],表示我们手牌和是11,庄家拿6,我们没ace。
这个神经网络的output应该要像这样 [-0.4 0.2],其中-0.4表示采取stand预期的
reward,0.2表示采取hit预期的reward,那我们当然希望reward越大越好所以就采取
hit这个动作。
因此这个output通常是有正有负的,不能用relu,那就用linear吧。
再简单提一下memory,是我们储存各个observation跟action的地方,在训练的时候
会从中随机抽样用TD的方法去不断更新我们的神经网络。
四、基本版训练成果:
https://imgur.com/9l0Akrl
靠左的数字是我们的手牌,上面的数字是庄家的手牌,H(hit拿牌)或S(stand不拿)
则是这个神经网络打算采取的策略,会有两段是因为上面那段是手上没有ace的情况
,下面是有ace的。
可以看到这个神经网络已经可以做出和人类非常相近的决策:在大牌选择stand,
小牌选择hit,并在手上有ace的时候更积极hit。而在这样策略下胜率提升到42%,
败率49%。
也就是说平均每下注一百元会输掉七元,虽然相较完全随机玩有显著的进步,但还有
进步空间,因此我决定加入其他战略(double)让玩家可以选择双倍下注,看能不能
让机器人有更聪明的表现。
五、加入double战略
由于openAI的原始程式码并没有double的功能因此我自己改写了一下他们的程式码,
简单来说现在玩家可以选择在第一手要不要double,要是double的话,玩家只能再拿
一张牌,然后庄家开始要牌。
神经网络方面我一样用DQN下去train,只是在output的时候变成输出三种动作的
期望reward。除此之外我参考了周莫烦的code和prioritised replay的paper实作了
优先回放,其中有用到sumtree的资料结构。也加入了doubleDQN的训练方法。
六、double版训练成果
在看这个神经网络的决策之前我们先看一下我们人类推算出的最佳21点战略表。
https://imgur.com/15vKVBF
然后这是我们的神经网络在面对各种情况所下的决策表。
https://imgur.com/eKQzWDW
可以看到明显它学习到在我们握有9, 10, 11且庄家是weak hand的时候可以进行
double,还有在手上有ace的时候该如何double等等。虽然不尽完美,但在我用这个
神经网络下去玩一百万场的结果平均每一百元大约输2.X元左右,如果真的要赢钱,
可能还要加上split的战略和算牌了。
七、结语
其实发文也同时是记录自己的学习笔记和找到同样对RL也有兴趣的朋友、前辈一起
讨论啦,感谢大家耐心看完。要是对我的程式码有兴趣欢迎去我的github按个星https://
github.com/blue0107/DQN-blackjack-pokerbot,欢迎大家讨论
指教啦!