强化学习系列5:有模型的策略迭代方法

1. 策略迭代算法

这里策略迭代使用的是表格法,基本步骤是:

  • 用字典存储每个s的v值
  • 根据v值来选骰子

策略迭代的步骤为:

  1. 初始化 V V Π \Pi
  2. 进行一定次数的迭代。
    2.1 每次首先进行策略评估,不断按照 Π \Pi 更新 V V 直至收敛,计算公式为 V = Π P ( R + γ V ) V'=\Pi P(R+\gamma V) (当然也可以直接解方程进行计算,但是求逆比较费时间,一般不这么做);
    2.2 然后进行策略更新,遍历所有可能action,按照更新后的 V V 计算最优的策略,矩阵形式为 Π = arg max a P ( R + γ V ) \Pi = \arg \max_{a} P(R+\gamma V)

2. 一些额外的代码

继续使用上一节蛇棋的例子。首先定义一个智能体类,将游戏操作的一些方法进行封装。由于问题是有模型的,因此智能体知道环境env的信息。

class TableAgent(object):
    def __init__(self, env):
        self.s_len = env.observation_space.n # |S|
        self.a_len = env.action_space.n # |A|
        self.r = [env.reward(s) for s in range(0, self.s_len)] # R
        self.pi = np.array([0 for s in range(0, self.s_len)]) # π
        self.p = env.p # P
        self.value_pi = np.zeros((self.s_len)) # V
        self.value_q = np.zeros((self.s_len, self.a_len)) # Q
        self.gamma = 0.8 # γ

    def play(self, state):
        return self.pi[state]

顺便把无模型的智能体也定义一下,相比上面有模型的智能体,少了p和r两个值:

class ModelFreeAgent(object):
    def __init__(self, env):
        self.s_len = env.observation_space.n
        self.a_len = env.action_space.n
        self.pi = np.array([0 for s in range(0, self.s_len)])
        self.value_q = np.zeros((self.s_len, self.a_len))
        self.value_n = np.zeros((self.s_len, self.a_len))
        self.gamma = 0.8

    def play(self, state, epsilon = 0):
        if np.random.rand() < epsilon:
            return np.random.randint(self.a_len)
        else:
            return self.pi[state]

其次定义一个评估时间的方法:

import time
def timer(name):
    start = time.time()
    yield
    end = time.time()
    print('{} COST:{}'.format(name, end - start))

最后定义评价游戏策略效果的方法:

def eval_game(env, policy):
    state = env.reset()
    return_val = 0
    # 有两种play的方法,一种是用我们定义的智能体去玩,另一种是直接指定每个s的a。
    while True:
        if isinstance(policy, TableAgent) or isinstance(policy, ModelFreeAgent):
            act = policy.play(state)
        elif isinstance(policy, list):
            act = policy[state]
        else:
            raise Error('Illegal policy')
        state, reward, terminate, _ = env.step(act) # 不断游戏直至结束
        return_val += reward
        if terminate:
          break
    return return_val

我们先假设没有梯子,然后设计3种直接策略来测试一下这个评估函数的效果:

policy_opt = [1] * 97 + [0] * 3 # 最优策略
policy_0 = [0] * 100 # 全部都投掷第一个骰子(1~3)
policy_1 = [1] * 100 # 全部都投掷第二个骰子(1~6)
np.random.seed(0)
sum_opt = 0
sum_0 = 0
sum_1 = 0
env = SnakeEnv(0, [3, 6])
for i in range(10000):
    sum_opt += eval_game(env, policy_opt)
    sum_0 += eval_game(env, policy_0)
    sum_1 += eval_game(env, policy_1)
print('opt avg={}'.format(sum_opt / 10000.0))
print('0 avg={}'.format(sum_0 / 10000.0))
print('1 avg={}'.format(sum_1 / 10000.0))

输出结果为:

ladders info:
{}
opt avg=70.5498
0 avg=49.6654
1 avg=68.0072

3. 策略迭代方法的实现

下面按照第一节的步骤定义策略迭代:

