策略迭代与值迭代:从零开始理解两种核心算法

大家好,我是你们的老朋友。今天咱们来聊聊强化学习里两个最基础、也最容易被搞混的算法——策略迭代和值迭代。说实话,我刚开始学的时候,也觉得这俩东西长得太像了,到底有什么区别?什么时候该用哪个?别急,咱们一个一个拆开来看。

核心知识点速览

  • 策略迭代 = 策略评估 + 策略改进,两个步骤交替进行
  • 值迭代 = 策略评估的简化版 + 隐式策略改进,一步到位
  • 策略迭代收敛更快(迭代次数少),值迭代计算更简单(每次迭代快)

策略迭代算法:先评估,再改进

策略迭代的思路其实很直观。你想想看,如果你有一个策略,你首先得知道这个策略到底好不好,对吧?这就是策略评估要做的事。然后根据评估结果,你再去改进这个策略。就这么简单。

策略评估:算清楚当前策略的价值

策略评估说白了,就是给定一个策略 π,我们算出每个状态的价值 Vπ(s)。怎么算?用贝尔曼期望方程迭代求解。

# 策略评估的伪代码
def policy_evaluation(pi, P, R, gamma, theta):
    V = zeros(num_states)
    while True:
        delta = 0
        for s in range(num_states):
            v = V[s]
            # 根据当前策略计算新价值
            a = pi[s]
            V[s] = sum(P[s][a][s'] * (R[s][a][s'] + gamma * V[s']) for s' in range(num_states))
            delta = max(delta, abs(v - V[s]))
        if delta < theta:
            break
    return V

嗯,这里要注意一个细节:收敛阈值 theta 怎么设?我个人的习惯是设 1e-6,但如果你追求速度,1e-4 也够用。我在项目中遇到过一个问题,阈值设得太小(1e-8),结果迭代了上千次还没收敛,白白浪费计算资源。

策略改进:让策略变得更好

评估完了,接下来就是改进。怎么改进?贪心策略!对于每个状态,我们选择能带来最大价值的动作。

# 策略改进的伪代码
def policy_improvement(V, P, R, gamma):
    policy = zeros(num_states, dtype=int)
    for s in range(num_states):
        # 计算每个动作的价值
        q_values = [sum(P[s][a][s'] * (R[s][a][s'] + gamma * V[s']) for s' in range(num_states)) 
                    for a in range(num_actions)]
        policy[s] = argmax(q_values)
    return policy

我曾经犯过一个低级错误——忘了考虑动作空间为空的情况。虽然理论上不会出现,但实际项目中数据可能有缺失,所以建议加个异常处理。

策略迭代的完整流程

把评估和改进串起来,就是完整的策略迭代算法:

def policy_iteration(P, R, gamma, theta):
    # 初始化随机策略
    pi = random_policy(num_states, num_actions)
    while True:
        # 策略评估
        V = policy_evaluation(pi, P, R, gamma, theta)
        # 策略改进
        pi_new = policy_improvement(V, P, R, gamma)
        # 检查是否收敛
        if (pi_new == pi).all():
            break
        pi = pi_new
    return pi, V

小技巧:策略迭代通常只需要几次迭代就能收敛。我见过一个网格世界问题,策略迭代只用了 3 次外循环就找到了最优策略,而值迭代跑了 50 多步。所以别被「迭代」两个字吓到,实际跑起来很快。

值迭代算法:一步到位

值迭代的思路更直接——我们不去显式地维护策略,而是直接更新价值函数。每次更新时,我们直接取所有动作中的最大值。

def value_iteration(P, R, gamma, theta):
    V = zeros(num_states)
    while True:
        delta = 0
        for s in range(num_states):
            v = V[s]
            # 直接取所有动作的最大值
            V[s] = max(sum(P[s][a][s'] * (R[s][a][s'] + gamma * V[s']) for s' in range(num_states))
                       for a in range(num_actions))
            delta = max(delta, abs(v - V[s]))
        if delta < theta:
            break
    # 从价值函数提取策略
    pi = policy_improvement(V, P, R, gamma)
    return pi, V

你看,值迭代把策略评估和策略改进合并成了一个步骤。每次更新 V[s] 的时候,其实就是在做「隐式」的策略改进——因为我们选了最大值,相当于默认采用了最优动作。

注意:值迭代的收敛条件跟策略评估一样,都是看价值函数的变化量。但值迭代的收敛速度通常比策略评估慢,因为每次更新只改一点点。我建议在值迭代中把 theta 设得稍微大一点,比如 1e-3,这样能快很多。

策略迭代 vs 值迭代:到底选哪个?

这个问题我经常被问到。说实话,没有绝对的答案,得看具体情况。我整理了一个对比表格,方便你快速决策:

对比维度 策略迭代 值迭代
迭代次数 少(通常 < 10 次) 多(可能上百次)
每次迭代计算量 大(需要完整策略评估) 小(一次扫描即可)
收敛速度 快(外循环少) 慢(需要很多步)
实现难度 中等 简单
适用场景 状态空间小,需要精确解 状态空间大,追求实现简单

我个人更倾向于:如果状态空间小于 1000,用策略迭代;如果大于 10000,用值迭代。中间那部分,看心情吧(笑)。

收敛性分析:为什么它们能收敛?

这个问题有点理论,但我尽量说得通俗点。策略迭代和值迭代能收敛,本质上是因为它们都在做「压缩映射」——每次迭代都在缩小价值函数的误差。

策略迭代的收敛性

策略迭代的收敛性很好理解:每次策略改进后,新策略的价值函数一定不差于旧策略。因为我们是贪心选择,所以 Vπ_new ≥ Vπ_old。而策略空间是有限的(状态数 × 动作数),所以最终一定会收敛到最优策略。

关键点:策略迭代在有限 MDP 中保证在有限步内收敛到最优策略。这个「有限步」通常很小,我见过的最极端情况也就 20 步左右。

值迭代的收敛性

值迭代的收敛性依赖于贝尔曼最优算子 T* 是压缩映射。说白了,每次应用 T*,价值函数的误差都会缩小 γ 倍(γ 是折扣因子)。所以只要 γ < 1,最终一定会收敛。

嗯,这里有个坑要注意:如果 γ = 1(无折扣),值迭代可能不收敛。我在项目中遇到过这个问题,当时没注意 γ 的设置,结果价值函数一直在震荡。后来把 γ 改成 0.99,问题就解决了。

知识体系总览

为了让你更直观地理解本章的知识结构,我画了一张流程图:

策略迭代与值迭代知识体系 策略迭代算法 策略评估 策略改进 循环直到策略不变 值迭代算法 直接更新价值函数(取最大值) 收敛后提取策略 核心对比 策略迭代:外循环少(3-10次),每次计算量大 值迭代:外循环多(50-200次),每次计算量小 收敛性:两者都保证收敛到最优解(有限MDP) 选择建议:状态空间小用策略迭代,大用值迭代

这张图把两个算法的流程和对比都画出来了。你可以看到,策略迭代是「评估→改进→评估→改进...」的循环,而值迭代是直接更新价值函数,最后再提取策略。

我的建议:刚开始学的时候,先手算一遍策略迭代,再手算一遍值迭代。找个 3×3 的网格世界,手动迭代 2-3 步,你就能深刻理解两者的区别。我当年就是这么学的,效果比看十遍书都好。

好了,这一章的内容就到这里。策略迭代和值迭代是强化学习的基石,理解了它们,后面的 Q-learning、SARSA 学起来就轻松多了。记住:策略迭代像「慢工出细活」,值迭代像「快刀斩乱麻」,各有各的妙处。


公众号:蓝海资料掘金营,微信deep3321