强化学习知识要点与编程实践(2)——动态规划寻找最优策略

0. 前言

本文未经许可禁止转载,如需转载请联系笔者

本章将详细讲解如何利用动态规划算法来解决强化学习中的规划问题。规划问题包含两个方面的内容,一是预测(prediction),二是控制(control),预测问题是给定策略,然后求在这个给定策略下,各个状态的价值;控制问题是不给定策略,只给定必要的环境信息(如奖励和状态转移概率等),然后求解最优策略和此策略下的最优价值函数。

动态规划是指把求解复杂问题分解为求解子问题,通过求解子问题进而得到整个问题的解。它需要求解的问题具有两个性质:一是可分解为一系列子问题,二是子问题具有重复性。

而马尔可夫决策过程恰好具备这两个性质,贝尔曼方程就决定了状态价值的可分性,而状态价值函数就存储了子问题的解,重复子问题直接通过状态价值函数即可求解。

下面来讲述如何求解预测和控制问题。

1. 策略评估

策略评估(policy evaluation)指计算给定策略下状态价值函数的过程。显然这是 预测问题 ,求解这一问题的思路是,首先随机设置初始时所有状态的价值(通常设为0),然后运用贝尔曼期望方程进行迭代(回顾贝尔曼期望方程见马尔可夫决策过程.),最后直至所有状态价值收敛,根据压缩映射理论可知,只要 γ \gamma γ介于0到1之间,一定会收敛。

