强化学习中的动态规划算法(Dynamic Programming)


动态规划的基本思想是将待求解问题分解成若干子问题,先求解子问题,然后从这些子问题的解得到原问题的解

基于动态规划的强化学习算法有两种:

  • 策略迭代(Policy Iteration):有两部分组成:策略评估(Policy Evaluation)和策略提升(Policy Improvement);使用贝尔曼方程来得到一个策略的状态价值函数
  • 价值迭代(Value Iteration):直接使用贝尔曼最优方程来进行动态规划,得到最终的最优状态价值

本blog将讨论如何使用动态规划的思想来求解马尔科夫决策过程中的最优策略

1、Cliff Walking 环境

使用策略迭代和价值迭代来求解Cliff Walking这个环境的最优策略。下图是环境的样子
在这里插入图片描述
Cliff Walking 是一个非常经典的强化学习环境,它要求一个智能体从起点出发,避开悬崖行走,最终到达目标位置。如图所示,有一个 4 * 12 的网格世界,每一个网格是一个状态,起点是左下角的状态,目标是右下角的状态。智能体在每一个状态都可以采取 4 种动作:上,下,左,右,如果采取动作后触碰到边界墙壁则状态不发生改变,否则就会相应到达下一个状态。其中有一段悬崖,智能体到达目标状态或掉入悬崖都会结束并回到起点,也就是说它们是终止状态。每走一步的奖励是-1,掉入悬崖的奖励是-100

import copy

class CliffWalkingEnv:
    """ Cliff Walking环境"""
    def __init__(self, ncol=12, nrow=4):
        self.ncol = ncol # 定义环境的宽
        self.nrow = nrow # 定义环境的高
        self.P = self.createP() # 转移矩阵P[state][action] = [(p, next_state, reward, done)],包含下一个状态和奖励

    def createP(self):
        P = [[[] for j in range(4)] for i in range(self.nrow * self.ncol)] # 初始化
        change = [[0, -1], [0, 1], [-1, 0], [1, 0]] # 4 种动作, 0:上, 1:下, 2:左, 3:右。原点(0,0)定义在左上角
        for i in range(self.nrow):
            for j in range(self.ncol):
                for a in range(4):
                    if i == self.nrow - 1 and j > 0:  # 位置在悬崖或者终点,因为无法继续交互,任何动作奖励都为0
                        P[i * self.ncol + j][a] = [(1, i * self.ncol + j, 0, True)]
                        continue
                    # 其他位置
                    next_x = min(self.ncol - 1, max(0, j + change[a][0]))
                    next_y = min(self.nrow - 1, max(0, i + change[a][1]))
                    next_state = next_y * self.ncol + next_x
                    reward = -1
                    done = False
                    if next_y == self.nrow - 1 and next_x > 0: # 下一个位置在悬崖或者终点
                        done = True
                        if next_x != self.ncol - 1: # 下一个位置在悬崖
                            reward = -100
                    P[i * self.ncol + j][a] = [(1, next_state, reward, done)]
        return P

2、策略迭代(Policy Iteration)

策略迭代是策略评估和策略提升这两个过程的不断循环交替,直至最后得到最优策略

1)策略评估(Policy Evaluation)

这一过程用来计算一个策略的状态价值函数。之前的贝尔曼期望方程:
在这里插入图片描述
其中 π ( a ∣ s ) \pi(a|s) π(as)是策略 π \pi π在状态 s 下采取动作 a 的概率。当知道奖励函数和状态转移函数的时候,可以根据下一个状态的价值来计算当前状态的价值。

根据DP的思想,可以将下一个可能状态的价值当成一个子问题,计算当前状态的价值看成当前问题;考虑所有的状态,就变成了用上一轮的状态价值函数来计算当前这一轮的状态价值函数
在这里插入图片描述

2)策略提升(Policy Improvement)

再用策略评估计算得到当前策略的状态价值函数之后,可以改进策略,这个步骤就是策略提升。

假设此时对于策略 π \pi π,已经知道了策略函数 V π V_{\pi} Vπ,也就是知道了从每一个状态 s 出发一直根据策略 π \pi π 最终得到的期望回报;如何改变策略来获得在状态 s 下更高的期望回报是一个问题。

