强化学习系列7:无模型的蒙特卡洛法

1. 无模型问题

在很多时候,我们无法得知模型信息,比如前几节的蛇棋中,我们不知道棋盘梯子的信息和骰子的信息,用数学化的方法来说,就是我们用于决策的智能体不知道状态转移概率 P P

2. 采样的方法

2.1 计算公式

当我们无法得知 P P 的时候,一个直观的想法就是使用大量的采样去进行估计。
为了方便,我们把策略迭代算法中策略评估的公式进行一下拆解:
Q = P ( R + γ V ) Q=P(R+\gamma V)
V = Π Q V=\Pi Q
Q Q 无法使用迭代的方法计算得到,因此使用蒙特卡洛法,采样得到大量的样本序列:
s 0 , a 0 , r 0 , s 1 1 , a 1 1 , r 1 1 . . . {s_0,a_0,r_0,s^1_1,a^1_1,r^1_1...}
s 0 , a 0 , r 0 , s 1 2 , a 1 2 , r 1 2 . . . {s_0,a_0,r_0,s^2_1,a^2_1,r^2_1...}
s 0 , a 0 , r 0 , s 1 3 , a 1 3 , r 1 3 . . . {s_0,a_0,r_0,s^3_1,a^3_1,r^3_1...}

s 0 , a 0 , r 0 , s 1 N , a 1 N , r 1 N . . . {s_0,a_0,r_0,s^N_1,a^N_1,r^N_1...}
状态行动集( s 0 s_0 , a 0 a_0 )的值函数可以近似为: Q = 1 N ( r 0 + γ r 1 1 + γ 2 r 2 1 . . . + r 0 + γ r 1 2 + γ 2 r 2 2 . . . + r 0 + γ r 1 N + γ 2 r 2 N + . . . ) Q=\frac{1}{N}(r_0+\gamma r^1_1+\gamma ^2r^1_2...+r_0+\gamma r^2_1+\gamma^2 r^2_2...+r_0+\gamma r^N_1+\gamma^2 r^N_2+...) 。再根据策略 Π \Pi 就可以计算出 V V 了。
具体计算时,我们是使用更新的方法计算 Q Q ,而不是等遍历完再取均值。令 q i = r 0 + γ r 1 i + γ 2 r 2 i . . . q^i = r_0+\gamma r^i_1+\gamma ^2r^i_2...
则更新公式为: Q i = Q i 1 + 1 i ( q i Q i 1 ) Q^i=Q^{i-1}+\frac{1}{i}(q^i-Q^{i-1})
可以证明: Q N = 1 N ( q 1 + . . . + q N ) Q^N=\frac{1}{N}(q^1+...+q^N)
更新公式和梯度下降法非常相像。

2.2 采样随机探索

在使用蒙特卡洛法采样的时候,我们有很多前进的方法。一方面,我们要保证行动 a a 的最优性,这样才能准确估计出 v v ;另一方面,我们要保证行动的随机性,这样我们才能访问到更多状态,避免陷入局部最优。
一种常见的策略是在采样的时候加入一定的随机性,比如 ϵ \epsilon -greedy策略,即以一定的概率采取随机行动,其他情况采取最优策略行动。

2.3 every visit和first visit

加入我们在行动的过程中,又碰到了 ( s 0 , a 0 ) (s_0,a_0) 怎么办?一种方法是将其加入 Q Q 的计算之中,称为every visit方法;另一种方法是不加入,称为first visit方法。
在有限状态的序贯决策问题中,不同时间段访问同样状态的 V V 值可能会差很大。这种情况下建议使用first visit方法,减少 q q 值的方差,加快收敛速度。

3. 代码与测试

class MonteCarlo(object):
    def __init__(self, epsilon=0.0):
        self.epsilon = epsilon

	# 一轮蒙特卡洛采样
    def monte_carlo_eval(self, agent, env):
        state = env.reset()
        episode = []
        while True:
            ac = agent.play(state, self.epsilon)
            next_state, reward, terminate, _ = env.step(ac)
            episode.append((state, ac, reward))
            state = next_state
            if terminate:
                break

        value = []
        return_val = 0
        # 逆向计算q值
        for item in reversed(episode):
            return_val = return_val * agent.gamma + item[2]
            value.append((item[0], item[1], return_val))
        # 进行更新
        for item in reversed(value):
        	if agent.value_n[item[0]][item[1]] == 0: # 这里是first visit方法;every visit把这句判断删除即可
	            agent.value_n[item[0]][item[1]] += 1 # 访问次数
	            agent.value_q[item[0]][item[1]] += (item[2] -  \
	                agent.value_q[item[0]][item[1]]) /  \
	                agent.value_n[item[0]][item[1]] 
       
    def policy_improve(self, agent):
        new_policy = np.zeros_like(agent.pi)
        for i in range(1, agent.s_len):
            new_policy[i] = np.argmax(agent.value_q[i,:])
        if np.all(np.equal(new_policy, agent.pi)):
            return False
        else:
            agent.pi = new_policy
            return True

    # monte carlo法
    def monte_carlo_opt(self, agent, env):
        for i in range(10): # 进行10轮迭代
            for j in range(100): # 每次采样100轮
                self.monte_carlo_eval(agent, env)
            self.policy_improve(agent)

对比测试结果如下:

ladders info:
{14: 65, 18: 48, 41: 68, 30: 45, 68: 41, 48: 18, 49: 89, 71: 79, 65: 14, 70: 18, 73: 41, 45: 30, 89: 49, 79: 71}
Iter 3 rounds converge
Timer PolicyIter COST:0.2772650718688965
return_pi=64
Timer MonteCarlo COST:0.22463107109069824
return_pi=30

MonteCarlo法的效果一般,主要是因为前几轮迭代的值方差很大,而我们对迭代次数进行了限制,因此较难收敛。下一节将介绍可以减小方差的时序差分法。

猜你喜欢

转载自blog.csdn.net/kittyzc/article/details/85319429