{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 第十六讲:马尔可夫决策过程\n", "\n", "# 第十三部分:强化学习及控制\n", "\n", "接下来我们将要学习的是**强化学习(RL: Reinforcement Learning)**以及**自适应控制(Adaptive Control)**。\n", "\n", "回顾前面学过的内容:\n", "\n", "* 在监督学习中,算法尝试模仿训练集中的数据,给新的输入$x$贴上一个合适的标签$y$。在这种情形下,$y$会明确的指出每个输入$x$所对应的那个“正确的”标签;\n", "* 在无监督学习中,算法尝试发掘数据中潜在的固有结构,使用模型将这种结构表示出来,之后再对数据进行标记,使其存在于某个结构中。\n", "\n", "但是在很多需要作出渐进决策及控制的问题中,我们很难提供这种明确的监督,去告诉算法什么是正确、什么是错误。比如我们建造了一个四腿机器人,然后尝试通过编程让它行走。从一开始我们就无法定义对于行走来说,什么是正确的动作。所以也就无法为算法提供一个明确的监督方案令其模仿了。\n", "\n", "在强化学习中,我们只会给算法提供一个**奖励函数(reword function)**,这个函数会告诉算法在什么情况下是做的好,在什么情况下是做的不好,比如在四腿机器人的例子中,当机器人向前行走时奖励函数会给算法正面的反馈,而当机器人无故后退或翻倒时函数则会给算法负面的反馈。而学习算法的任务就是自主发现“通过做出怎样的动作才能获得更多的奖励”。\n", "\n", "强化学习算法在直升机自动驾驶、机器人腿部移动、手机网络路由、市场策略选择、工厂控制、高效网页索引等领域都有非常成功的应用案例。而我们对强化学习算法的介绍将从**[马尔可夫决策过程](https://zh.wikipedia.org/wiki/%E9%A6%AC%E5%8F%AF%E5%A4%AB%E6%B1%BA%E7%AD%96%E9%81%8E%E7%A8%8B)([MDP: Markov Decision Process](https://en.wikipedia.org/wiki/Markov_decision_process))**开始,MDP将形式化RL算法经常遇到的问题。\n", "\n", "## 1. 马尔可夫决策过程\n", "\n", "一个马尔可夫决策过程就是一个元组$(S,A,\\{P_{sa},\\gamma,R\\})$,其中:\n", "\n", "* $S$是一个**状态(states)**的集合;(在直升机自动驾驶的例子中,就代表了直升机所有可能的位置和姿态。)\n", "* $A$是一个**动作(action)**的集合;(在直升机自动驾驶的例子中,就代表了通过操作杆可以对直升机做出的所有动作。)\n", "* $P_{sa}$是状态转换概率,对每个状态$s\\in S$和动作$a\\in A$,$P_{sa}$是一个在状态空间上的分布。在后面我们会详细讨论这一点,简单的说,就是如果在状态$s$下发生了动作$a$,“那么下一步所将变为哪一个状态”的概率分布就由$P_{sa}$确定;\n", "* $\\gamma\\in[0,1)$称为**折扣因子(discount factor)**,用来调整未来奖励与当作奖励之间的权重;\n", "* $R:S\\times A\\to\\mathbb R$就是奖励函数。(有时只把奖励函数当做关于状态$S$的函数$R:S\\to\\mathbb R$。)\n", "\n", "简要的描述一下MDP的动态过程:首先,我们从状态$s_0$开始,选择在MDP中做一个动作$a_0\\in A$。动作的结果就是产生一个随机的状态转换,转换到下一个状态$s_1$,而$s_1\\sim P_{s_0a_0}$(也就是事件“转换到下一个状态”服从由当前状态$s_0,a_0$确定的分布)。接下来我们继续在MDP中做出下一个动作$a_1$。同样,$a_1$的发生使得MDP转换到下一个状态$s_2\\sim P_{s_1a_0}$。之后我们继续做出动作$a_2$,等等……我们可以用下图描述上面的文字:\n", "\n", "$$\n", "\\require{AMScd}\n", "\\begin{CD}\n", "s_0@>{a_0}>>s_1@>{a_1}>>s_2@>{a_2}>>s_3@>{a_3}>>\\cdots\\\\\n", "\\end{CD}\n", "$$\n", "\n", "MDP在做出动作$a_0,a_1,\\cdots$后从状态$s_0,s_1$中得到的“总收益”为:\n", "\n", "$$\n", "R(s_0,a_0)+\\gamma R(s_1,a_1)+\\gamma^2R(s_2,a_2)+\\cdots\n", "$$\n", "\n", "也可以写成只与状态相关的函数:\n", "\n", "$$\n", "R(s_0)+\\gamma R(s_1)+\\gamma^2R(s_2)+\\cdots\n", "$$\n", "\n", "对于大多数情况,我们都使用较为简单的状态奖励函数$R(s)$,尽管更加一般化的状态-动作奖励函数$R(s,a)$也并没有增加什么计算难度。\n", "\n", "在强化学习中,我们的目标是按照时间顺序依次选择动作,使得整个MDP获得的“总收益”最大:\n", "\n", "$$\n", "\\mathrm E\\left[R(s_0)+\\gamma R(s_1)+\\gamma^2R(s_2)+\\cdots\\right]\n", "$$\n", "\n", "注意到第$t$步的奖励被折扣因子$\\gamma^t$降低了(而且随着步数的推移折扣会越来越大),于是,为了保证期望值足够大,我们倾向于尽可能早的获得正反馈(并让负反馈到来的越晚越好)。在经济学应用领域,$R(\\cdot)$代表赚到的钱,$\\gamma$也有自然的解释——利率(也就是今天的美元比明天的美元更值钱)。\n", "\n", "**策略(policy)**是一个函数$\\pi:S\\to A$,它是一个从状态到动作的映射。不论何时,只要处于状态$s$,就采用动作$a=\\pi(s)$,我们称这个过程为**执行(executing)**策略$\\pi$(在复杂模型中,策略不止通过当前状态,还可能通过过去的状态及动作,对下一步动作做出判断;而在本课程中,我们仅使用当前状态作为策略的输入)。为策略$\\pi$定义**价值函数(value function)**:\n", "\n", "$$\n", "V^\\pi(s)=\\mathrm E\\left[\\underbrace{R(s_0)}_\\textrm{immediate reward}+\\underbrace{\\gamma R(s_1)+\\gamma^2R(s_2)+\\cdots}_\\textrm{future reward}\\mid s_0=s,\\pi\\right]\n", "$$\n", "\n", "$V^\\pi(s)$是从状态$s$开始依照策略$\\pi$所得到的折扣奖励的预期总收益。(式中的这种标记法理论上说是不正确的,因为$\\pi$并不是随机变量,不过这种写法在文献中是标准记法。)这个式子由两部分组成:\n", "* 我们通常称前半部分为**即时奖励(immediate reward)**$R(s)$,即从状态$s$开始时直接得到的奖励;\n", "* 称后半部分为**未来奖励(future reward)**,即当前状态之后的所有步骤决策所产生的折扣奖励的总收益。\n", "\n", "容易看出,这个式子可以写成$\\displaystyle V^\\pi(s)=\\mathrm E\\left[R(s_0)+\\gamma\\underbrace{\\left(R(s_1)+\\gamma R(s_2)+\\cdots\\right)}_{V^\\pi\\left(s'\\right)}\\mid s_0=s,\\pi\\right]$(为了方便,我们暂时定义映射$s_0\\to s,\\ s_1\\to s'$),这是一个递归定义,可以按照递归式写作:\n", "\n", "$$\n", "V^\\pi(s)=R(s)+\\gamma\\sum_{s'\\in S}P_{s\\pi(s)}\\left(s'\\right)V^\\pi\\left(s'\\right)\n", "$$\n", "\n", "我们之所以不把后半部分直接写成$\\gamma V^\\pi\\left(s'\\right)$,是因为在位于前一个状态时,下一个状态是一个随机变量。所以上式的未来奖励部分写成了期望的定义式$\\displaystyle\\operatorname*{E}_{s'\\sim P_{s\\pi(s)}}\\left[V^\\pi\\left(s'\\right)\\right]$——即“位于状态$s$时,执行策略$\\pi(s)$,到达状态$s'$的概率:$P_{s\\pi(s)}\\left(s'\\right)$(可以看做是$P_{sa}\\left(s'\\right)$,而$a=\\pi(s)$)”与“状态$s'$下的价值函数:$V^\\pi\\left(s'\\right)$”之积在所有状态$s'\\in S$上求和——这是从状态$s'$开始依照策略$\\pi$所得到的折扣奖励的预期总收益。其中$s'$是一个与概率$P_{s\\pi(s)}$相关的分布,这是一个在MDP中从状态$s$开始,按照策略$\\pi(s)$执行动作后,落在下一个状态$s'$上时对应的分布。因此,第二项就是MDP在执行第一步之后,在未来能够获得的折扣奖励的预期总收益。\n", "\n", "上面这个式子也称作**贝尔曼方程(Bellman Equation)**,这是我们在解决MDP问题时用到的主要方程之一。我们可以说,对于给定的策略$\\pi$,其价值函数$V^\\pi$满足贝尔曼方程。\n", "\n", "贝尔曼方程可以高效的解出$V^\\pi$,尤其是对有限状态的MDP($\\lvert S\\rvert\\lt\\infty$),我们可以为每一个状态$s$计算一次$V^\\pi(s)$。如此就可以得到一个由“关于$V^\\pi(s)$的有$\\lvert S\\rvert$个变量(每个状态$s\\in S$都需要一个预期总收益$V^\\pi(s)$)同时具有$\\lvert S\\rvert$个方程(每个状态$s\\in S$的价值函数$V^\\pi(s)$都有一个方程)的线性方程”组成线性方程组,进而通过这些方程高效的解出每一个$V^\\pi(s)$。\n", "\n", "我们再给出**最优价值函数(optimal value function)**的定义:\n", "\n", "$$\n", "V^*(s)=\\max_\\pi V^\\pi(s)\\tag{1}\n", "$$\n", "\n", "换句话说,这是在状态$s$时,对于所有可能的策略,能够使折扣奖励的总预期收益最大化的策略$\\pi$下,得到的总收益。当然,这个最优价值函数也有一个对应的贝尔曼方程:\n", "\n", "$$\n", "V^*(s)=R(s)+\\max_{a\\in A}\\gamma\\sum_{s'\\in S}P_{s\\pi(s)}\\left(s'\\right)V^*\\left(s'\\right)\\tag{2}\n", "$$\n", "\n", "第一项没有变,仍旧是策略的立即奖励;而第二项是对于所有可能的动作,在选择执行动作$a$后,使获得的未来折扣奖励的预期总收益最大化。\n", "\n", "这也引出了最佳策略的定义$\\pi^*:S\\to A$为:\n", "\n", "$$\n", "\\pi^*(s)=\\arg\\max_{a\\in A}\\sum_{s'\\in S}P_{sa}\\left(s'\\right)V^*\\left(s'\\right)\\tag{3}\n", "$$\n", "\n", "也就是说$\\pi^*(s)$给了我们一个使$(2)$式中使$\\max$部分取到最大值时$a$的取值(我们这里省略了$\\gamma$,因为对最大化的式子来说它只是一个常数)。(忘记$\\max f(x)$与$\\arg\\max f(x)$区别的话可以参考[What is the difference between $\\arg\\max$ and $\\max$?](http://math.stackexchange.com/questions/312012/what-is-the-difference-between-arg-max-and-max),简单地说$\\max f(x)$返回函数$f(x)$的极值,而$\\arg\\max f(x)$返回函数取到极值点时参数的取值。)\n", "\n", "那么,对于所有状态$s$以及所有策略$\\pi$,有:\n", "\n", "$$\n", "V^*(s)=V^{\\pi^*}(s)\\geq V^\\pi(s)\n", "$$\n", "\n", "前面的等式说明$V^{\\pi^*}$(即策略$\\pi^*$的价值函数)等于“对于所有状态$s$取到最优的价值函数$V^*$”。后面的不等式说明$\\pi^*$的值比任何策略的值都要大。也就是说$(3)$式定义的$\\pi^*$是最优策略。\n", "\n", "注意到$\\pi^*$的一个有趣的属性——它是对于所有状态$s$的最优策略。这并不是说,如果从状态$s$开始,就有一个针对$s$的最优策略;如果从状态$s'$开始,就会采取别的针对$s'$的最优策略。而是说令$(1)$式取到最大值的的$\\pi^*$是对所有状态$s$而言的,这意味着不论MDP从什么状态开始,我们都可以用策略$\\pi^*$。\n", "\n", "## 2. 值迭代与策略迭代\n", "\n", "接下来我们将介绍两种针对有限状态的MDP的高效算法。我们假设MDP具有有限的状态以及有限的动作空间($\\lvert S\\rvert\\leq\\infty,\\lvert A\\rvert\\leq\\infty$)。\n", "\n", "要介绍的第一个算法是**值迭代(value iteration)**:\n", "1. 对于每个状态$s$,以$V(s):=0$初始化;(相当于在内存中创建了大小为$\\lvert S\\rvert$的状态向量,用$\\vec0$初始化。)\n", "2. 重复直到收敛:`{`\n", "\n", " * 对于每个状态,更新$\\displaystyle V(s):=R(s)+\\max_{a\\in A}\\gamma\\sum_{s'\\in S}P_{sa}\\left(s'\\right)V\\left(s'\\right)$。(注意,本小节介绍的算法都是在状态转换概率$P_{sa}$及奖励函数$R$已知时使用的。)\n", " \n", " `}`\n", "\n", "这个算法可以看做是不停的尝试使用贝尔曼方程更新价值函数。对于内部的循环,有两种更新方法:\n", "1. 可以先计算每个$s$的$V(s)$,然后用这些新算出来的值代替所有的旧值(用新计算的状态向量更新第1步中的状态向量),这也称作**同步(synchronous)**更新。这种方法可以看做是一种“Bellman backup operator”的实现,用对当前价值函数的估值映射到新的估值上;\n", "2. 可以遍历状态$s$(按某种固定顺序),每次更新一个值(每次$V(s)$的计算都从状态向量中取最新的分量,得出结果后直接更新状态向量中想要的分量),这也称作**异步(asynchronous)**更新。\n", "\n", "不论是使用同步还是异步的更新(通常异步会快一点),对值的迭代可以使得$V$最终收敛于$V^*$。得到$V^*$后就可以使用$(3)$式计算最优策略了。\n", "\n", "在MDP中还有一种用于计算最优策略的标准算法,称为**策略迭代(policy iteration)**:\n", "1. 随机初始化策略$\\pi$;\n", "2. 重复直到收敛:`{`\n", " * (a) 令$V:=V^\\pi$;(通过贝尔曼方程组求解。)\n", " * (b) 对每一个状态$s$,令$\\displaystyle\\pi(s):=\\arg\\max_{a\\in A}\\sum_{s'\\in S}P_{sa}\\left(s'\\right)V\\left(s'\\right)$。\n", " \n", " `}`\n", "\n", "内部的循环会不停的计算当前策略下的价值函数,然后使用当前的价值函数更新策略。((b)步骤中求$\\pi$也称为**关于$V$的贪心策略(greedy with respect to $V$)**)。而步骤(a)可以通过求解贝尔曼方程组得到——也就是在策略$\\pi$给定时,有$\\lvert S\\rvert$个变量$V^\\pi(s)$,以及$\\lvert S\\rvert$个方程的线性方程组。\n", "\n", "在有限的迭代之后,$V$会收敛于$V^*$且$\\pi$会收敛于$\\pi^*$。\n", "\n", "值迭代与策略迭代都是求解MDP的标准算法,目前大家并没有对于哪个算法更好达成共识。对于小型MDP,策略迭代通常更快并会在很少的几步迭代之后收敛。然而在求解具有很多状态的大型MDP时,对$V^\\pi$的求解将涉及到解大型线性方程组,这通常会很困难。在这种情况下,值迭代可能会是更好的选择。也正是因为这个原因,我们通常在现实中更喜欢使用值迭代。\n", "\n", "## 3. MDP的模型估计\n", "\n", "到目前为止,我们讨论的MDP以及MDP算法都是建立在状态转换概率以及奖励函数已知的前提下。但是,在现实问题中,状态转换概率和奖励函数可能不是明确的已知条件,那么我们必须从数据中估计这些量。(通常$S,A,\\gamma$都是已知的。)\n", "\n", "比如在倒置钟摆问题中(见[问题集4](http://cs229.stanford.edu/materials/ps4.pdf)),我对MDP进行了一系列的试验,过程如下:\n", "\n", "$$\n", "\\require{AMScd}\n", "\\begin{CD}\n", "s_0^{(1)} @>{a_0^{(1)}}>> s_1^{(1)} @>{a_1^{(1)}}>> s_2^{(1)} @>{a_2^{(1)}}>> s_3^{(1)} @>{a_3^{(1)}}>> \\cdots\n", "\\end{CD}\\\\\n", "\\begin{CD}\n", "s_0^{(2)} @>{a_0^{(2)}}>> s_1^{(2)} @>{a_1^{(2)}}>> s_2^{(2)} @>{a_2^{(2)}}>> s_3^{(2)} @>{a_3^{(2)}}>> \\cdots\n", "\\end{CD}\n", "$$\n", "\n", "这里$s_i^{(j)}$表示在第$j$此试验中第$i$步的状态,而$a_i^{(j)}$表示在第$j$此试验中第$i$步的状态下执行的动作。在实际操作中,除非MDP过程终止(比如在倒置钟摆问题中,在钟摆倒下时MDP终止),否则每一个试验将一直运行下去;或者可能运行很多但是有限步后停下。有了这些MDP试验的“经验”,我们就可以推导出状态转换概率的最大似然估计:\n", "\n", "$$\n", "P_{sa}\\left(s'\\right)=\\frac{\\textrm{在状态}s\\textrm{下执行动作}a\\textrm{得到状态}s'\\textrm{的次数}}{\\textrm{状态}s\\textrm{下执行动作}a\\textrm{的次数}}\\tag{4}\n", "$$\n", "\n", "如果在应用上式时得到的比值为$\\displaystyle\\frac{0}{0}$时(比如在状态$s$下从未执行过动作$a$时),我们可以简单的将$P_{sa}\\left(s'\\right)$估计为$\\displaystyle\\frac{1}{\\lvert S\\rvert}$。(即将$P_{sa}$估计为在所有状态上的均匀分布。)\n", "\n", "注意到如果能够在MDP中得到更多的“经验”(即进行更多次试验),我们就可以使用一种更高效的方法,拿新“经验”的数据对状态转换概率的估计进行更新:我们可以记录$(4)$式中分子和分布的计数值,当我们拿到新的试验数据时,不停的累加分子和分母的计数值就可以了。最后只需计算一次比值,就能够得到$P_{sa}$。\n", "\n", "使用类似的过程也可以处理奖励函数$R$未知的情况,我们可以使用所有状态$s$下的平均奖励来估计状态$s$预期即时奖励$R(s)$。\n", "\n", "在得到MDP的模型之后,我们可以带入估计出的状态转换概率和奖励函数,使用值迭代或策略迭代解出MDP的最佳策略。举个例子,联合使用模型估计和值迭代,在状态转换概率未知的情况下学习MDP,可以使用下面的算法:\n", "1. 随机初始化策略$\\pi$;\n", "2. 重复 `{`\n", "\n", " * (a) 在MDP中按照策略$\\pi$执行一些试验;\n", " * (b) 使用在MDP的试验中积累的“经验”,更新对$P_{sa}$的估计(如果可以的话也更新$R$);\n", " * (c) 使用估计的状态转换概率和奖励函数,应用值迭代,估计新的价值函数$V$;\n", " * (d) 使用关于$V$的贪心策略更新$\\pi$;\n", "\n", " `}`\n", "\n", "注意到对于上面这个特定算法,有一个能够大幅优化性能的方法:在内部循环使用值迭代的步骤里,我们不像值迭代定义的那样用$V=0$做初始化,而使用算法上一步迭代得到的解做初始化,这样就给值迭代过程了一个更好的起点,能够使值迭代过程收敛更加迅速。" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.2" } }, "nbformat": 4, "nbformat_minor": 0 }