假设在状态 s 下采取动作 a 之后的动作依旧遵循策略 π \pi π, 此时得到的期望回报其实是动作价值 Q π ( s , a ) > V π ( s ) Q_\pi(s,a)>V_\pi(s) Qπ(s,a)>Vπ(s),则说明在状态 s 下采取动作 a 会比原来的策略 π ( a ∣ s ) \pi(a|s) π(as) 得到更高的期望回报。现在假设存在一个确定性策略 π ′ \pi' π,在任意一个状态 s 下,都满足:
Q π ( s , π ′ ( s ) ) > = V π ( s ) Q_\pi(s,\pi'(s)) >= V_\pi(s) Qπ(s,π(s))>=Vπ(s)

于是在状态 s 下,就有:
V π ′ ( s ) > = V π ( s ) V_{\pi'}(s) >= V_\pi(s) Vπ(s)>=Vπ(s)

于是可以直接贪心的在每一个状态选择动作价值最大的动作:
在这里插入图片描述

3)策略迭代算法

综上所述:策略迭代算法首先对当前策略进行策略评估,得到其状态价值函数,然后该状态价值函数根据策略提升来得到一个更好的新策略,之后继续评估、提升,循环往复,直到收敛:
在这里插入图片描述
算法:
在这里插入图片描述
策略迭代python代码:

class PolicyIteration:
    """ 策略迭代 """
    def __init__(self, env, theta, gamma):
        self.env = env
        self.v = [0] * self.env.ncol * self.env.nrow  # 初始化价值为0
        self.pi = [[0.25, 0.25, 0.25, 0.25] for i in range(self.env.ncol * self.env.nrow)]  # 初始化为均匀随机策略
        self.theta = theta  # 策略评估收敛阈值
        self.gamma = gamma  # 折扣因子

    def policy_evaluation(self): # 策略评估
        cnt = 1 # 计数器
        while 1:
            max_diff = 0
            new_v = [0] * self.env.ncol * self.env.nrow
            for s in range(self.env.ncol * self.env.nrow):
                qsa_list = []
                for a in range(4):
                    qsa = 0
                    for res in self.env.P[s][a]:
                        p, next_state, r, done = res
                        qsa += p * (r + self.gamma * self.v[next_state] * (1-done))  # 本章节环境比较特殊,奖励和下一个状态有关,所以需要和状态转移概率相乘
                    qsa_list.append(self.pi[s][a] * qsa)
                new_v[s] = sum(qsa_list)  # 状态价值函数和动作价值函数之间的关系
                max_diff = max(max_diff, abs(new_v[s] - self.v[s]))
            self.v = new_v
            if max_diff < self.theta: break
            cnt += 1
        print("策略评估进行%d轮后完成" % cnt)

    def policy_improvement(self): # 策略提升
        for s in range(self.env.nrow * self.env.ncol):
            qsa_list = []
            for a in range(4):
                qsa = 0
                for res in self.env.P[s][a]:
                    p, next_state, r, done = res
                    qsa += p * (r + self.gamma * self.v[next_state] * (1-done))
                qsa_list.append(qsa)
            maxq = max(qsa_list)
            cntq = qsa_list.count(maxq)
            self.pi[s] = [1/cntq if q == maxq else 0 for q in qsa_list]  # 让相同的动作价值均分概率
        print("策略提升完成")
        return self.pi

    def policy_iteration(self): # 策略迭代
        while 1:
            self.policy_evaluation()
            old_pi = copy.deepcopy(self.pi) # 将列表进行深拷贝,方便接下来进行比较
            new_pi = self.policy_improvement()
            if old_pi == new_pi: break

为了展现最终策略,增加一个打印策略函数来打印当前策略每个状态的价值以及会采取的动作。对于打印出来的动作,^o<0表示相等的概率执行向上向左,ooo>只采取向右走的动作:

def print_agent(agent, action_meaning, disaster=[], end=[]):
    print("状态价值:")
    for i in range(agent.env.nrow):
        for j in range(agent.env.ncol):
            print('%6.6s' % ('%.3f' % agent.v[i * agent.env.ncol + j]), end=' ') # 为了输出美观,保持输出6个字符
        print()

    print("策略:")
    for i in range(agent.env.nrow):
        for j in range(agent.env.ncol):
            if (i * agent.env.ncol + j) in disaster: # 一些特殊的状态,例如Cliff Walking中的悬崖
                print('****', end=' ')
            elif (i * agent.env.ncol + j) in end: # 终点
                print('EEEE', end=' ')
            else:
                a = agent.pi[i * agent.env.ncol + j]
                pi_str = ''
                for k in range(len(action_meaning)):
                    pi_str += action_meaning[k] if a[k] > 0 else 'o'
                print(pi_str, end=' ')
        print()

执行策略迭代:

env = CliffWalkingEnv()
action_meaning = ['^', 'v', '<', '>']
theta = 0.001
gamma = 0.9
agent = PolicyIteration(env, theta, gamma)
agent.policy_iteration()
print_agent(agent, action_meaning, list(range(37, 47)), [47])

-----------------------------------------------------------------
策略评估进行60轮后完成
策略提升完成
策略评估进行72轮后完成
策略提升完成
策略评估进行44轮后完成
策略提升完成
策略评估进行12轮后完成
策略提升完成
策略评估进行1轮后完成
策略提升完成
状态价值:
-7.712 -7.458 -7.176 -6.862 -6.513 -6.126 -5.695 -5.217 -4.686 -4.095 -3.439 -2.710 
-7.458 -7.176 -6.862 -6.513 -6.126 -5.695 -5.217 -4.686 -4.095 -3.439 -2.710 -1.900 
-7.176 -6.862 -6.513 -6.126 -5.695 -5.217 -4.686 -4.095 -3.439 -2.710 -1.900 -1.000 
-7.458  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000 
策略:
ovo> ovo> ovo> ovo> ovo> ovo> ovo> ovo> ovo> ovo> ovo> ovoo 
ovo> ovo> ovo> ovo> ovo> ovo> ovo> ovo> ovo> ovo> ovo> ovoo 
ooo> ooo> ooo> ooo> ooo> ooo> ooo> ooo> ooo> ooo> ooo> ovoo 
^ooo **** **** **** **** **** **** **** **** **** **** EEEE 

3、价值迭代(Value Iteration)

价值迭代可以看成认为是一种策略评估只进行了一次更新的策略迭代

价值迭代可以看成是一个动态规划的过程,利用的是贝尔曼方程:
在这里插入图片描述
将其写成迭代更新的方式:
在这里插入图片描述

价值迭代按照以上的方式进行更新,等到 V k + 1 V_{k+1} Vk+1 V k V_k Vk一样时,就是贝尔曼最优方程的不动点,此时对应着最优状态价值函数 V ∗ V_* V,然后从中恢复出最优策略即可:
在这里插入图片描述

算法:
在这里插入图片描述
python实现代码:

class ValueIteration:
    """ 价值迭代 """
    def __init__(self, env, theta, gamma):
        self.env = env
        self.v = [0] * self.env.ncol * self.env.nrow  # 初始化价值为0
        self.theta = theta  # 价值收敛阈值
        self.gamma = gamma
        self.pi = [None for i in range(self.env.ncol * self.env.nrow)] # 价值迭代结束后得到的策略

    def value_iteration(self):
        cnt = 0
        while 1:
            max_diff = 0
            new_v = [0] * self.env.ncol * self.env.nrow
            for s in range(self.env.ncol * self.env.nrow):
                qsa_list = []
                for a in range(4):
                    qsa = 0
                    for res in self.env.P[s][a]:
                        p, next_state, r, done = res
                        qsa += p * (r + self.gamma * self.v[next_state] * (1-done))
                    qsa_list.append(qsa) # 这一行和下一行是和策略迭代的主要区别
                new_v[s] = max(qsa_list)
                max_diff = max(max_diff, abs(new_v[s] - self.v[s]))
            self.v = new_v
            if max_diff < self.theta: break
            cnt += 1
        print("价值迭代一共进行%d轮" % cnt)
        self.get_policy()

    def get_policy(self): # 根据价值函数导出一个贪心策略
        for s in range(self.env.nrow * self.env.ncol):
            qsa_list = []
            for a in range(4):
                qsa = 0
                for res in self.env.P[s][a]:
                    p, next_state, r, done = res
                    qsa += r + p * self.gamma * self.v[next_state] * (1-done)
                qsa_list.append(qsa)
            maxq = max(qsa_list)
            cntq = qsa_list.count(maxq)
            self.pi[s] = [1/cntq if q == maxq else 0 for q in qsa_list]  # 让相同的动作价值均分概率
env = CliffWalkingEnv()
action_meaning = ['^', 'v', '<', '>']
theta = 0.001
gamma = 0.9
agent = ValueIteration(env, theta, gamma)
agent.value_iteration()
print_agent(agent, action_meaning, list(range(37, 47)), [47])

-------------------------------------------------------------------
价值迭代一共进行14轮
状态价值:
-7.712 -7.458 -7.176 -6.862 -6.513 -6.126 -5.695 -5.217 -4.686 -4.095 -3.439 -2.710 
-7.458 -7.176 -6.862 -6.513 -6.126 -5.695 -5.217 -4.686 -4.095 -3.439 -2.710 -1.900 
-7.176 -6.862 -6.513 -6.126 -5.695 -5.217 -4.686 -4.095 -3.439 -2.710 -1.900 -1.000 
-7.458  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000 
策略:
ovo> ovo> ovo> ovo> ovo> ovo> ovo> ovo> ovo> ovo> ovo> ovoo 
ovo> ovo> ovo> ovo> ovo> ovo> ovo> ovo> ovo> ovo> ovo> ovoo 
ooo> ooo> ooo> ooo> ooo> ooo> ooo> ooo> ooo> ooo> ooo> ovoo 
^ooo **** **** **** **** **** **** **** **** **** **** EEEE 

4、参考文献

  • 7
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值