做强化学习,绕不开的第一个坎就是马尔可夫决策过程(MDP)。我在最初学RL的时候,最懵的也是这一块:看了好多公式,听了很多名词,状态、动作、奖励、折扣因子、价值函数,全认识,但你让我说完“MDP到底是什么”,又说不上来。之后谁跟我说MDP就是“把决策问题形式化成一个数学框架”,我都点头,但心里其实没懂。直到我自己动手把一个小任务完整建模成MDP,又用代码跑了出来,这才算真正打通了。
这篇笔记就当作我自己的复盘,也顺便给后来的人趟趟路。我会从“为什么要用MDP来描述强化学习问题”讲起,把五元组拆开揉碎,再讲到回报、价值函数、贝尔曼方程,最后落到实际调参和踩坑经验。全程不用高深数学,全部用大白话和一个贯穿始终的小例子来讲。不管你是零基础入门,还是学完概念想系统梳理,这篇应该都能帮上忙。
1. 先搞明白:强化学习凭什么用MDP来建模
1.1 强化学习面对的是什么样的世界
先想一个普通问题:你在教一条狗“坐下”这个指令。狗听完指令后,有可能会坐下来,也有可能会歪着头看你,还会因为你手里有没有零食而表现不同。每次喂零食都算一次反馈,狗根据反馈慢慢学会“听到坐下”这个行为。这个过程里,狗看到的是当下的画面、听到的是指令,它不需要回忆三分钟前的风吹草动,只需要根据“现在”的状态做判断。
强化学习解决的问题就是这一类:智能体在环境里不断试错,通过环境反馈的奖励信号来学习策略,目标是让累积的奖励尽可能大。自动驾驶、下棋、机器人抓取、推荐系统,其实都能塞进这个框架里。可问题来了,环境千变万化,动作五花八门,奖励时有时无,要怎么把这些统一成一种能被数学和程序处理的模型?
这个时候就轮到MDP登场了。它的核心思路是把整个交互过程简化成一组固定的组成部分:状态、动作、转移规则、反馈信号、目标度量。只要你能把你的问题拆成这五样,就等于把一个具体业务问题翻译成了强化学习能解决的问题。所以先别急着背公式,先用这五样把你想解决的问题拆一拆。
1.2 回合、轨迹和时间步:MDP的最小叙事单元
MDP里的时间不是连续的,而是被切成一连串离散的时间步,t = 0, 1, 2, 3... 在每一个时间步,智能体观察当前状态 S_t,然后选择一个动作 A_t。环境收到动作后,会做两件事:给出一个即时奖励 R_{t+1},并把状态推进到 S_{t+1}。然后进入下一个时间步,不停循环。
这种“观察-决策-反馈-推进”的循环,其实就是你打游戏时的完整体验:看到屏幕画面(状态),按键操作(动作),看到分数变化(奖励),画面跳转到新场景(新状态)。没有任何多余的东西,MDP就是把这个循环数学化。
一段从头到尾完整的交互历史,叫一条轨迹,记作:
S_0, A_0, R_1, S_1, A_1, R_2, S_2, ...
写作笔记时我习惯把轨迹里的奖励序号错开一格,因为奖励 R_{t+1} 是对动作 A_t 的回应,这样标号在处理公式时特别顺手。你可以跟我一样,在纸上多写几遍这条序列,直到闭上眼能默写出来为止。后面的所有公式,全是在这条轨迹上做各种累加和期望。
1.3 马尔可夫性:为什么“只需要看现在”
“马尔可夫”四个字是整个框架的命根子。它的意思是:给定当前状态 S_t,未来的状态转移只跟 S_t 有关,跟更早的历史 S_{t-1}, S_{t-2} 都无关。用公式写就是:
P(S_{t+1} | S_t, S_{t-1}, ..., S_0) = P(S_{t+1} | S_t)
用人话说,我只看你现在的状态就够了,不关心你是怎么走到这一步的。这听起来像是很强的限制,但实际里大部分环境都能近似满足,因为你可以把“状态”定义得足够完整,把历史信息全塞进当前状态里。比如打麻将只看当前手牌是不行的,因为上一轮出过什么牌会影响别人要什么,但你如果把“已经出过的牌”也编进状态,那马尔可夫性就成立了。
每次谈到这儿我都愿意多说一句:马尔可夫性是MDP能简化问题的根本原因。没有它,所有算法都得维护整段历史,复杂度爆炸,根本算不下去。记住“状态要能反映所有影响未来的信息”,这个认知会直接影响你后面设计状态表示的水平。
2. MDP五元组逐项拆解:S、A、P、R、γ
MDP通常被写成一个五元组:S、A、P、R、γ。S是状态空间,A是动作空间,P是状态转移概率,R是奖励函数,γ是折扣因子。逐个拆开讲,顺便用一个走格子的小环境把它们串起来。
2.1 状态空间和动作空间:先给环境“画个框架”
状态空间 S 是所有可能状态的集合,动作空间 A 是所有动作的集合。两者最关键是定义清楚“粒度”:状态要抽象到什么程度,动作要细到什么程度。
继续拿格子世界举例。一个4x4的地图,机器人从左上角出发,走到右下角算胜利,中间有一个洞,掉进去算失败。状态可以简单编成坐标 (0,0) 到 (3,3),动作只有四个:上、下、左、右。这就是最典型的MDP教学环境。
但是真实项目里,状态空间远没有那么温柔。比如机器人的状态可能是关节角度、角速度、末端坐标的几十个连续值,动作可能是关节力矩的连续向量。这就要考虑状态里要不要包含速度?要不要包含上一时刻动作?这些问题的答案,直接决定马尔可夫性能不能满足。
关于状态设计的经验,留到后面第6节专门讲,这里先记住一句话:状态空间决定你“看到的世界”有多全,动作空间决定你“改变世界的能力”有多大,两个都要按任务目的来定,没定好后面全白搭。
2.2 状态转移概率:环境对你动作的“回应方式”
状态转移概率 P 描述的是:在状态 s 执行动作 a 之后,环境进入状态 s' 的概率,记作 P(s' | s, a)。它刻画了环境的动态特性。
还是用格子世界,假如机器人命令是“向右”,但轮子打滑,有0.8的概率真的到右边一格,有0.2的概率原地没动,这个概率分布就是 P。在很多教学环境里,P 是程序里硬编码的随机数。在真实场景中,P 往往不知道,需要通过采样的方式去估计。
这里必须强调一个概念区分:知道P和不知道P,对应着两种完全不同的解题路线。知道P,属于“基于模型”的强化学习,可以用动态规划直接算;不知道P,属于“无模型”强化学习,需要靠大量采样试错。MDP这个框架的厉害之处就在于,不管你知道不知道P,它都还是同一套问题描述,只是算法不同。
2.3 奖励函数:所有学习的“唯一信号”
奖励函数 R 是环境给智能体的反馈,通常写作 R(s, a) 或 R(s, a, s'),表示在某状态做某动作(到达某后续状态)能拿到的即时分数。奖励是强化学习里唯一告诉智能体“什么叫好”的信号,所以它的设计质量直接决定学习效果。
格子世界里可以这样定:到达终点给+1,掉进洞给-1,其他所有步都给0。这个设计看起来很简单,但已经包含了稀疏奖励、正负奖励平衡这些要素。
写奖励函数时最核心的一条心得是:奖励应该反映任务目标,而不是教智能体怎么做事。你写“往前走给0.1”就会训练出一个原地转圈刷分的东西。这表面上是个玩笑,但在真实项目里,我们吃过太多类似的亏,奖励稍一设计偏,智能体就会钻空子。
2.4 折扣因子:让算法学会“远近轻重”
折扣因子 γ 取值范围 [0,1],表示未来奖励相对当前奖励的重要程度。γ 越接近1,智能体越有耐心,愿意为了长远收益放弃眼前利益;γ 越接近0,智能体越短视,只看重当前一步。
为什么必须有折扣?两个原因。第一个是数学上的:无限时间步的情况下,如果不打折扣,累计回报可能变成无穷大,没法比较。第二个是直觉上的:未来的奖励有不确定性,投资里叫“现值”,越远的钱越不值钱,所以打个折扣合理。
格子世界里可以取 γ=0.9。这样一个三步之后拿到的奖励等于现在拿到0.729,算起来方便,也直观。折扣因子的选择在实际调参里非常讲究,后面6.1节单独展开。
2.5 把这些串起来:一个小环境的MDP建模样例
搞个最简单的2x2格子环境,状态 s1 是起点,s2 是终点,从 s1 只能向右或向下走。不啰嗦了,我直接写一个极简的MDP定义给你看:
- S = {s1, s2}
- A = {向右,向下}
- P:在 s1 执行“向右”有 1.0 概率到 s2,执行“向下”有 0.5 概率到 s2、0.5 概率留在 s1
- R:转移到 s2 得 +1,原地停留得 0
- γ = 0.9
你看,一个简单的MDP就定义完了。真到写代码时,我习惯把 P 和 R 写成字典嵌套结构,key分别是(s, a)和(s, a, s'),这样后续算价值函数时取数非常顺手。贴一段我常用的数据定义风格,方便你参考:
# 模拟2x2环境的转移与奖励 P = { ("s1", "向右"): [("s2", 1.0)], ("s1", "向下"): [("s2", 0.5), ("s1", 0.5)], ("s2", "向右"): [("s2", 1.0)], ("s2", "向下"): [("s2", 1.0)], } R = { ("s1", "向右", "s2"): 1.0, ("s1", "向下", "s2"): 1.0, ("s1", "向下", "s1"): 0.0, ("s2", "向右", "s2"): 1.0, ("s2", "向下", "s2"): 1.0, }把五元组落到这种数据结构,后面你写任何价值迭代、策略迭代代码都能直接上手,学习曲线一下子平缓很多。五元组拆完了,接下来看怎么用奖励去衡量一个策略的长期好坏。
3. 从回报到价值函数:智能体凭什么判断“好不好”
3.1 回报与折扣:计算一个时间步往后的累积奖励
给定一条轨迹,从某个时间步 t 开始往后累积的折扣奖励之和叫回报(Return),记作 G_t:
G_t = R_{t+1} + γR_{t+2} + γ^2 R_{t+3} + ...
这里把 γ 的幂次和轨迹时间步对应好,初学最容易错的就是这里。上面的公式展开是:当前奖励打0折、下一个奖励打1折、下下个奖励打2折,以此类推。我最早算回报时,总把第一项写成 γR_{t+1},结果怎么算都不对,后来专门在笔记里用红笔标了“第一项不打折”。
格子世界做个练习题:从 s1 出发,按“向下”动作一直执行,假设实际轨迹奖励依次是 0, 1, 0, 1,γ=0.9,那么在 t=0 时的回报是:
G_0 = 0 + 0.9 × 1 + 0.81 × 0 + 0.729 × 1 = 1.628
这种计算练个两三道,回报的含义就刻在脑子里了。回报是整个强化学习目标函数的微观单元,你后面见到的所有目标函数,本质上都是回报的某种期望。
3.2 状态价值函数:这是“这个局面值多少分”
光看一条轨迹的回报不够,因为同一条策略在同一个状态下,环境随机性会导致每次走得不一样。所以价值函数定义为回报的期望。
状态价值函数 V_π(s) 表示:在策略 π 下,从状态 s 出发,能获得的期望回报。符号里的 π 是策略:给定状态 s,π(a|s) 表示选动作 a 的概率。
V_π(s) = E_π[G_t | S_t = s]
同样在2x2格子环境,如果策略是“50%概率向右、50%概率向下”,那么从 s1 出发,可以手算:
V_π(s1) = 0.5 × (R向右+γV_π(s2)) + 0.5 × (0.5(R向下到s2+γV_π(s2)) + 0.5(R向下留在s1+γV_π(s1)))
这个形式其实就是下一步要讲的贝尔曼方程。数值上不用急着求出来,理解它的含义更重要:价值函数是策略的“天气预报”,它告诉你在这条策略下,这个状态未来平均能拿多少。
3.3 动作价值函数:把“状态”细分到“状态下做某个动作”
状态价值函数有一个不够用的地方:策略已经定好了,你在状态 s 只能按策略概率选择动作,没法评价“如果我不按策略来,临时换个动作会怎样”。动作价值函数 Q_π(s, a) 就是专门干这个的:
Q_π(s, a) = E_π[G_t | S_t = s, A_t = a]
含义是:从状态 s 出发,先执行动作 a,之后完全按策略 π 行动,能获得的期望回报。注意开头这个“先执行”非常重要,它把动作给固定下来了,所以Q函数才能回答“某个动作值不值得做”的问题。
为什么大家在做控制时总喜欢用Q而不是只用V?因为最优策略选择时,只要在状态 s 里挑 Q 值最大的动作就行,不需要再套一层策略公式。这一点在5.3节会把两种价值函数的用法对比开。
3.4 两个价值函数之间的桥梁
V 和 Q 之间存在一个简单的换算关系:V_π(s) 等于在该状态下所有动作 Q 值按策略概率加权的平均。
V_π(s) = Σ_a π(a|s) Q_π(s, a)
反过来说,Q_π(s, a) 也可以写成“即时奖励 + 下一个状态V值期望”的形式:
Q_π(s, a) = R(s,a) + γ Σ_{s'} P(s'|s,a) V_π(s')
这两条关系式是接下来贝尔曼方程的支柱。我自己记忆的方法是:V是Q的“平均”,Q是V的“下一步展开”。你只要能把这两句话复述清楚,推导贝尔曼方程时候就不会绕晕。
实际用代码算价值函数时,我习惯先算Q再算V,因为Q更接近模拟器给出的原始数据(状态、动作、奖励、下一状态),V算出来其实是个派生的汇总指标。很多现成库的底层设计也遵从这个顺序,你从动作价值入手写代码会顺很多。
4. 贝尔曼方程:整个强化学习算法的“源头旋钮”
4.1 从胆敢写下一个自洽的等式说起
贝尔曼方程不是靠逻辑推导“蹦”出来的,它不过是把“回报”的定义往自己身上套了一次。你回想 G_t 的定义:
G_t = R_{t+1} + γR_{t+2} + γ^2R_{t+3} + ...
把它拆成第一项加“后面所有项”:
G_t = R_{t+1} + γ(R_{t+2} + γR_{t+3} + ...)
括号里这串,恰好就是从 t+1 时刻开始的回报 G_{t+1}。所以:
G_t = R_{t+1} + γG_{t+1}
一个时步的回报,等于本步奖励加下一步回报的折扣。这个递推关系看着简单,却是所有强化学习算法的地基。价值函数是回报的期望,把这层期望一加,贝尔曼方程就到手了。
4.2 状态值贝尔曼方程:拆成“现在”和“将来”
对 V_π(s) 应用上面的递推,再按策略展开动作期望,就得到状态价值的贝尔曼方程:
V_π(s) = Σ_a π(a|s) [ R(s,a) + γ Σ_{s'} P(s'|s,a) V_π(s') ]
这个公式里每一项都能找到实际意义:策略决定选动作的概率,即时奖励衡量眼前,转移概率加下一步价值代表未来期望。整个公式就是“当前奖励 + 未来奖励的折扣期望”。
2x2格子环境,策略若是“等概率选向右或向下”,则:
V_π(s1) = 0.5 × (R(s1,向右) + γV_π(s2)) + 0.5 × (R(s1,向下) + γ[0.5V_π(s2) + 0.5V_π(s1)])
把它展开写出来,你才能真的看到这个方程在“递归地计算自己”。很多教材上来就说这是“动态规划方程”,但没点破:它把 V(s) 表示成其他 V(s') 的加权组合,而 s' 可能是自己,也可能是别的状态。
4.3 动作值贝尔曼方程:策略评估其实更常算这个
动作价值的贝尔曼方程同样来自递推式:
Q_π(s, a) = R(s,a) + γ Σ_{s'} P(s'|s,a) Σ_{a'} π(a'|s') Q_π(s', a')
这里多了一层 Σ_{a'} π(a'|s'),是因为动作价值进入下一步后,策略又接管了动作选择。也就是说,当前动作是固定的,但后续动作仍按策略来。
写代码做策略评估,我几乎总是用Q版本的方程,原因很实在:模拟器里天然输出四元组 (s, a, r, s'),Q版本的迭代直接采样就能更新。相比之下V版本要先对策略求平均,多一步操作。你实际操作几次就会体会到,学公式的时候V更清爽,写代码的时候Q更顺手。
4.4 为什么叫“自举”:用一个猜测更新另一个猜测
贝尔曼方程最独特的性质,叫做“自举”(bootstrapping):等式右侧的 V(s') 本身就是要算的值,却出现在左侧 V(s) 的定义里。就好比你先估一个未来价格,用它来更新当前价格,然后明天再用更新后的当前价格去校准未来价格,一轮一轮迭代。
这种“用猜测更新猜测”的做法,让贝尔曼方程可以转化成迭代算法:随便给 V 初始化成0,然后反复套方程更新,经过足够多次迭代就会收敛到真实价值。这个过程就是动态规划里的“策略评估”。虽然高级算法会绕开显式的P,直接用样本代替期望,但贝尔曼方程这层递归结构是它们共同的基因。
所以我说贝尔曼方程是“源头旋钮”:Q-learning、SARSA、策略梯度,表面公式五花八门,内核都在这个递推式上做文章。你把4.1节里那个 G_t = R_{t+1} + γG_{t+1} 吃透,后面看所有算法推导都会轻松一大截。
5. 最优策略怎么找:目标函数、最优点与经典求解思路
5.1 最优价值函数:把所有策略比出一个高下
策略有好有坏,怎么定义“最好”?定义最优状态价值函数 V* 为所有可能策略下每个状态的最大价值:
V*(s) = max_π V_π(s)
类似地,最优动作价值函数 Q*(s, a) 是所有策略里最大的状态-动作价值。这时有一个非常重要的性质:所有最优策略共享同一个最优价值函数,而且如果已知 Q*,最优策略直接选每一状态下 Q* 最大的动作即可,不需要再算策略概率。
这个性质是理解强化学习控制算法的关键。你在教材里常看到的一组最优贝尔曼方程:
V*(s) = max_a [ R(s,a) + γ Σ_{s'} P(s'|s,a) V*(s') ]
Q*(s, a) = R(s,a) + γ Σ_{s'} P(s'|s,a) max_{a'} Q*(s', a')
把策略那层平均改成了取max,意思就是:在每一个状态,我只挑最优动作来评估。这就是求解最优策略时“贪心”的来源,也是策略迭代里策略改进步骤的理论依据。
5.2 最优策略为什么可以用“贪心”来确定
你可能有个疑惑:MDP整个问题那么复杂,为什么最优策略不能靠眼前贪心,而把长远计算全部交给 Q* 来打包?原因在于 Q* 已经包含了未来期望。动作价值函数的价值就是把“长远”压缩进了一个数,于是决策就变成了比较这些数。
换句话说,强化学习的难不在“贪心选择”这一步,而在“如何算准Q”。一旦有了准的Q,策略就是一层皮,直接取argmax就行。这也是为什么这么多算法都在试图估计Q值而不是直接算策略。
这里必须分清楚“策略评估”和“策略改进”两个环节。策略评估是求当前策略的价值函数,策略改进是根据价值函数更新策略(比如改成贪心)。两个环节交替执行,就是下面要说的策略迭代。
5.3 两种经典算法:值迭代与策略迭代
拿到一个MDP且已知P和R,最简单的求解方式就是动态规划。口诀我都给你整理好了:
- 策略评估:反复套贝尔曼方程更新V,直到收敛,得到当前策略下的价值函数。
- 策略迭代:评估完一轮,立刻按V或Q改进策略,改进完再重新评估,循环直到策略不再变化。
- 价值迭代:不显式维护策略,直接把V更新公式里的策略平均换成max,反复迭代直到V收敛,再从V提取最优策略。
策略迭代收敛快,但每轮评估要多次扫描;价值迭代实现简单,每次只做一个max更新,适合编码入门。在2x2格子上跑价值迭代,代码量非常少:
import math states = ["s1", "s2"] actions = ["向右", "向下"] gamma = 0.9 V = {s: 0.0 for s in states} # 转移函数和奖励(简写) trans = { ("s1", "向右"): [("s2", 1.0)], ("s1", "向下"): [("s2", 0.5), ("s1", 0.5)], ("s2", "向右"): [("s2", 1.0)], ("s2", "向下"): [("s2", 1.0)], } rew = { ("s1", "向右", "s2"): 1.0, ("s1", "向下", "s2"): 1.0, ("s1", "向下", "s1"): 0.0, ("s2", "向右", "s2"): 1.0, ("s2", "向下", "s2"): 1.0, } for it in range(100): V_new = {} for s in states: best_val = -math.inf for a in actions: val = 0.0 for s_next, p in trans[(s, a)]: val += p * (rew[(s, a, s_next)] + gamma * V[s_next]) best_val = max(best_val, val) V_new[s] = best_val V = V_new print(V)这段代码就是价值迭代最朴素的写法。最外层迭代会得到一个稳定的V,你在s1上会看到最优价值收敛到某个固定值。跑完这个样例再去看Q-learning,你会发现自己已经能看懂算法里每一步在干嘛了。
5.4 P未知怎么办:从这里跨进无模型学习的门槛
大部分教材的MDP章节会一直停留在动态规划,然后突然跳去蒙特卡洛和时间差分。其实那一步跨越的动机应该先讲明白:一旦环境P未知,上面所有需要P的计算全都没法直接用,符号 P(s'|s,a) 要从求和里消失,你必须靠采样多条轨迹来估计那个期望。
这个跨越非常关键。动态规划是“数学上精确解”,无模型方法是“数据中统计估计”。两者共用同一个贝尔曼方程框架,区别只不过是把“期望”换成“样本平均”而已。认识到这一点,你就不再用死记硬背的方式学深度强化学习,而是从“怎么用样本近似期望”这个角度去理解它们了。
我在学习时最大的感触是:MDP章节学得好不好,不在于背了多少公式,而在于闭卷能不能说出“为什么无模型算法还能用贝尔曼方程”。一旦这句话能流畅说出来,说明你已经把框架逻辑吃透了。
6. 实战里的坑与经验:不是算法的错,是建模和调参
6.1 折扣因子怎么选:别小看这个数,它决定了智能体的“近视度”
γ 的选择直接塑造智能体的行为。做直达目标的格子世界,γ=0.9 和 γ=0.95 差别还不大,但换成长期决策任务,比如一个月时长的库存补货,γ太低会让智能体只顾今天清货,不管月底目标。反过来在回合制的游戏里,γ 接近1也没问题,因为回合总会结束,累计回报不会爆炸。
我的经验是按“有效时间视野”来估:1/(1-γ) 大约等于智能体能“看得见”的有效步数。γ=0.9 时大约看10步,γ=0.99 时大约看100步。任务的核心决策窗口有多长,就往那个方向调。
6.2 奖励稀疏怎么办:别急着堆“中间奖励”
任务如果只在最后给一次奖励,智能体会非常难学,因为绝大多数动作都是零奖励,没有任何梯度可言。但直接把每一步都塞一点小奖励,又会诱导出各种钻空子的行为。
我自己踩过的坑是:在某次路径规划模拟项目里,为了加快收敛,给“离目标更近”加了一点点正奖励,结果智能体学会了在原地小碎步打转,因为离得近的瞬时奖励容易刷,但根本到不了终点。后来换成只在终点给大奖励、再配合回放缓冲区,反而学得又快又稳。
处理稀疏奖励的体感优先顺序是:先检查状态表示是否太差,再考虑奖励塑形,最后才动奖励数值。奖励塑形本身有点像是“给目标插路标”,写着写着就会发现,和设计状态表示实际上是一件事的两面。
6.3 状态设计比“算法选型”更值得花时间
不少新人容易把精力都放在选算法上:要不要上PPO,要不要换SAC,但真正决定任务能不能做出来,往往是状态表示和动作空间设计。状态要尽量满足两点:一是包含所有和长期决策相关的信息,二是尽量让马尔可夫性近似成立。
比如机器人抓取任务,如果状态里只有手爪当前位置,没有物体位置,那这个任务基本没法做,因为历史信息和本质关联完全被切断了。把物体坐标加进状态,等于把“环境感知”补全,问题一下子就变成可解的。我通常会在建模前列一张表:影响最优决策的因素有哪些、哪些能被传感器测到、哪些需要通过历史记忆来补,再决定状态向量到底包含什么。
6.4 从MDP这一步往后学,你的“接口”已经就位
MDP学完之后,其实你就拿到了通往后续所有强化学习算法的大门钥匙。接下来无论是蒙特卡洛、时序差分、Q-learning,还是策略梯度、Actor-Critic,全都默认你已经理解了“状态-动作-奖励-价值”这套脉络。往后所有新概念,无非是在“如何估计期望”这一步上做文章。
我自己在翻后续资料时,常用一句口号自洽:“任何算法,先问它在估计哪个期望,再问它用什么数据来算这个期望。” 这句话是MDP章节教给我的,现在分享给你。
7. 顺手做个小练习:把“猜数字游戏”建模成MDP
学MDP最怕的是只看不练。我给你留一个特别小的练习,十几分钟能做完,但做完对MDP的理解会扎实非常多。
任务:设计一个猜数字游戏。裁判在心里想一个1到10之间的整数,玩家每轮猜一个数,猜中得+10分,猜错得0分,游戏继续。你有10次机会,10次全错游戏结束得0分。请把这个问题建模成MDP,明确写出S、A、P、R、γ,并说明为什么“裁判心里的数”和“玩家已经猜过的数”都应进入状态。
这个练习里最容易错的是把状态只定义成“当前还没结束”,忘了把“已经猜过哪些数”放进去。但如果没有猜过的历史,玩家下一轮就会重复猜同一个数,这明显不是最优策略。造这个练习就是为了让你亲手体会一次“状态设计影响马尔可夫性”的滋味。
做完之后再来对照:状态 S 可以是“剩余次数 + 已猜集合”,动作 A 是“猜1到10中的某个数”,转移概率里“猜中/猜不中”是确定性的,奖励规则上面已经给了,γ 可以取0.9。你会发现照着五元组一拆,整个问题无比清晰,也根本不需要用什么高级算法,枚举都能解。
最后再分享一个小技巧:写笔记的时候,别只记公式,把每一步遇到的“理解障碍”也记下来。比如我当时就在页边写了“γ 是折扣,不是概率”,后来复习的时候一眼就看到重点。这份MDP笔记我翻过很多遍,每次都有新收获,希望你也能建立起自己的理解版本,走通强化学习这条难走但特别有趣的路。