class PolicyIteration(object):
    # 迭代计算V直至收敛
    def policy_evaluation(self, agent, max_iter = -1):
        iteration = 0
        while True:
            iteration += 1
            new_value_pi = agent.value_pi.copy()
            for i in range(1, agent.s_len): 
                value_sas = []
                ac = agent.pi[i]
                transition = agent.p[ac, i, :]
                value_sa = np.dot(transition, agent.r + agent.gamma * agent.value_pi)
                new_value_pi[i] = value_sa
            diff = np.sqrt(np.sum(np.power(agent.value_pi - new_value_pi, 2)))
            if diff < 1e-6:
                break
            else:
                agent.value_pi = new_value_pi
            if iteration == max_iter:
                break
    
    # 根据V更新π
    def policy_improvement(self, agent):
        new_policy = np.zeros_like(agent.pi)
        for i in range(1, agent.s_len):
            for j in range(0, agent.a_len):
                agent.value_q[i,j] = np.dot(agent.p[j,i,:], agent.r + agent.gamma * agent.value_pi)
            max_act = np.argmax(agent.value_q[i,:])
            new_policy[i] = max_act
        if np.all(np.equal(new_policy, agent.pi)):
            return False
        else:
            agent.pi = new_policy
            return True
	 
	 # 大框架:进行一定次数的迭代,每次先策略评估,再策略改善
    def policy_iteration(self, agent):
        iteration = 0
        while True:
            iteration += 1
            self.policy_evaluation(agent)
            ret = self.policy_improvement(agent)
            if not ret:
                break
        print('Iter {} rounds converge'.format(iteration))

我们来看一下在没有梯子的时候,这个算法的效果:

env = SnakeEnv(0, [3,6])
agent = TableAgent(env)
pi_algo = PolicyIteration()
pi_algo.policy_iteration(agent)
print('return_pi={}'.format(eval_game(env, agent)))
print(agent.pi)

输出结果为:

ladders info:
{}
Iter 2 rounds converge
return_pi=71
[0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0]

两轮迭代就收敛,获得了和最优策略一样的结果,V值也和真实的V值非常接近。

4. 一些分析

4.1 10个梯子的策略迭代法

我们首先来看一下有10个梯子时,第二节的3种策略和策略迭代方法给出策略的计算结果:

env = SnakeEnv(10, [3,6])
agent = TableAgent(env)
agent.pi[:]=0
print('0 avg={}'.format(eval_game(env,agent)))
agent.pi[:]=1
print('1 avg={}'.format(eval_game(env,agent)))
agent.pi[97:100]=0
print('opt avg={}'.format(eval_game(env,agent)))
pi_algo = PolicyIteration()
pi_algo.policy_iteration(agent)
print('pi avg={}'.format(eval_game(env,agent)))
print(agent.pi)

输出结果为:

ladders info:
{78: 38, 11: 13, 27: 25, 55: 55, 41: 59, 21: 10, 76: 74, 10: 21, 43: 35, 50: 53, 38: 78, 13: 21, 25: 27, 59: 41, 74: 76, 35: 43, 53: 50}
0 avg=62
1 avg=57
opt avg=81
Iter 3 rounds converge
pi avg=85
[0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1
 1 1 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0]

在这个案例中,policy iteration经过3轮迭代后收敛,在测试案例上的效果最好。但注意梯子是随机生成的,而且policy iteration并不一定是收敛到了全局最优,因此在不同的case下并不总是policy iteration的效果最好。

4.2 策略迭代法的V值变化分析

我们来看下状态50在策略评估阶段的V值变化情况:
在这里插入图片描述

可以大致看出一些结论:1. 值函数在最开始的迭代过程中波动非常大;2. 每轮迭代收敛时的迭代次数越来越少。

4.3 限制策略迭代的轮数

策略迭代方法的iteration是比较费时间的,我们将允许迭代的最大次数进行限制,然后看一下运行效果:

ladders info:
{42: 45, 10: 62, 34: 81, 40: 35, 38: 72, 44: 45, 48: 73, 12: 26, 60: 51, 70: 66, 45: 44, 62: 10, 81: 34, 35: 40, 72: 38, 73: 48, 26: 12, 51: 60, 66: 70}
Iter 3 rounds converge
Timer PolicyIter COST:0.26311492919921875
return_pi=75
Iter 3 rounds converge
Timer PolicyIter COST:0.15500688552856445
return_pi=81
Iter 5 rounds converge
Timer PolicyIter COST:0.06501197814941406
return_pi=77
Iter 11 rounds converge
Timer PolicyIter COST:0.04662799835205078
return_pi=88

先不管结果,光看时间的话,限制迭代次数为1时,虽然总迭代次数最多(7次),但是由于每次迭代花的策略评估时间大幅降低,因此总耗时也大幅降低了。
然后再直观看下s=50对应的值函数在每种情况下不同迭代轮数时的变化图:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/kittyzc/article/details/85295796
今日推荐