游戏规则:
游戏里一个玩家(player)和一个庄家(dealer)。每个回合结果可能是玩家获胜,庄家获胜,或者平手。回合开始时,玩家和庄家各有两张牌,玩家可以看到玩家的两张牌和庄家的一张牌,接着玩家可以选择是不是要更多地牌,如果要更多的牌,称为(hit),玩家可以再得到一张牌,并统计玩家手里的所有牌点数之和,其中A代表1或者11点,如果点数和大于21,则玩家输掉这一回合,庄家获胜;如果小于等于二十一,玩家可以决定是否要更多的牌,直到玩家不再要更多的牌。如果玩家在总点数小于等于21的情况下,不再要更多的牌那么其手中总点数就是最终玩家的点数。接下来庄家展示其没有显示的那张牌,并且在其点数小于17的情况下抽取更多的牌,如果庄家的总点数大于21则庄家输掉此回合,玩家获胜,如果最终庄家点数小于等于21,则比较庄家和玩家的点数,大的一方获胜。双方点数相等则平局。
0.思路:
环境部分用gym库的"Blackjack-v0"
用动态规划算法的策略迭代。
1.模拟环境并用随机策略进行游戏
import gym
import numpy as np
import matplotlib.pyplot as plt
env=gym.make("Blackjack-v0")
observation=env.reset()
print("观测={}".format(observation))
while True:
print("玩家={},庄家={},".format(env.player,env.dealer))
action = np.random.choice(env.action_space.n)
print("动作={}".format(action))
observation,reward,done,_=env.step(action)
print("观测={},奖励={},结束指示={}".format(observation,reward,done))
if done:
break
2.同策回合更新。
回合更新策略基本思路是用蒙特卡洛方法估计动作价值函数。这个期望可以由多次实验的最终回报估计得到,当实验册数越多,越接近于其真实值。
代码中,policy是一个维数为(22,11,2,2)的数组,第一位表示玩家点数和,第二位表示庄家可见的点数,第三位表示玩家计算点数时是否将A看作11,第四位表示玩家动作,是否继续要牌。
例如
print(policy[21,1,1,1])
print(policy[21,1,1,0])
在训练之后前者应该接近于0,后者应该接近于1,即在玩家手里点数是21,庄家可见点数是1,把玩家手中A看作1的情况下,接着要牌(1)的概率小,而停止要牌(0)的概率大。
事实上env.step(action)的第0个返回值就是policy的前三项,只不过第三项为bool 类型
while True:
state_action.append((state,action))
observation,reward,done,_=env.step(action)
if done:
break
state=ob2state(observation)
action=np.random.choice(env.action_space.n,p=policy[state])
g=reward
for state,action in state_action:
c[state][action]+=1
q[state][action]+=(g-q[state][action]/c[state][action])
a=q[state].argmax()
policy[state]=0.
policy[state][a]=1
3.柔性同侧回合更新
上面实现的是确定性的策略,执行动作的概率不是0就是1。接下来介绍一种不确定性的策略一
ε-soft policy和ε-greedy policy。
即在一个状态s下每个动作的概率都是 ε/n(n是此状态下可以执行的动作的数量)的前提下,给最优的动作的概率加上1-ε,和为1-ε+ε/n
def monte_carlo_with_soft(env,episode_num=500000,epsilon=0.1):
policy=np.ones((22,11,2,2))*0.5
q=np.zeros_like(policy)
c=np.zeros_like(policy)
for _ in range(episode_num):
state_action=[]
observation=env.reset()
while True:
state=ob2state(observation)
action=np.random.choice(env.action_space.n,p=policy[state])
state_action.append((state,action))
observation,reward,done,_=env.step(action)
if done:
break
g=reward
for state,action in state_action:
c[state][action]+=1.
q[state][action]+=(g-q[state][action]/c[state][action])
a=q[state].argmax()
policy[state]=epsilon/2
policy[state][a]+=(1.-epsilon)
return policy,q
参考书目:
肖智清 《强化学习原理与python实现》