# 马尔可夫决策过程与动态规划
**马尔可夫决策过程**( **MDP** )提供了解决**强化学习**( **RL** )问题的数学框架。 几乎所有的 RL 问题都可以建模为 MDP。 MDP 被广泛用于解决各种优化问题。 在本章中,我们将了解什么是 MDP 以及如何使用它来解决 RL 问题。 我们还将学习动态编程,它是一种有效解决复杂问题的技术。
在本章中,您将学习以下主题:
* 马尔可夫链与马尔可夫过程
* 马尔可夫决策过程
* 奖励与退货
* 贝尔曼方程
* 使用动态规划求解 Bellman 方程
* 利用价值和政策迭代来解决冻湖问题
# 马尔可夫链与马尔可夫过程
在进入 MDP 之前,让我们了解马尔可夫链和马尔可夫过程,它们构成了 MDP 的基础。
马尔可夫性质指出,未来仅取决于现在而不是过去。 马尔可夫链是一个概率模型,仅依靠当前状态来预测下一个状态,而不是先前的状态,也就是说,未来有条件地独立于过去。 马尔可夫链严格遵循马尔可夫属性。
例如,如果我们知道当前状态是多云,则可以预测下一个状态可能是雨天。 我们得出的结论是,只有考虑当前状态(多云)而不考虑过去的状态(可能是晴天,大风等),下一个状态才会下雨。 但是,Markov 属性并不适用于所有进程。 例如,掷骰子(下一个状态)与前一个数字无关,无论骰子上显示的是什么(当前状态)。
从一种状态移动到另一种状态称为**过渡**,其概率称为**过渡概率**。 我们可以用表格的形式来表示转移概率,如下所示,它被称为 **Markov table** 。 在给定当前状态的情况下,它显示了移至下一个状态的概率为:
| **当前状态** | **下一个状态** | **转移概率** |
| 多云的 | 多雨的 | 0.6 |
| 多雨的 | 多雨的 | 0.2 |
| 阳光明媚 | 多云的 | 0.1 |
| 多雨的 | 阳光明媚 | 0.1 |
我们还可以以状态图的形式表示马尔可夫链,该状态图显示过渡概率:
![](img/00018.jpeg)
前面的状态图显示了从一种状态转移到另一种状态的可能性。 还是不了解马尔可夫链? 好吧,让我们谈谈。
我:“你在做什么?”
您:“我正在阅读有关马尔可夫链的信息。”
我:“看完书后你打算做什么?”
你:“我要睡觉了。”
我:“您确定要睡觉吗?”
您:“可能。如果我不困,我会看电视的。”
我:“很酷;这也是一条马尔可夫链。”
你:“嗯?”
我们可以将对话公式化为马尔可夫链,并绘制如下状态图:
![](img/00019.jpeg)
马尔可夫链位于核心概念中,即未来仅取决于现在而不是过去。 如果遵循 Markov 属性,则随机过程称为 Markov 过程。
# 马尔可夫决策过程
MDP 是马尔可夫链的延伸。 它提供了用于建模决策情况的数学框架。 几乎所有的强化学习问题都可以建模为 MDP。
MDP 由五个重要元素表示:
* 代理实际上可以处于的一组状态![](img/00020.jpeg)。
* 代理可以执行的一组动作![](img/00021.jpeg),用于从一种状态转移到另一种状态。
* 转移概率(![](img/00022.jpeg)),是通过执行某些操作![](img/00025.jpeg)从一种状态![](img/00023.jpeg)转移到另一种![](img/00024.jpeg)状态的概率。
* 奖励概率(![](img/00026.jpeg)),是代理商通过执行某些动作![](img/00029.jpeg)从一种状态![](img/00027.jpeg)转移到另一种状态![](img/00028.jpeg)所获得的奖励的概率。
* 折扣因子(![](img/00030.jpeg)),用于控制立即和将来奖励的重要性。 我们将在接下来的部分中详细讨论。
# 奖励与退货
如我们所知,在 RL 环境中,代理通过执行操作与环境交互,并从一种状态转移到另一种状态。 根据其执行的动作,它会获得奖励。 奖励不过是一个数字值,例如,一个好动作为+1,而一个坏动作为-1。 我们如何确定一个动作是好是坏? 在迷宫游戏中,好动作是指特工进行移动以使其不会撞到迷宫壁的地方,而不好的动作是指特工进行移动并撞到迷宫壁的地方。
代理试图最大化从环境而不是即时奖励中获得的奖励(累积奖励)总量。 代理商从环境中获得的总奖励金额称为回报。 因此,我们可以将代理商收到的奖励(回报)总额计算如下:
![](img/00031.jpeg)
![](img/00032.jpeg)是代理在执行步骤
![](img/00034.jpeg)从一种状态转换到另一种状态时在时间步骤![](img/00033.jpeg)时收到的奖励。 ![](img/00035.jpeg)是代理在执行从一个状态到另一状态的动作时,在
步骤![](img/00036.jpeg)时收到的奖励。 类似地,![](img/00037.jpeg)是代理在执行从一个状态到另一状态的动作时,在最后时间步骤![](img/00038.jpeg)所收到的奖励。
# 间歇性和连续性任务
情景任务是具有终端状态(结束)的任务。 在 RL 中,情节被视为从初始状态到最终状态的代理人与环境的相互作用。
例如,在赛车视频游戏中,您启动游戏(初始状态)并玩游戏直到游戏结束(最终状态)。 这称为情节。 游戏结束后,您可以通过重新启动游戏来开始下一个情节,并且无论您在上一个游戏中所处的位置如何,都将从初始状态开始。 因此,每个情节彼此独立。
在连续任务中,没有终端状态。 连续的任务永远不会结束。 例如,个人协助机器人没有终端状态。
# 折现系数
我们已经看到,代理商的目标是使回报最大化。 对于一项临时任务,我们可以将返回定义为 *R t = r t + 1 + r t + 2 +...。 + r T* ,其中 *T* 是情节的最终状态,我们尝试最大化回报 *R t* 。
由于连续任务没有任何最终状态,因此我们可以将连续任务的收益定义为 *R t = r t + 1 + r t + 2 + ....* ,总和为无穷大。 但是,如果永不止息,我们如何才能最大化回报呢?
这就是为什么我们引入折扣因子的概念。 我们可以使用折扣因子![](img/00039.jpeg)重新定义收益,如下所示:
![](img/00040.jpeg) ---(1)
![](img/00041.jpeg) ---(2)
折扣系数决定了我们对未来奖励和即时奖励的重视程度。 折扣因子的值在 *0* 至 *1* 之内。 折扣因子 *0* 意味着即时奖励更为重要,而折扣因子 *1* 意味着未来奖励比即时奖励更为重要。
折扣系数 *0* 永远不会只考虑立即获得的奖励。 同样, *1* 的折扣因子将永远学习,以寻找未来的奖励,这可能导致无限。 因此,折现因子的最佳值在 0.2 到 0.8 之间。
根据使用情况,我们重视即时奖励和将来的奖励。 在某些情况下,未来的奖励比立即的奖励更可取,反之亦然。 在国际象棋游戏中,目标是击败对手的国王。 如果我们重视即时奖励,而即时奖励是通过典当击败任何对手玩家等行动获得的,那么坐席将学会执行此子目标,而不是学习达到实际目标。 因此,在这种情况下,我们重视未来的奖励,而在某些情况下,我们更喜欢即时奖励,而不是未来的奖励。 (说,如果我今天或 13 个月后给您巧克力,您会喜欢巧克力吗?)
# 政策功能
我们已经在[第 1 章](01.html#K0RQ0-3c5bb317ad314d43ac43a332c0db6f00)和*强化学习简介*中了解了策略功能,该功能将状态映射到操作。 用π表示。
策略功能可以表示为![](img/00042.jpeg),指示从状态到动作的映射。 因此,基本上,策略功能会说明在每种状态下要执行的操作。 我们的最终目标在于找到最佳策略,该策略指定在每个状态下执行的正确操作,从而最大化回报。
# 状态值功能
状态值函数也简称为值函数。 它通过策略*π*指定代理处于特定状态的状态如何。 值函数通常由 *V(s)*表示。 它表示遵循策略的状态的值。
我们可以定义一个状态值函数,如下所示:
![](img/00043.jpeg)
这根据策略*π*指定从状态 *s* 开始的预期收益。 我们可以用(2)的值函数替换 *R t* 的值,如下所示:
![](img/00044.jpeg)
请注意,状态值函数取决于策略,并且取决于我们选择的策略而有所不同。
我们可以在表中查看值函数。 假设我们有两个状态,并且这两个状态都遵循策略π。 根据这两个状态的值,我们可以判断出执行策略后,我们的代理处于该状态有多好。 值越大,状态越好:
| **状态** | **值** |
| 状态 1 | 0.3 |
| 状态 2 | 0.9 |
根据上表,我们可以知道处于状态 2 很好,因为它具有很高的价值。 在接下来的部分中,我们将看到如何直观地估计这些值。
# 状态作用值功能(Q 功能)
状态作用值函数也称为 *Q* 函数。 它指定代理在具有策略*π*的状态下执行特定操作的效果如何。 *Q* 函数由 *Q(s)*表示。 它表示在遵循策略*π*的状态下执行操作的值。
我们可以定义 *Q* 函数,如下所示:
![](img/00045.jpeg)
这根据策略*π*指定从状态 *s* 开始的预期回报,其动作为 *a* 。 我们可以从(2)的 *Q* 函数中替换 *R t* 的值,如下所示:
![](img/00046.jpeg)
值函数和 *Q* 函数之间的区别在于,值函数指定状态的优劣,而 *Q* 函数指定状态的行为的优劣。
与状态值函数一样, *Q* 函数可以在表中查看。 也称为 *Q* 表。 让我们说我们有两个状态和两个动作。 我们的 *Q* 表如下所示:
| **状态** | **动作** | **值** |
| 状态 1 | 动作 1 | 0.03 |
| 状态 1 | 动作 2 | 0.02 |
| 状态 2 | 动作 1 | 0.5 |
| 状态 2 | 动作 2 | 0.9 |
因此, *Q* 表显示了所有可能的状态动作对的值。 因此,通过查看此表,我们可以得出结论,在状态 1 中执行动作 1 和在状态 2 中执行动作 2 是更好的选择,因为它具有很高的价值。
每当我们说值函数 *V(S)*或 *Q* 函数 *Q(S,a),*时,它实际上表示值表,而 *Q* 表,如前所示。
# Bellman 方程和最优性
以美国数学家理查德·贝尔曼(Richard Bellman)命名的贝尔曼方程式可帮助我们求解 MDP。 它在 RL 中无处不在。 当我们说解决 MDP 时,实际上意味着找到最佳的策略和价值功能。 根据不同的策略,可以有许多不同的价值函数。 与所有其他值函数相比,最优值函数![](img/00047.jpeg)是产生最大值的函数:
![](img/00048.jpeg)
类似地,最优策略是导致最优值函数的策略。
由于最优值函数![](img/00049.jpeg)是比所有其他值函数(即最大收益)更高的值的函数,因此它将是 *Q* 函数的最大值。 因此,可以通过将 *Q* 函数的最大值取如下来轻松地计算最佳值函数:
![](img/00050.jpeg)-(3)
值函数的 Bellman 方程可以表示为(在下一主题中,我们将看到如何推导该方程):
![](img/00051.jpeg)
它指示状态值及其后继状态与所有可能性的平均值之间的递归关系。
类似地,用于 *Q* 函数的 Bellman 方程可以表示为:
![](img/00052.jpeg) ---(4)
将公式(4)代入(3),我们得到:
![](img/00053.jpeg)
前面的方程式称为 Bellman 最优方程式。 在接下来的部分中,我们将看到如何通过求解该方程式找到最佳策略。
# 推导值和 Q 函数的 Bellman 方程
现在,让我们看看如何导出值和 *Q* 函数的 Bellman 方程。
如果您对数学不感兴趣,可以跳过本节。 但是,数学将非常有趣。
首先,我们将![](img/00054.jpeg)定义为在执行动作*和*时从状态![](img/00055.jpeg)转换为![](img/00056.jpeg)的转移概率:
![](img/00057.jpeg)
我们将![](img/00058.jpeg)定义为在执行动作*和*时从状态![](img/00059.jpeg)移至![](img/00060.jpeg)所获得的奖励概率:
![](img/00061.jpeg)
![](img/00062.jpeg) from(2)---(5)
我们知道值函数可以表示为:
![](img/00063.jpeg)
![](img/00064.jpeg)来自(1)
我们可以通过获取第一笔报酬来重写价值函数:
![](img/00065.jpeg) ---(6)
如果我们处于状态 *s* ,并通过策略*π*执行动作*和*,则值函数中的期望值指定了期望收益。
因此,我们可以通过总结所有可能的动作和奖励来明确地重写我们的期望,如下所示:
![](img/00066.jpeg)
在 RHS 中,我们将等式(5)中的![](img/00067.jpeg)替换为:
![](img/00068.jpeg)
同样,在 LHS 中,我们将从等式(2)中替换 *r t + 1* 的值,如下所示:
![](img/00069.jpeg)
因此,我们的最终期望方程变为:
![](img/00070.jpeg) ---(7)
现在,我们将期望值(7)替换为值函数(6),如下所示:
![](img/00071.jpeg)
代替![](img/00072.jpeg),我们可以用等式(6)代替![](img/00073.jpeg),我们的最终值函数如下所示:
![](img/00074.jpeg)
以非常相似的方式,我们可以为 *Q* 函数导出一个 Bellman 方程; 最终方程如下:
![](img/00075.jpeg)
现在,对于值函数和 *Q* 函数都有一个 Bellman 方程,我们将看到如何找到最佳策略。
# 求解 Bellman 方程
我们可以通过求解 Bellman 最优性方程来找到最优策略。 为了解决 Bellman 最优性方程,我们使用一种称为动态规划的特殊技术。
# 动态编程
**动态编程**( **DP** )是一种解决复杂问题的技术。 在 DP 中,不是一次解决一个复杂的问题,而是将问题分解为简单的子问题,然后针对每个子问题,我们计算并存储解决方案。 如果出现相同的子问题,我们将不会重新计算,而是使用已经计算的解决方案。 因此,DP 有助于极大地减少计算时间。 它的应用广泛,包括计算机科学,数学,生物信息学等。
我们使用两种强大的算法来求解 Bellman 方程:
* 价值迭代
* 策略迭代
# 价值迭代
在值迭代中,我们从随机值函数开始。 显然,随机值函数可能不是最佳函数,因此我们以迭代方式寻找新的改进值函数,直到找到最佳值函数为止。 一旦找到最优值函数,就可以轻松地从中得出最优策略:
![](img/00076.gif)
值迭代涉及的步骤如下:
1. 首先,我们初始化随机值函数,即每个状态的随机值。
2. 然后,我们为 *Q(s,a)*的所有状态动作对计算 *Q* 函数。
3. 然后,我们使用 *Q(s,a)*中的最大值更新值函数。
4. 我们重复这些步骤,直到 value 函数的变化很小。
让我们通过手动逐步执行值迭代来直观地理解它。
考虑此处显示的网格。 让我们说我们处于状态 **A** ,我们的目标是在不访问状态 **B** 的情况下达到状态 **C** ,我们有两个动作,0-左/右 和 1-上/下:
![](img/00077.gif)
您能想到这里的最佳政策吗? 此处的最佳策略是告诉我们在 **A** 状态下执行操作 1 的策略,这样我们就可以访问 **C** 而无需访问 **B** 。 我们如何找到这个最佳政策? 现在让我们看看:
初始化随机值函数,即所有状态的随机值。 让我们将 **0** 分配给所有状态:
![](img/00078.gif)
让我们计算所有状态动作对的 *Q* 值。
Q 值告诉我们每个状态下一个动作的值。 首先,让我们计算状态 *A* 的 *Q* 值。 调用 *Q* 函数的方程式。 为了进行计算,我们需要过渡和奖励概率。 让我们考虑状态 **A** 的转移和奖励概率,如下所示:
![](img/00079.gif)
状态 **A** 的 Q 函数可以计算如下:
*Q(s,a)=转移概率*(奖励概率+伽玛* next_state 的值)*
在此,*伽马*是折扣因子; 我们将其视为 *1* 。
*状态 *A* 和操作 *0* *的 Q* 值:*
![](img/00080.jpeg)
*Q(A,0)=(0.1 *(0 + 0))+(0.4 *(-1.0 + 0))+(0.3 *(1.0 + 0))*
Q(A,0)= -0.1
现在我们将计算状态 *A* 和操作 *1:*的 *Q* 值
![](img/00081.jpeg)
*Q(A,1)=(0.3 *(0 + 0))+(0.1 *(-2.0 + 0))+(0.5 *(1.0 + 0))*
*Q(A,1)= 0.3*
现在,我们将在 *Q* 表中对此进行更新,如下所示:
![](img/00082.gif)
从 *Q(s,a)*更新值函数为最大值。
如果您查看前面的 *Q* 函数,则 *Q(A,1)*的值大于 *Q(A,0)*的值,因此我们将更新该值 状态 *A* 的形式为 *Q(A,1)*:
![](img/00083.gif)
同样,我们为所有状态动作对计算 *Q* 值,并通过获取具有最高状态动作值的 *Q* 值来更新每个状态的值函数。 我们的更新值函数如下所示。 这是第一次迭代的结果:
![](img/00084.gif)
我们重复此步骤几次迭代。 也就是说,我们将步骤 *2* 重复到步骤 *3* (在每次迭代中,在计算 *Q* 值时,我们使用更新后的值函数,而不是相同的随机初始化函数) 值函数)。
这是第二次迭代的结果:
![](img/00085.gif)
这是第三次迭代的结果:
![](img/00086.gif)
但是我们什么时候停止呢? 当每次迭代之间的值变化较小时,我们将停止; 如果查看第二和第三次迭代,则值函数的变化不大。 在这种情况下,我们停止迭代并将其视为最佳值函数。
好了,既然我们已经找到了最优价值函数,那么我们如何得出最优政策呢?
这很简单。 我们用最终的最优值函数计算 *Q* 函数。 假设我们计算出的 *Q* 函数如下所示:
![](img/00087.gif)
从此 *Q* 函数中,我们选择每种状态下具有最大值的动作。 在状态 **A** 下,我们为操作 1 设置了最大值,这是我们的最佳策略。 因此,如果我们在状态 **A** 中执行动作 1,则无需访问 **B** 就可以达到 **C** 。
# 策略迭代
与值迭代不同,在策略迭代中,我们从随机策略开始,然后找到该策略的值函数。 如果价值函数不是最优的,那么我们会找到新的改进策略。 我们重复此过程,直到找到最佳策略。
策略迭代有两个步骤:
1. **策略评估**:评估随机估算策略的价值函数。
2. **策略改进**:在评估价值函数时,如果它不是最优的,我们会发现新的改进策略:
![](img/00088.gif)
策略迭代涉及的步骤如下:
1. 首先,我们初始化一些随机策略
2. 然后我们找到该随机策略的值函数,并进行评估以检查其是否最优,这称为策略评估
3. 如果不是最佳选择,我们会找到新的改进政策,称为政策改进
4. 我们重复这些步骤,直到找到最佳策略
让我们通过逐步手动执行策略迭代来直观地理解。
考虑我们在*值迭代*部分中看到的相同网格示例。 我们的目标是找到最佳策略:
1. 初始化随机策略功能。
让我们通过为每个状态指定随机动作来初始化随机策略函数:
说 *A-> 0*
*B-> 1*
*C-> 0*
2. 查找随机初始化策略的值函数。
现在,我们必须使用随机初始化的策略来查找值函数。 我们说一下计算后的值函数如下:
![](img/00089.gif)
现在,根据随机初始化的策略有了新的价值函数,让我们使用新的价值函数计算新的政策。 我们如何做到这一点? 这与我们在*值迭代*中所做的非常相似。 我们为新值函数计算 *Q* 值,然后针对具有最大值的每个状态采取措施作为新策略。
我们说新政策的结果是:
*A-> 0*
*B-> 1*
*C-> 1*
我们检查旧策略,即随机初始化的策略和新策略。 如果它们相同,则我们已经达到收敛,即找到了最佳策略。 如果没有,我们将旧策略(随机策略)更新为新策略,并从步骤 *2* 开始重复。
听起来令人困惑? 看一下伪代码:
```py
policy_iteration():
Initialize random policy
for i in no_of_iterations:
Q_value = value_function(random_policy)
new_policy = Maximum state action pair from Q value
if random_policy == new policy:
break
random_policy = new_policy
return policy
```
# 解决冻湖问题
如果您到目前为止还不了解我们所学的知识,请放心,我们将研究所有概念以及冻湖问题。
想象一下,有一个从家到办公室的冰冻湖泊。 您必须在结冰的湖面上行走才能到达办公室。 但是糟糕! 冰冻的湖面上有洞,因此您在冰冻的湖面上行走时必须小心,以免被困在洞中:
![](img/00090.gif)
看上图:
* **S** 是起始位置(原点)
* **F** 是您可以漫步的冰冻湖
* **H** 是孔,您必须非常小心
* **G** 是目标(办公室)
好的,现在让我们代替您使用我们的代理来找到到达办公室的正确方法。 该代理的目标是找到从 **S** 到 **G** 的最佳路径,而不会陷入 **H** 的陷阱。 代理商如何做到这一点? 如果代理正确地在冰冻的湖面上行走,我们给+1 分作为奖励,如果代理正确落入洞中,则给 0 分,以便代理确定正确的行动。 代理现在将尝试找到最佳策略。 最优政策意味着采取正确的道路,这将最大化代理商的报酬。 如果代理人正在使报酬最大化,则显然代理人正在学习跳过漏洞并到达目的地。
我们可以将问题建模为我们先前研究的 MDP。 MDP 包含以下内容:
* **状态**:状态集。 在这里,我们有 16 个州(网格中的每个小方框)。
* **动作**:所有可能动作的集合(左,右,上,下;这是我们的代理商在冰冻湖面环境中可以采取的所有四种可能动作)。
* **转移概率**:通过执行动作*和*从一种状态( **F** )转换为另一种状态( **H** )的概率。
* **奖励概率**:这是从一种状态( **F** )迁移到另一种状态( **H** )时获得奖励的概率。 执行*和*动作。
现在我们的目标是解决 MDP。 解决 MDP 意味着寻找最佳策略。 现在,我们介绍三个特殊功能:
* **策略功能**:指定在每种状态下要执行的操作
* **值函数**:指定状态的良好程度
* **Q 函数**:指定动作在特定状态下的状态
当我们说有多好时,这到底意味着什么? 这意味着最大化奖励是多么的好。
然后,我们使用称为 Bellman 最优性方程的特殊方程式表示值函数和 *Q* 函数。 如果我们解决这个方程,我们可以找到最佳策略。 在这里,求解方程式意味着找到正确的价值函数和策略。 如果我们找到正确的价值功能和政策,那将是我们获得最大回报的最佳途径。
我们将使用一种称为动态规划的特殊技术来求解 Bellman 最优性方程。 要应用 DP,必须预先知道模型动力学,这基本上意味着必须预先知道模型环境的转换概率和奖励概率。 由于我们知道模型动力学,因此可以在此处使用 DP。 我们使用两种特殊的 DP 算法来找到最佳策略:
* 价值迭代
* 策略迭代
# 价值迭代
简单来说,在值迭代中,我们首先将一些随机值初始化为 value 函数。 我们初始化的随机值很有可能不会达到最佳状态。 因此,我们遍历每个状态并找到新的值函数; 我们停止迭代,直到找到最佳值函数。 一旦找到最优值函数,就可以轻松地从中提取最优策略。
现在我们将看到如何使用值迭代来解决冻湖问题。
首先,我们导入必要的库:
```py
import gym
import numpy as np
```
然后,我们使用 OpenAI 的 Gym 创建冰冻的湖泊环境:
```py
env = gym.make('FrozenLake-v0')
```
我们将首先探索环境。
由于我们有一个 4 * 4 的网格,因此环境中的状态数为 16:
```py
print(env.observation_space.n)
```
环境中的操作数为四个,分别为上,下,左和右:
```py
print(env.observation_space.n)
```
现在我们定义一个`value_iteration()`函数,该函数返回最佳值函数(值表)。 我们将首先逐步了解该功能,然后再查看整个功能。
首先,我们为所有状态和迭代次数初始化随机值表`0`:
```py
value_table = np.zeros(env.observation_space.n)
no_of_iterations = 100000
```
然后,在每次迭代开始时,我们将`value_table`复制到`updated_value_table`:
```py
for i in range(no_of_iterations):
updated_value_table = np.copy(value_table)
```
现在,我们计算 Q 表,并选择具有最高值的最大状态动作对作为值表。
我们将使用之前解决的示例来理解代码。 我们在上一个示例中计算了状态 *A* 和操作 *1* 的 *Q* 值:
*Q(A,1)=(0.3 *(0 + 0))+(0.1 *(-1.0 + 0))+(0.5 +(1.0 + 0))*
*Q(A,1)= 0.5*
我们没有为每个状态创建 *Q* 表,而是创建了一个名为`Q_value`的列表,然后为该状态中的每个动作创建了一个名为`next_states_rewards`的列表,该列表存储了`Q_value` 下一个过渡状态。 然后,我们对`next_state_rewards`求和并将其附加到我们的`Q_value`中。
请看前面的示例,其中状态为 *A* ,操作为 *1* 。 *(0.3 *(0 + 0))*是过渡状态 *A* 和*(0.1 *(-1.0 + 0))*的下一个状态奖励 过渡状态 *B* 的下一状态奖励。 *(0.5 +(1.0 + 0))*是过渡状态 *C* 的下一个状态奖励。 我们将所有这些加总为`next_state_reward`,并将其附加到`Q_value`中,该值为 0.5。
当我们为状态的所有动作计算`next_state_rewards`并将其附加到 *Q* 值时,我们选取最大的 *Q* 值并将其更新为我们的状态值:
```py
for state in range(env.observation_space.n):
Q_value = []
for action in range(env.action_space.n):
next_states_rewards = []
for next_sr in env.P[state][action]:
trans_prob, next_state, reward_prob, _ = next_sr
next_states_rewards.append((trans_prob * (reward_prob + gamma * updated_value_table[next_state])))
Q_value.append(np.sum(next_states_rewards))
```
```py
#Pick up the maximum Q value and update it as value of a state
value_table[state] = max(Q_value)
```
然后,我们将检查是否已经达到收敛,即,我们的价值表和更新后的价值表之间的差异非常小。 我们怎么知道它很小? 我们定义了一个名为`threshold`的变量,然后我们看一下差异是否小于我们的`threshold`; 如果小于,则中断循环,并将值函数返回为最佳值函数:
```py
threshold = 1e-20
if (np.sum(np.fabs(updated_value_table - value_table)) <= threshold):
print ('Value-iteration converged at iteration# %d.' %(i+1))
break
```
查看`value_iteration()`的完整功能可以更好地理解:
```py
def value_iteration(env, gamma = 1.0):
value_table = np.zeros(env.observation_space.n)
no_of_iterations = 100000
threshold = 1e-20
for i in range(no_of_iterations):
updated_value_table = np.copy(value_table)
for state in range(env.observation_space.n):
Q_value = []
for action in range(env.action_space.n):
next_states_rewards = []
for next_sr in env.P[state][action]:
trans_prob, next_state, reward_prob, _ = next_sr
next_states_rewards.append((trans_prob * (reward_prob + gamma * updated_value_table[next_state])))
Q_value.append(np.sum(next_states_rewards))
value_table[state] = max(Q_value)
if (np.sum(np.fabs(updated_value_table - value_table)) <= threshold):
print ('Value-iteration converged at iteration# %d.' %(i+1))
break
return value_table, Q_value
```
因此,我们可以使用`value_iteration`导出`optimal_value_function`:
```py
optimal_value_function = value_iteration(env=env,gamma=1.0)
```
找到`optimal_value_function`后,如何从`optimal_value_function`中提取最佳策略? 我们使用最佳值操作来计算 *Q* 值,并选择每个状态中具有最高 *Q* 值的操作作为最佳策略。 我们通过称为`extract_policy()`的函数来完成此操作; 我们现在将逐步介绍这一点。
首先,我们定义随机策略; 对于所有状态,我们将其定义为`0`:
```py
policy = np.zeros(env.observation_space.n)
```
然后,对于每个状态,我们都构建一个`Q_table`,并且针对该状态中的每个动作,我们计算 *Q* 值并将其添加到我们的`Q_table`中:
```py
for state in range(env.observation_space.n):
Q_table = np.zeros(env.action_space.n)
for action in range(env.action_space.n):
for next_sr in env.P[state][action]:
trans_prob, next_state, reward_prob, _ = next_sr
Q_table[action] += (trans_prob * (reward_prob + gamma * value_table[next_state]))
```
然后,我们为`state`选择策略,将其作为具有 *Q* 最高值的操作:
```py
policy[state] = np.argmax(Q_table)
```
看一下完整的功能:
```py
def extract_policy(value_table, gamma = 1.0):
policy = np.zeros(env.observation_space.n)
for state in range(env.observation_space.n):
Q_table = np.zeros(env.action_space.n)
for action in range(env.action_space.n):
for next_sr in env.P[state][action]:
trans_prob, next_state, reward_prob, _ = next_sr
Q_table[action] += (trans_prob * (reward_prob + gamma * value_table[next_state]))
policy[state] = np.argmax(Q_table)
return policy
```
因此,我们可以得出`optimal_policy`如下:
```py
optimal_policy = extract_policy(optimal_value_function, gamma=1.0)
```
我们将获得如下输出,即`optimal_policy`,即在每种状态下要执行的操作:
```py
array([0., 3., 3., 3., 0., 0., 0., 0., 3., 1., 0., 0., 0., 2., 1., 0.])
```
完整的程序如下:
```py
import gym
import numpy as np
env = gym.make('FrozenLake-v0')
def value_iteration(env, gamma = 1.0):
value_table = np.zeros(env.observation_space.n)
no_of_iterations = 100000
threshold = 1e-20
for i in range(no_of_iterations):
updated_value_table = np.copy(value_table)
for state in range(env.observation_space.n):
Q_value = []
for action in range(env.action_space.n):
next_states_rewards = []
for next_sr in env.P[state][action]:
trans_prob, next_state, reward_prob, _ = next_sr
next_states_rewards.append((trans_prob * (reward_prob + gamma * updated_value_table[next_state])))
Q_value.append(np.sum(next_states_rewards))
value_table[state] = max(Q_value)
if (np.sum(np.fabs(updated_value_table - value_table)) <= threshold):
print ('Value-iteration converged at iteration# %d.' %(i+1))
break
return value_table
def extract_policy(value_table, gamma = 1.0):
policy = np.zeros(env.observation_space.n)
for state in range(env.observation_space.n):
Q_table = np.zeros(env.action_space.n)
for action in range(env.action_space.n):
for next_sr in env.P[state][action]:
trans_prob, next_state, reward_prob, _ = next_sr
Q_table[action] += (trans_prob * (reward_prob + gamma * value_table[next_state]))
policy[state] = np.argmax(Q_table)
return policy
optimal_value_function = value_iteration(env=env,gamma=1.0)
optimal_policy = extract_policy(optimal_value_function, gamma=1.0)
print(optimal_policy)
```
# 策略迭代
在策略迭代中,首先我们初始化一个随机策略。 然后,我们将评估初始化的随机策略:它们是否好? 但是,我们如何评估政策呢? 我们将通过计算它们的价值函数来评估我们随机初始化的策略。 如果它们不好,那么我们会找到新的政策。 我们重复此过程,直到找到好的政策。
现在让我们看看如何使用策略迭代来解决冻湖问题。
在查看策略迭代之前,我们将了解在给定策略的情况下如何计算价值函数。
我们用状态数将`value_table`初始化为零:
```py
value_table = np.zeros(env.nS)
```
然后,对于每个状态,我们从策略中获取操作,然后根据`action`和`state`计算值函数,如下所示:
```py
updated_value_table = np.copy(value_table)
for state in range(env.nS):
action = policy[state]
value_table[state] = sum([trans_prob * (reward_prob + gamma * updated_value_table[next_state])
for trans_prob, next_state, reward_prob, _ in env.P[state][action]])
```
当`value_table`和`updated_value_table`之差小于我们的`threshold`时,我们将打破这一点:
```py
threshold = 1e-10
if (np.sum((np.fabs(updated_value_table - value_table))) <= threshold):
break
```
查看以下完整功能:
```py
def compute_value_function(policy, gamma=1.0):
value_table = np.zeros(env.nS)
threshold = 1e-10
while True:
updated_value_table = np.copy(value_table)
for state in range(env.nS):
action = policy[state]
value_table[state] = sum([trans_prob * (reward_prob + gamma * updated_value_table[next_state])
for trans_prob, next_state, reward_prob, _ in env.P[state][action]])
if (np.sum((np.fabs(updated_value_table - value_table))) <= threshold):
break
return value_table
```
现在,我们将逐步了解如何执行策略迭代。
首先,我们将`random_policy`初始化为零个 NumPy 数组,其形状为状态数:
```py
random_policy = np.zeros(env.observation_space.n)
```
然后,对于每次迭代,我们根据随机策略计算`new_value_function`:
```py
new_value_function = compute_value_function(random_policy, gamma)
```
我们将使用计算出的`new_value_function`提取策略。 `extract_policy`函数与我们在值迭代中使用的函数相同:
```py
new_policy = extract_policy(new_value_function, gamma)
```
然后,我们检查是否已经达到收敛,即是否通过比较`random_policy`和新策略找到了最佳策略。 如果它们相同,我们将中断迭代。 否则,我们用`new_policy`更新`random_policy`:
```py
if (np.all(random_policy == new_policy)):
print ('Policy-Iteration converged at step %d.' %(i+1))
break
random_policy = new_policy
```
查看完整的`policy_iteration`函数:
```py
def policy_iteration(env,gamma = 1.0):
random_policy = np.zeros(env.observation_space.n)
no_of_iterations = 200000
gamma = 1.0
for i in range(no_of_iterations):
new_value_function = compute_value_function(random_policy, gamma)
new_policy = extract_policy(new_value_function, gamma)
if (np.all(random_policy == new_policy)):
print ('Policy-Iteration converged at step %d.' %(i+1))
break
random_policy = new_policy
return new_policy
```
因此,我们可以使用`policy_iteration`获得`optimal_policy`:
```py
optimal_policy = policy_iteration(env, gamma = 1.0)
```
我们将获得一些输出,即`optimal_policy`,这是在每种状态下要执行的操作:
```py
array([0., 3., 3., 3., 0., 0., 0., 0., 3., 1., 0., 0., 0., 2., 1., 0.])
```
完整的程序如下:
```py
import gym
import numpy as np
env = gym.make('FrozenLake-v0')
def compute_value_function(policy, gamma=1.0):
value_table = np.zeros(env.nS)
threshold = 1e-10
while True:
updated_value_table = np.copy(value_table)
for state in range(env.nS):
action = policy[state]
value_table[state] = sum([trans_prob * (reward_prob + gamma * updated_value_table[next_state])
for trans_prob, next_state, reward_prob, _ in env.P[state][action]])
if (np.sum((np.fabs(updated_value_table - value_table))) <= threshold):
break
return value_table
def extract_policy(value_table, gamma = 1.0):
policy = np.zeros(env.observation_space.n)
for state in range(env.observation_space.n):
Q_table = np.zeros(env.action_space.n)
for action in range(env.action_space.n):
for next_sr in env.P[state][action]:
trans_prob, next_state, reward_prob, _ = next_sr
Q_table[action] += (trans_prob * (reward_prob + gamma * value_table[next_state]))
policy[state] = np.argmax(Q_table)
return policy
def policy_iteration(env,gamma = 1.0):
random_policy = np.zeros(env.observation_space.n)
no_of_iterations = 200000
gamma = 1.0
for i in range(no_of_iterations):
new_value_function = compute_value_function(random_policy, gamma)
new_policy = extract_policy(new_value_function, gamma)
if (np.all(random_policy == new_policy)):
print ('Policy-Iteration converged at step %d.' %(i+1))
break
random_policy = new_policy
return new_policy
print (policy_iteration(env))
```
因此,我们可以得出最佳策略,该策略使用值和策略迭代来解决冻结湖问题,从而指定在每种状态下要执行的操作。
# 概要
在本章中,我们了解了什么是马尔可夫链和马尔可夫过程,以及如何使用 MDP 表示 RL 问题。 我们还研究了 Bellman 方程,并解决了 Bellman 方程,从而使用 DP 推导了最优策略。 在下一章[,第 4 章](04.html#2VBO80-3c5bb317ad314d43ac43a332c0db6f00),*使用蒙特卡洛方法*进行游戏中,我们将研究蒙特卡洛树搜索以及如何使用它进行智能游戏的构建。
# 问题
问题列表如下:
1. 马尔可夫财产是什么?
2. 为什么我们需要马尔可夫决策过程?
3. 我们何时更喜欢即时奖励?
4. 折扣因子有什么用?
5. 为什么要使用 Bellman 函数?
6. 您将如何导出 Q 函数的 Bellman 方程?
7. 值函数和 Q 函数有何关系?
8. 价值迭代和策略迭代有什么区别?
# 进一步阅读
[**MDP 哈佛讲座材料**](http://am121.seas.harvard.edu/site/wp-content/uploads/2011/03/MarkovDecisionProcesses-HillierLieberman.pdf)