以下就是贝尔曼期望方程和其备份图(Backup Diagram)
在这里插入图片描述
标记 k k k代表第 k k k次迭代,显然, k + 1 k+1 k+1次迭代中的状态价值都是从第 k k k次迭代中的状态价值计算而来。这种方法也被称为同步迭代法。如果说在等式的右边采用最新的 v k + 1 ( s ′ ) v_{k+1}(s') vk+1(s),那么这就是后面要讲的异步动态规划算法中的原位动态规划

我们来看小方格世界(Small Gridworld)中,对随机策略的评估。这里以k=3,第二行第一列的-2.4为例,讲述同步迭代法的计算过程。记第二行第一列的位置状态为 s s s,看k=2的那张图, s s s向北走,即时奖励是-1,变换后的状态价值是0; s s s向东走,即时奖励是-1,变换后的状态价值是-2; s s s向南走,即时奖励是-1,变换后的状态价值是-2; s s s向西走,即时奖励是-1,变换后的状态价值是-1.7(它本身);因此:
− 2.4 = 1 4 × ( − 1 + 0 ) + 1 4 × ( − 1 − 2 ) + 1 4 × ( − 1 − 2 ) + 1 4 × ( − 1 − 1.7 ) -2.4=\frac{1}{4}\times(-1+0)+\frac{1}{4}\times(-1-2)+\frac{1}{4}\times(-1-2)+\frac{1}{4}\times(-1-1.7) 2.4=41×(1+0)+41×(12)+41×(12)+41×(11.7)

在这里插入图片描述
在这里插入图片描述

2. 策略迭代

策略在循环迭代中得到更新改善的过程称为策略迭代(policy iteration)

策略如何改善?一种最直接的就是贪婪策略。贪婪策略就是每次agent都会走向价值最大的状态(如果存在多个价值最大,那么就随机走向其中一个),而对于那些价值非最大的,走向它的概率为0.

在第一节的Small Gridworld的右侧就显示了贪婪策略,它展示的是,每一次迭代中,贪婪策略应该是怎么样的。需要注意,下一次迭代过程中并没有采用贪婪策略,而是采用随机策略计算状态价值。

可以看到在第一节的Small Gridworld中,k=3时贪婪策略就已经是最优策略了,但是需要注意的是,最优策略并不唯一,但是最优策略们对应的最优状态价值函数必定相同

下图显示了策略迭代的过程。首先随机选取一个策略,然后计算所有状态的价值(即计算价值函数),然后采用贪婪策略更新策略,接着继续计算价值函数,接着继续更新策略,如此往复,直至价值函数不再变化(即收敛)。此时的策略必定为最优策略,状态价值函数必定为最优状态价值函数

在这里插入图片描述
上面阐述的道理很容易理解,因为状态的价值是未来可能获得的奖励的期望,每次都走期望最大的,当然最后得到的策略是最优的。接下来从数学的角度,严格地证明这件事情:

考虑一个依据确定性策略 π \pi π对任意状态 s s s产生的行为 a = π ( s ) a=\pi(s) a=π(s),贪婪策略在同样的状态 s s s下会得到新行为: a ′ = π ′ ( s ) a'=\pi'(s) a=π(s),其中:
在这里插入图片描述
假如个体在与环境交互的 下一步采取该贪婪策略产生的行为,而在 后续步骤仍采取基于原策略产生的行为,那么下面的(不)等式成立:
在这里插入图片描述
(注意角标 π \pi π代表策略 π \pi π,而括号内的 π ′ ( s ) \pi'(s) π(s)代表状态 s s s下的最优动作,只是状态 s s s转移到 s ′ s' s有区别,后面的都采用原策略 π \pi π

由于上式中的 s s s对状态集 S S S中的所有状态都成立,那么针对状态 s s s的所有后续状态均使用贪婪策略产生的行为,不等式: v π ′ ( s ) ≥ v π ( s ) v_{\pi'}(s)\geq v_{\pi}(s) vπ(s)vπ(s)将成立。这表明新策略下状态价值函数总不劣于原策略下的状态价值函数。该步的推导如下:

在这里插入图片描述

如果状态价值函数不再改善,即:
在这里插入图片描述
那么就满足贝尔曼最优方程的描述(回顾贝尔曼最优方程见马尔可夫决策过程.),即:
在这里插入图片描述
此时,对于所有状态集内的状态 s ∈ S s ∈S sS,满足: v π ( s ) = v ∗ ( s ) v_{\pi}(s)= v_{*}(s) vπ(s)=v(s),这表明此时的策略 π \pi π即为最优策略。证明完成。

总结一下,策略迭代即为贪婪策略+贝尔曼期望方程迭代求解状态价值。

3. 价值迭代

价值迭代:每次只更新状态的价值,用贝尔曼最优方程迭代计算状态价值。

表面上看,好像没有策略,但策略在计算状态价值时就隐含了,因为下一次迭代的状态价值就是取当前迭代中最优动作(行为)价值,所以这就是一种“贪婪策略”

贝尔曼最优方程如下:
在这里插入图片描述
因此价值函数的更新公式为:
在这里插入图片描述
可以看出 价值迭代 的目标仍然是寻找到一个 最优策略,它通过贝尔曼最优方程从前次迭代的价值函数中计算得到当次迭代的价值函数,在这个反复迭代的过程中,并没有一个明确的策略参与,由于使用 贝尔曼最优方程 进行价值迭代时类似于贪婪地选择了 最优行为对应的后续状态的价值,因而价值迭代其实等效于策略迭代中 每迭代一次价值函数就更新一次策略的过程。需要注意的是,在纯粹的价值迭代寻找最优策略的过程中,迭代过程中产生的状态价值函数不一定对应一个策略。

总结对比一下三种算法:策略评估,策略迭代,价值迭代,如下表所示:
在这里插入图片描述

4. 异步动态规划算法

前文所述的系列算法均为 同步动态规划算法,它表示所有的状态更新是同步的。与之对应的还有异步动态规划算法。在这些算法中,每一次迭代并不对所有状态的价值进行更新,而是依据一定的原则有选择性的更新部分状态的价值,这种算法能显著的节约计算资源,并且只要所有状态能够得到持续的被访问更新,那么也能确保算法收敛至最优解。比较常用的异步动态规划思想有︰原位动态规划优先级动态规划、和实时动态规划等。下文将简要叙述各类异步动态规划算法的特点。

原位动态规划(in-place dynamic programming):与同步动态规划算法通常对状态价值保留一个额外备份不同,原位动态规划则直接利用当前状态的后续状态的价值来更新当前状态的价值。

优先级动态规划(prioritised sweeping)︰该算法对每一个状态进行优先级分级,优先级越高的状态其状态价值优先得到更新。通常使用贝尔曼误差来评估状态的优先级,贝尔曼误差被表示为新状态价值与前次计算得到的状态价值差的绝对值。直观地说,如果一个状态价值在更新时变化特别大,那么该状态下次将得到较高的优先级再次更新。这种算法可以通过维护一个优先级队列来较轻松的实现。

实时动态规划(real-time dynamic programming):实时动态规划直接使用个体与环境交互产生的实际经历来更新状态价值,对于那些个体实际经历过的状态进行价值更新。这样个体经常访问过的状态将得到较高频次的价值更新,而与个体关系不密切、个体较少访问到的状态其价值得到更新的机会就较少。

动态规划算法使用 全宽度(full-width)的回溯机制 来进行状态价值的更新,也就是说,无论是同步还是异步动态规划,在每一次回溯更新某一个状态的价值时,都要追溯到该状态的所有可能的后续状态,并结合已知的马尔科夫决策过程定义的状态转换矩阵和奖励来更新该状态的价值。这种全宽度的价值更新方式对于状态数在百万级别及以下的中等规模的马尔科夫决策问题还是比较有效的,但是当问题规模继续变大时,动态规划算法将会因 贝尔曼维度灾难 而无法使用,每一次的状态回溯更新都要消耗非常昂贵的计算资源。为此需要寻找其他有效的算法,这就是后文将要介绍的 采样回溯。这类算法的一大特点是不需要知道马尔科夫决策过程的定义,也就是不需要了解状态转移概率矩阵以及奖励函数,而是使用采样产生的奖励和状态转移概率。这类算法通过采样避免了维度灾难,其回溯的计算时间消耗是常数级的。由于这类算法具有非常可观的优势,在解决大规模实际问题时得到了广泛的应用。

5. 编程实践

在本节的编程实践中,笔者将结合4*4小型方格世界环境使用动态规划算法进行策略评估、策略迭代和价值迭代,以此加深对相关概念的理解。

接下来分析下图所示的Small Gridworld:
在这里插入图片描述
与前一章马尔可夫决策过程不同的是,这里的奖励函数和状态转移概率都应该是python函数形式,而不是字典形式。为了比较贪婪策略和均一化随机策略,需要将这两个策略都写成函数形式,且函数接口统一,这样方便调用。

这里首先给出程序运行结果,如下所示:

在这里插入图片描述
可以看到均一化策略的求解结果与David的PPT完全一致,而贪婪策略下的策略评估,与策略迭代,价值迭代的结果完全一致,这与理论也是相符的,因为他们都是最优策略和最优价值函数。最后也输出了策略。

python代码如下:



def dynamics(s, a): # 环境动力学
    '''模拟小型方格世界的环境动力学特性
    Args:
    s 当前状态 int 0-15
    a 行为 str in [ 'n' , 'e' ,'s ' , 'w']分别表示北、东、南、西

    Returns: tuple (s_prime, reward, is_end)
    s _prime 后续状态
    reward 奖励值
    is_end 是否进入终止状态
    '''
    s_prime = s
    if (s%4 == 0 and a == "w") or (s<4 and a == "n") \
        or ((s+1)%4 == 0 and a == "e") or (s > 11 and a == "s")\
        or s in [0, 15]:
        pass
    else:
        ds = ds_actions[a]
        s_prime = s + ds
    reward = 0 if s in [0, 15] else -1  #s=0或15时,reward=0,否则为-1
    is_end = True if s in [0, 15] else False
    return s_prime, reward, is_end

def P(s, a, s1): #状态转移概率函数
    s_prime, _, _ = dynamics(s, a)
    return s1 == s_prime

def R(s, a): #奖励函数
    _, r, _ = dynamics(s, a)
    return r






# 均一化策略
def uniform_random_pi(MDP = None, V = None, s = None, a = None):
    _, A, _, _, _ = MDP
    n = len(A)
    return 0 if n == 0 else 1.0/n


# 贪婪策略
def greedy_pi(MDP, V, s, a): 
    S, A, P, R, gamma = MDP
    max_v, a_max_v = -float('inf'), []

    for a_opt in A: # 统计后续状态的最大价值以及到达到达该状态的行为(可能不止一个)
        s_prime, reward, _ = dynamics(s, a_opt)
        v_s_prime = get_value(V, s_prime)
        if v_s_prime > max_v:
            max_v = v_s_prime
            a_max_v = [a_opt]
        elif(v_s_prime == max_v):
            a_max_v.append(a_opt)
    n = len(a_max_v)
    if n == 0: return 0.0
    return 1.0/n if a in a_max_v else 0.0

def get_pi(Pi, s, a, MDP = None, V = None):
    return Pi(MDP, V, s, a)



# 辅助函数
def get_prob(P, s, a, s1): #获取状态转移概率
    return P(s, a, s1)

def get_reward(R, s, a): #获取奖励值
    return R(s, a)

def set_value(V, s, v): #设置价值字典
    V[s] = v

def get_value(V, s): #获取状态价值
    return V[s]

def display_V(V): #显示状态价值
    for i in range(16):
        print('{0:>6.2f}'.format(V[i]),end = " ")
        if (i+1) % 4 == 0:
            print("")
    print()


def compute_q(MDP, V, s, a):
    '''根据给定的MDP,价值函数V,计算  状态行为对(s,a)  的价值qsa
    '''
    S, A, R, P, gamma = MDP
    q_sa = 0
    for s_prime in S:
        q_sa += get_prob(P, s, a, s_prime) * get_value(V, s_prime) #因为这里采取一个动作后,得到的状态s'是确定,因此get_prob的输出为0或1
    q_sa = get_reward(R, s,a) + gamma * q_sa
    return q_sa


def compute_v(MDP, V, Pi, s):
    '''给定MDP下依据某一策略Pi和当前状态价值函数V计算某状态s的价值
    '''
    S, A, R, P, gamma = MDP
    v_s = 0
    for a in A:
        v_s += get_pi(Pi, s, a, MDP, V) * compute_q(MDP, V, s, a)
    return v_s

def update_V(MDP, V, Pi):
    '''给定一个MDP和一个策略,更新该策略下的价值函数V
    '''
    S, _, _, _, _ = MDP
    V_prime = V.copy()
    for s in S:
        set_value(V_prime, s, compute_v(MDP, V_prime, Pi, s))
    return V_prime

def policy_evaluate(MDP, V, Pi, n):
    '''使用n次迭代计算来评估一个MDP在给定策略Pi下的状态价值,初始时价值为V
    '''
    for i in range(n):
        V = update_V(MDP, V, Pi)
    return V

def policy_iterate(MDP, V, Pi, n, m):
    for i in range(m):
        V = policy_evaluate(MDP, V, Pi, n)
        Pi = greedy_pi # 第一次迭代产生新的价值函数后随机使用贪婪策略
    return V

# 价值迭代得到最优状态价值过程
def compute_v_from_max_q(MDP, V, s):
    '''根据一个状态的下所有可能的行为价值中最大一个来确定当前状态价值
    '''
    S, A, R, P, gamma = MDP
    v_s = -float('inf')
    for a in A:
        qsa = compute_q(MDP, V, s, a)
        if qsa >= v_s:
            v_s = qsa
    return v_s

def update_V_without_pi(MDP, V):
    '''在不依赖策略的情况下直接通过后续状态的价值来更新状态价值
    '''
    S, _, _, _, _ = MDP
    V_prime = V.copy()
    for s in S:
        set_value(V_prime, s, compute_v_from_max_q(MDP, V_prime, s))
    return V_prime

def value_iterate(MDP, V, n):
    '''价值迭代
    '''
    for i in range(n):
        V = update_V_without_pi(MDP, V)
    return V




S = [i for i in range(16)] # 状态空间
A = ["n", "e", "s", "w"] # 行为空间
# P,R,将由dynamics函数动态生成
ds_actions = {"n": -4, "e": 1, "s": 4, "w": -1} # 行为对状态的改变
gamma = 1.00
MDP = S, A, R, P, gamma


# 策略评估
print("-"*8 + " Policy Evaluation " + "-"*8)
print("uniform method")
V = [0 for _ in range(16)] # 初始化状态价值
V_pi = policy_evaluate(MDP, V, uniform_random_pi, 100)
display_V(V_pi)

print("greedy method")
V = [0 for _ in range(16)] # 初始化状态价值
V_pi = policy_evaluate(MDP, V, greedy_pi, 100)
display_V(V_pi)


# 策略迭代
print("-"*8 + " Policy Iteration " + "-"*8)
V = [0 for _ in range(16)] # 初始化状态价值
V_pi = policy_iterate(MDP, V, greedy_pi, 1, 100)
display_V(V_pi)


# 价值迭代
print("-"*8 + " Value Iteration " + "-"*8)
V_star = value_iterate(MDP, V, 4)
display_V(V_star)



# 观察最优状态下对应的最优策略
def greedy_policy(MDP, V, s):
    S, A, P, R, gamma = MDP
    max_v, a_max_v = -float('inf'), []
    for a_opt in A:# 统计后续状态的最大价值以及到达到达该状态的行为(可能不止一个)
        s_prime, reward, _ = dynamics(s, a_opt)
        v_s_prime = get_value(V, s_prime)
        if v_s_prime > max_v:
            max_v = v_s_prime
            a_max_v = a_opt
        elif(v_s_prime == max_v):
            a_max_v += a_opt
    return str(a_max_v)

def display_policy(policy, MDP, V):
    S, A, P, R, gamma = MDP
    for i in range(16):
        print('{0:^6}'.format(policy(MDP, V, S[i])),end = " ")
        if (i+1) % 4 == 0:
            print("")
    print()


print("-"*8 + " The Optimal Policy Solved by Greedy Method " + "-"*8)
display_policy(greedy_policy, MDP, V_star)

参考文献:

  1. David Silver强化学习视频.
  2. 叶强《强化学习入门——从原理到实践》
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值