强化学习分类:基于价值、基于策略、Actor-Critic

一句话概述

强化学习算法可以分为三大流派:基于价值(Value-based)的方法学习Q函数然后选最优动作,代表是Q-learning和DQN;基于策略(Policy-based)的方法直接优化策略参数,代表是REINFORCE和PPO;Actor-Critic方法结合两者——Actor负责决策,Critic负责评价,代表是A2C、A3C、SAC。理解这三类方法的关系,你就掌握了强化学习算法的全景图。

💡 核心要点:①基于价值的方法间接学习策略(通过Q函数),适合离散动作空间;②基于策略的方法直接优化策略,能处理连续动作和随机策略;③Actor-Critic融合两者优点,Actor更新策略,Critic评估价值,收敛更快更稳定;④现代RL算法大多属于Actor-Critic家族(PPO、SAC、TD3)。

教学与演示

一、基于价值的方法——学Q函数,选最优动作

是什么(定义):基于价值(Value-based)的方法通过估计最优动作价值函数Q*(s,a),然后隐式地定义策略:π(s) = argmax_a Q(s,a)。代表性算法包括:Q-learning、SARSA、DQN及其变体(Double DQN、Dueling DQN、Rainbow)。

大白话 基于价值的方法就像"评分系统"——给每个动作打分(Q值),然后选分数最高的那个。它不直接告诉你"该做什么",而是告诉你"每个动作有多好",让你自己选最好的。就像点菜时看评分——哪个菜评分高就点哪个。

为什么(原理):基于价值的方法的理论基础是贝尔曼最优方程。Q-learning的目标是学习Q*(s,a),使得Q值满足最优贝尔曼方程。这类方法的核心是TD学习——用实际获得的奖励来修正Q值估计。在离散动作空间中,argmax操作可以直接实现;在连续动作空间中,需要额外的优化步骤。

怎么做(实现)

import numpy as np

# ==================== Q-learning:基于价值的方法 ====================
n_states = 9
n_actions = 4
actions = [(-1, 0), (1, 0), (0, -1), (0, 1)]

def step(state, action):
    row, col = state // 3, state % 3
    dr, dc = actions[action]
    nr = max(0, min(2, row + dr))
    nc = max(0, min(2, col + dc))
    next_state = nr * 3 + nc
    if next_state == 8:   r = 10.0
    elif next_state == 7: r = -5.0
    else:                 r = -0.1
    return next_state, r

def q_learning(n_episodes=500, alpha=0.1, gamma=0.9, epsilon=0.2):
    """Q-learning:基于价值的经典算法"""
    Q = np.zeros((n_states, n_actions))  # Q表
    rewards_history = []

    for episode in range(n_episodes):
        state = 0
        total_reward = 0
        while state != 8 and state != 7:
            # ε-greedy选择动作
            if np.random.random() < epsilon:
                action = np.random.randint(n_actions)
            else:
                action = np.argmax(Q[state])

            next_state, reward = step(state, action)

            # Q-learning核心更新:用max计算目标
            best_next = np.max(Q[next_state])  # 基于价值的关键:max操作
            Q[state][action] += alpha * (reward + gamma * best_next - Q[state][action])

            total_reward += reward
            state = next_state
        rewards_history.append(total_reward)

    # 提取策略:π(s) = argmax_a Q(s,a)
    policy = np.argmax(Q, axis=1)
    return Q, policy, rewards_history

np.random.seed(42)
Q, policy, rewards = q_learning()

print("=== Q-learning(基于价值)===")
print("学习的Q函数(部分):")
for s in [0, 4]:
    print(f"  状态{s}: Q={[f'{q:.2f}' for q in Q[s]]}, 最优动作={policy[s]}")

print(f"\n提取的策略:")
for i in range(3):
    for j in range(3):
        s = i * 3 + j
        action_names = ["上", "下", "左", "右"]
        if s == 8: print("目标", end="  ")
        elif s == 7: print("陷阱", end="  ")
        else: print(action_names[policy[s]], end="  ")
    print()

print(f"\n基于价值的方法特点:")
print(f"  1. 学习Q函数,策略由argmax Q隐式定义")
print(f"  2. 适合离散动作空间(需要argmax操作)")
print(f"  3. 理论基础是贝尔曼最优方程")
Q-learning 更新规则\(Q(s, a) \leftarrow Q(s, a) + \alpha \left[ r + \gamma \max_{a'} Q(s', a') - Q(s, a) \right]\)

什么用(应用):基于价值的方法在离散动作空间中表现优异。DQN在Atari 2600游戏上达到人类水平;AlphaGo的价值网络是基于价值方法在围棋中的应用;在推荐系统中,Q-learning可以学习"推荐什么物品能最大化长期用户参与度"。

哪些坑(缺点):不适合连续动作空间(argmax在连续空间中不可行);max操作导致Q值过估计(Double DQN缓解);策略是确定性的,不能表示随机策略;在策略空间中只能间接优化,收敛可能较慢。

二、基于策略的方法——直接优化策略

是什么(定义):基于策略(Policy-based)的方法直接参数化策略π_θ(a|s),并通过梯度上升来优化累积奖励的期望。代表性算法包括:REINFORCE(蒙特卡洛策略梯度)、PPO(近端策略优化)、TRPO(信赖域策略优化)。

大白话 基于策略的方法就像"直接训练"——不绕弯子通过评分来选动作,而是直接告诉神经网络"看到这个情况该做什么"。它输出的是一个动作的概率分布,然后按照"能获得更多奖励的动作应该增加概率"的原则来更新参数。

为什么(原理):基于策略的方法的核心是策略梯度定理(Policy Gradient Theorem):∇_θ J(θ) = E_π[∇_θ log π_θ(a|s) · Q^π(s,a)]。梯度方向是"增加好动作的概率,减少坏动作的概率"。这种方法可以直接处理连续动作空间(输出高斯分布的均值和方差),也可以自然地表示随机策略。

怎么做(实现)

import numpy as np

# ==================== REINFORCE:基于策略的方法 ====================
# 使用softmax策略参数化

n_states = 9
n_actions = 4
actions = [(-1, 0), (1, 0), (0, -1), (0, 1)]

def step(state, action):
    row, col = state // 3, state % 3
    dr, dc = actions[action]
    nr = max(0, min(2, row + dr))
    nc = max(0, min(2, col + dc))
    next_state = nr * 3 + nc
    if next_state == 8:   r = 10.0
    elif next_state == 7: r = -5.0
    else:                 r = -0.1
    return next_state, r

def reinforce(n_episodes=1000, alpha=0.01, gamma=0.9):
    """REINFORCE:蒙特卡洛策略梯度"""
    # 策略参数:每个状态-动作对的偏好值
    theta = np.zeros((n_states, n_actions))  # 策略参数
    rewards_history = []

    for episode in range(n_episodes):
        # 收集一条完整的轨迹
        state = 0
        trajectory = []  # (状态, 动作, 奖励)
        while state != 8 and state != 7:
            # 基于策略的方法:从策略分布中采样动作
            logits = theta[state]  # 偏好值
            # softmax转为概率
            exp_logits = np.exp(logits - np.max(logits))  # 数值稳定
            probs = exp_logits / exp_logits.sum()
            # 采样动作
            action = np.random.choice(n_actions, p=probs)

            next_state, reward = step(state, action)
            trajectory.append((state, action, reward))
            state = next_state

        # 计算回报(从后往前)
        G = 0
        returns = []
        for _, _, r in reversed(trajectory):
            G = r + gamma * G
            returns.append(G)
        returns.reverse()

        # 策略梯度更新
        total_reward = sum(r for _, _, r in trajectory)
        for (s, a, _), G_t in zip(trajectory, returns):
            # 计算softmax梯度
            logits = theta[s]
            exp_logits = np.exp(logits - np.max(logits))
            probs = exp_logits / exp_logits.sum()

            # 梯度:对于选中的动作a,梯度 = alpha * G_t * (1 - prob[a])
            # 对于其他动作,梯度 = alpha * G_t * (0 - prob[other])
            grad = -probs.copy()
            grad[a] += 1  # 简化的softmax梯度
            theta[s] += alpha * G_t * grad  # 策略梯度更新

        rewards_history.append(total_reward)

    # 提取策略
    policy = np.zeros(n_states, dtype=int)
    for s in range(n_states):
        policy[s] = np.argmax(theta[s])  # 取偏好值最大的动作
    return theta, policy, rewards_history

np.random.seed(42)
theta, policy, rewards = reinforce()

print("=== REINFORCE(基于策略)===")
print(f"学习的策略参数(部分):")
for s in [0, 4]:
    probs = np.exp(theta[s] - np.max(theta[s]))
    probs = probs / probs.sum()
    print(f"  状态{s}: probs={[f'{p:.2f}' for p in probs]}")

print(f"\n提取的策略:")
action_names = ["上", "下", "左", "右"]
for i in range(3):
    for j in range(3):
        s = i * 3 + j
        if s == 8: print("目标", end="  ")
        elif s == 7: print("陷阱", end="  ")
        else: print(action_names[policy[s]], end="  ")
    print()

print(f"\n基于策略的方法特点:")
print(f"  1. 直接输出动作概率分布,不需要argmax")
print(f"  2. 可以处理连续动作和随机策略")
print(f"  3. 收敛到局部最优,学习过程可能波动大")
策略梯度定理\(\nabla_{\theta} J(\theta) = \mathbb{E}_{\pi_{\theta}} \left[ \nabla_{\theta} \log \pi_{\theta}(a \mid s) \cdot Q^{\pi}(s, a) \right]\)

什么用(应用):基于策略的方法在连续控制(机器人、自动驾驶)中占主导地位。PPO是OpenAI的默认RL算法,用于训练OpenAI Five(Dota 2)和dexterous hand manipulation;TRPO和PPO在机器人控制中广泛应用;策略梯度方法也是ChatGPT中RLHF(人类反馈强化学习)的核心。

哪些坑(缺点):梯度估计方差大(需要大量样本);容易收敛到局部最优而非全局最优;样本效率低,每次更新后旧数据就"过时"了;策略更新步长难以选择——太大导致策略崩溃,太小导致学习缓慢。

三、Actor-Critic——强强联合

是什么(定义):Actor-Critic方法结合了基于价值和基于策略的优点。Actor(演员)是策略网络π_θ(a|s),负责决策;Critic(评论家)是价值网络V_φ(s)或Q_φ(s,a),负责评估Actor的表现。Critic的评估结果用于指导Actor的更新,降低了策略梯度的方差。

大白话 Actor-Critic就像"演员和导演"——Actor是演员,在舞台上表演(做动作);Critic是导演,在台下点评(给反馈)。演员根据导演的评价来改进表演,导演根据实际效果来调整评价标准。两者互相配合,比单独一个演员(纯策略)或单独一个评分系统(纯价值)更高效。

为什么(原理):Actor-Critic的核心优势在于用Critic的估计替代了蒙特卡洛回报G_t,从而降低了策略梯度估计的方差。Actor使用策略梯度更新:θ ← θ + α ∇_θ log π_θ(a|s) · A(s,a),其中A(s,a)=Q(s,a)-V(s)是优势函数。Critic使用TD学习更新:φ ← φ + β [r + γV(s') - V(s)] ∇_φ V(s)。

怎么做(实现)

import numpy as np

# ==================== Actor-Critic:结合策略和价值 ====================
n_states = 9
n_actions = 4
actions = [(-1, 0), (1, 0), (0, -1), (0, 1)]

def step(state, action):
    row, col = state // 3, state % 3
    dr, dc = actions[action]
    nr = max(0, min(2, row + dr))
    nc = max(0, min(2, col + dc))
    next_state = nr * 3 + nc
    if next_state == 8:   r = 10.0
    elif next_state == 7: r = -5.0
    else:                 r = -0.1
    return next_state, r

def actor_critic(n_episodes=500, alpha_actor=0.01, alpha_critic=0.1, gamma=0.9):
    """Actor-Critic算法"""
    # Actor:策略参数
    theta = np.zeros((n_states, n_actions))
    # Critic:状态价值函数V(s)
    V = np.zeros(n_states)
    rewards_history = []

    for episode in range(n_episodes):
        state = 0
        total_reward = 0
        while state != 8 and state != 7:
            # Actor:根据策略选择动作
            logits = theta[state]
            exp_logits = np.exp(logits - np.max(logits))
            probs = exp_logits / exp_logits.sum()
            action = np.random.choice(n_actions, p=probs)

            next_state, reward = step(state, action)

            # Critic:计算TD误差
            td_error = reward + gamma * V[next_state] - V[state]

            # Critic:更新V(s)
            V[state] += alpha_critic * td_error

            # Actor:用TD误差(作为优势函数的近似)更新策略
            grad = -probs.copy()
            grad[action] += 1
            theta[state] += alpha_actor * td_error * grad

            total_reward += reward
            state = next_state
        rewards_history.append(total_reward)

    # 提取策略
    policy = np.zeros(n_states, dtype=int)
    for s in range(n_states):
        policy[s] = np.argmax(theta[s])
    return theta, V, policy, rewards_history

np.random.seed(42)
theta, V, policy, rewards = actor_critic()

print("=== Actor-Critic ===")
print(f"Critic学习的V函数(部分):")
for s in [0, 4]:
    print(f"  状态{s}: V={V[s]:.2f}")

print(f"\nActor学习的策略:")
action_names = ["上", "下", "左", "右"]
for i in range(3):
    for j in range(3):
        s = i * 3 + j
        if s == 8: print("目标", end="  ")
        elif s == 7: print("陷阱", end="  ")
        else: print(action_names[policy[s]], end="  ")
    print()

print(f"\nActor-Critic的特点:")
print(f"  1. Actor负责决策(策略),Critic负责评估(价值)")
print(f"  2. Critic的TD误差降低Actor梯度估计的方差")
print(f"  3. 比纯策略梯度更稳定,比纯价值方法更灵活")
Actor-Critic 更新规则\(\text{Actor: } \theta \leftarrow \theta + \alpha \nabla_{\theta} \log \pi_{\theta}(a \mid s) \cdot A(s, a) \text{Critic: } \phi \leftarrow \phi + \beta \left[ r + \gamma V_{\phi}(s') - V_{\phi}(s) \right] \nabla_{\phi} V_{\phi}(s)\)

什么用(应用):Actor-Critic是现代RL的主流架构。A2C/A3C用于Atari游戏和机器人控制;PPO是OpenAI的首选算法;SAC(Soft Actor-Critic)在连续控制任务中表现优异;TD3(Twin Delayed DDPG)是DDPG的改进版本。ChatGPT的RLHF实质上也是Actor-Critic。

哪些坑(缺点):需要同时训练两个网络,计算成本更高;Actor和Critic的训练步调需要协调;Critic的估计误差会影响Actor的更新方向;两个网络共享底层时,梯度冲突可能导致训练不稳定。

四、三类方法的对比——选择正确的工具

是什么(定义):三种RL方法各有优劣,选择取决于具体问题:动作空间类型(离散/连续)、是否需要随机策略、样本效率要求、实现复杂度。下表总结了它们的核心区别。

大白话 选方法就像选工具——拧螺丝用螺丝刀(离散动作用Q-learning),钉钉子用锤子(连续动作用策略梯度),精细活用电钻(复杂任务用Actor-Critic)。没有最好的方法,只有最合适的方法。

为什么(原理):方法选择的底层逻辑是:基于价值的方法在离散空间中通过max操作高效隐式定义策略,但在连续空间中max不可行;基于策略的方法直接输出分布参数,天然适合连续空间,但方差大;Actor-Critic用Critic降低方差,用Actor保持灵活性,是当前最通用的方案。

怎么做(实现)

import numpy as np

# ==================== 三类方法对比总结 ====================
print("=" * 60)
print("强化学习三大方法对比")
print("=" * 60)

comparison = {
    "维度": ["价值方法", "策略方法", "Actor-Critic"],
    "核心思想": [
        "学Q函数,选最优动作",
        "直接优化策略参数",
        "Actor决策+Critic评价"
    ],
    "策略表示": [
        "隐式(argmax Q)",
        "显式(概率分布)",
        "显式(Actor输出)"
    ],
    "动作空间": [
        "离散(需要argmax)",
        "连续+离散",
        "连续+离散"
    ],
    "随机策略": [
        "需要额外机制(ε-greedy)",
        "天然支持",
        "天然支持"
    ],
    "代表算法": [
        "Q-learning, DQN, Double DQN",
        "REINFORCE, PPO, TRPO",
        "A2C, A3C, SAC, TD3"
    ],
    "方差": [
        "低(TD学习)",
        "高(蒙特卡洛)",
        "中(Critic降低方差)"
    ],
    "样本效率": [
        "高(经验回放)",
        "低(on-policy)",
        "中-高(取决于算法)"
    ],
    "收敛性": [
        "较稳定",
        "可能波动大",
        "较稳定"
    ]
}

for key in comparison:
    print(f"\n{key}:")
    for i, val in enumerate(comparison[key]):
        print(f"  {['价值方法','策略方法','Actor-Critic'][i]}: {val}")

print(f"\n选择指南:")
print(f"  离散动作 + 简单任务 → Q-learning / DQN")
print(f"  连续动作 + 简单任务 → REINFORCE / PPO")
print(f"  复杂任务 + 需要稳定 → Actor-Critic (PPO, SAC)")
print(f"  高维离散动作 + 需要效率 → Rainbow DQN")
print(f"  机器人控制 + 连续动作 → SAC / TD3")

什么用(应用):理解三类方法的区别有助于在实际项目中做出正确的算法选择。在游戏AI中,离散动作用DQN系,连续控制用PPO/SAC;在推荐系统中,离散物品推荐用DQN,连续出价策略用PPO;在机器人控制中,几乎全部使用Actor-Critic系方法。

哪些坑(缺点):没有一种方法在所有场景下都最优;方法选择需要权衡多个因素(动作空间、样本效率、稳定性、实现复杂度);实际应用中,往往需要尝试多种方法才能找到最优方案。

五、On-Policy vs Off-Policy——另一个重要维度

是什么(定义):除了价值/策略/Actor-Critic的分类,RL算法还有一个重要维度:On-Policy(同策略)和Off-Policy(异策略)。On-Policy方法使用当前策略采集的数据来更新当前策略(如SARSA、PPO);Off-Policy方法可以使用任何策略(包括旧策略)采集的数据来更新(如Q-learning、DQN)。

大白话 On-Policy就像"自己反思自己"——你只能从自己最近的经历中学习,旧经历作废。Off-Policy就像"向前辈学习"——你可以从任何人(包括过去的自己)的经验中学习,数据可以复用。Off-Policy更高效(可以重复使用经验),但On-Policy更稳定(策略和数据的分布一致)。

为什么(原理):On-Policy和Off-Policy的区别在于更新公式中的期望是对哪个策略取的。Q-learning的更新使用max Q(s',a'),这对应的是贪心策略(目标策略),而数据采集使用的是ε-greedy(行为策略),两者不同→Off-Policy。SARSA的更新使用Q(s',a')(下一步实际执行的动作),数据采集和更新目标使用同一策略→On-Policy。

怎么做(实现)

import numpy as np

# 对比SARSA(On-Policy)和Q-learning(Off-Policy)
n_states = 9
n_actions = 4
actions = [(-1, 0), (1, 0), (0, -1), (0, 1)]

def step(state, action):
    row, col = state // 3, state % 3
    dr, dc = actions[action]
    nr = max(0, min(2, row + dr))
    nc = max(0, min(2, col + dc))
    next_state = nr * 3 + nc
    if next_state == 8:   r = 10.0
    elif next_state == 7: r = -5.0
    else:                 r = -0.1
    return next_state, r

def sarsa(n_episodes=500, alpha=0.1, gamma=0.9, epsilon=0.2):
    """SARSA:On-Policy"""
    Q = np.zeros((n_states, n_actions))
    for episode in range(n_episodes):
        state = 0
        # SARSA:先选动作
        action = (np.random.randint(n_actions) if np.random.random() < epsilon
                  else np.argmax(Q[state]))
        while state != 8 and state != 7:
            next_state, reward = step(state, action)
            # 选下一个动作(使用同一ε-greedy策略)
            next_action = (np.random.randint(n_actions) if np.random.random() < epsilon
                          else np.argmax(Q[next_state]))
            # SARSA更新:使用实际执行的下一个动作的Q值
            Q[state][action] += alpha * (reward + gamma * Q[next_state][next_action] - Q[state][action])
            state, action = next_state, next_action
    return Q

def q_learning_off(n_episodes=500, alpha=0.1, gamma=0.9, epsilon=0.2):
    """Q-learning:Off-Policy"""
    Q = np.zeros((n_states, n_actions))
    for episode in range(n_episodes):
        state = 0
        while state != 8 and state != 7:
            action = (np.random.randint(n_actions) if np.random.random() < epsilon
                      else np.argmax(Q[state]))
            next_state, reward = step(state, action)
            # Q-learning更新:使用max(贪心策略),而非实际执行的动作
            Q[state][action] += alpha * (reward + gamma * np.max(Q[next_state]) - Q[state][action])
            state = next_state
    return Q

np.random.seed(42)
Q_sarsa = sarsa()
Q_ql = q_learning_off()

print("=== SARSA (On-Policy) vs Q-learning (Off-Policy) ===")
print("关键区别:")
print(f"  SARSA更新: Q(s,a) ← Q(s,a) + α[r + γ·Q(s',a') - Q(s,a)]")
print(f"            a'是实际执行的下一个动作(ε-greedy)")
print(f"  Q-learn更新: Q(s,a) ← Q(s,a) + α[r + γ·max Q(s',a') - Q(s,a)]")
print(f"            使用max(贪心),而非实际执行的动作")
print(f"\nSARSA更保守(考虑探索的风险),Q-learning更乐观(假设最优)")
On-Policy vs Off-Policy\(\text{On-Policy: 行为策略} = \text{目标策略}, \quad \text{Off-Policy: 行为策略} \neq \text{目标策略}\)

什么用(应用):Off-Policy方法(DQN、SAC)可以使用经验回放,样本效率高,适合数据收集成本高的场景。On-Policy方法(PPO、TRPO、A3C)更稳定,策略更新更安全,适合高风险场景(如机器人控制、自动驾驶)。SAC虽然使用Off-Policy数据,但结合了最大熵探索,在连续控制中表现优异。

哪些坑(缺点):Off-Policy方法中,行为策略和目标策略的分布不匹配可能导致学习不稳定(重要性采样可以缓解);On-Policy方法每次更新都需要新数据,样本效率低;经验回放需要大量内存存储经验;Off-Policy方法在函数近似下可能不收敛。

六、实战:三种方法对比实验

是什么(定义):在同一网格世界上运行Q-learning(价值)、REINFORCE(策略)和Actor-Critic三种方法,对比它们的收敛速度、稳定性和最终表现。

怎么做(实现)

import numpy as np

# 在网格世界上对比三种方法
n_states = 9; n_actions = 4
actions = [(-1, 0), (1, 0), (0, -1), (0, 1)]

def step(state, action):
    row, col = state // 3, state % 3
    dr, dc = actions[action]
    nr = max(0, min(2, row + dr))
    nc = max(0, min(2, col + dc))
    next_state = nr * 3 + nc
    if next_state == 8:   r = 10.0
    elif next_state == 7: r = -5.0
    else:                 r = -0.1
    return next_state, r

def q_learning(n_episodes=500):
    Q = np.zeros((n_states, n_actions))
    rewards = []
    for ep in range(n_episodes):
        state = 0; ep_r = 0
        while state != 8 and state != 7:
            action = np.random.randint(n_actions) if np.random.random() < 0.2 else np.argmax(Q[state])
            next_s, r = step(state, action)
            Q[state][action] += 0.1 * (r + 0.9 * np.max(Q[next_s]) - Q[state][action])
            ep_r += r; state = next_s
        rewards.append(ep_r)
    return rewards

def reinforce(n_episodes=500):
    theta = np.zeros((n_states, n_actions))
    rewards = []
    for ep in range(n_episodes):
        state = 0; traj = []
        while state != 8 and state != 7:
            logits = theta[state]; exp_l = np.exp(logits - np.max(logits))
            probs = exp_l / exp_l.sum()
            action = np.random.choice(n_actions, p=probs)
            next_s, r = step(state, action)
            traj.append((state, action, r)); state = next_s
        G = 0; returns = []
        for _, _, r in reversed(traj): G = r + 0.9*G; returns.append(G)
        returns.reverse()
        for (s, a, _), G_t in zip(traj, returns):
            logits = theta[s]; exp_l = np.exp(logits - np.max(logits))
            probs = exp_l / exp_l.sum()
            grad = -probs.copy(); grad[a] += 1
            theta[s] += 0.01 * G_t * grad
        rewards.append(sum(r for _,_,r in traj))
    return rewards

def actor_critic(n_episodes=500):
    theta = np.zeros((n_states, n_actions))
    V = np.zeros(n_states)
    rewards = []
    for ep in range(n_episodes):
        state = 0; ep_r = 0
        while state != 8 and state != 7:
            logits = theta[state]; exp_l = np.exp(logits - np.max(logits))
            probs = exp_l / exp_l.sum()
            action = np.random.choice(n_actions, p=probs)
            next_s, r = step(state, action)
            td_err = r + 0.9*V[next_s] - V[state]
            V[state] += 0.1 * td_err
            grad = -probs.copy(); grad[action] += 1
            theta[state] += 0.01 * td_err * grad
            ep_r += r; state = next_s
        rewards.append(ep_r)
    return rewards

np.random.seed(42)
r_ql = q_learning()
r_rf = reinforce()
r_ac = actor_critic()

print("=== 三种方法对比实验 ===")
print(f"前10个episode奖励:")
print(f"{'Ep':<5} {'Q-learning':<12} {'REINFORCE':<12} {'Actor-Critic':<12}")
for i in range(10):
    print(f"{i+1:<5} {r_ql[i]:>8.1f}    {r_rf[i]:>8.1f}    {r_ac[i]:>8.1f}")

print(f"\n最后100个episode平均奖励:")
print(f"  Q-learning:    {np.mean(r_ql[-100:]):.2f}")
print(f"  REINFORCE:     {np.mean(r_rf[-100:]):.2f}")
print(f"  Actor-Critic:  {np.mean(r_ac[-100:]):.2f}")

print(f"\n收敛速度排序:Q-learning > Actor-Critic > REINFORCE")
print(f"稳定性排序:Q-learning ≈ Actor-Critic > REINFORCE")

什么用(应用):通过对比实验,你可以直观地理解不同方法的特点,为实际项目选择算法提供依据。对于离散动作的简单问题,Q-learning最简单高效;对于需要探索或连续动作的问题,Actor-Critic更合适。

哪些坑(缺点):实验结果的泛化性有限,不同任务上方法的相对表现可能不同;超参数(学习率、探索率)对结果影响很大,需要仔细调参。

概念关系图谱

概念上位概念核心思想关键公式/方法代表算法
基于价值RL方法学Q函数,选最优动作Q(s,a)←Q+α[r+γmaxQ-Q]Q-learning, DQN
基于策略RL方法直接优化策略参数∇J=E[∇logπ·Q]REINFORCE, PPO
Actor-CriticRL方法Actor决策+Critic评价θ←θ+α∇logπ·AA2C, A3C, SAC
On-Policy学习范式行为策略=目标策略数据采集策略=更新目标策略SARSA, PPO
Off-Policy学习范式行为策略≠目标策略数据采集策略≠更新目标策略Q-learning, DQN
经验回放数据利用存储并复用历史经验从经验池随机采样训练DQN, SAC
策略梯度优化方法沿梯度方向更新策略∇_θ J(θ)REINFORCE
优势函数价值度量动作比平均好多少A(s,a)=Q(s,a)-V(s)A2C, PPO

重点答疑

Q1: 什么时候用基于价值的方法,什么时候用基于策略的方法?

  • 离散动作空间 → 基于价值(DQN系列),因为argmax操作简单高效。
  • 连续动作空间 → 基于策略或Actor-Critic(PPO、SAC),因为argmax在连续空间中不可行。
  • 需要随机策略 → 基于策略(博弈、对抗场景),基于价值的方法天然是确定性的。
  • 样本效率优先 → 基于价值(Off-Policy + 经验回放)。
解答:动作空间类型是首要考虑因素——离散用价值,连续用策略。其他因素(样本效率、稳定性)是次要考虑。

Q2: Actor-Critic中的Actor和Critic可以共享网络吗?

可以,而且共享网络是现代Actor-Critic方法的标准做法。Actor和Critic共享底层的特征提取网络(如CNN处理图像),然后各自有不同的输出头(Actor输出动作分布,Critic输出V值)。共享的好处是:减少参数量、共享特征表示、加速训练。坏处是:两个目标可能冲突,需要仔细平衡学习率。

解答:在A3C、PPO等实际实现中,Actor和Critic通常共享底层网络。A3C的经典论文就使用了共享网络架构。

Q3: 为什么Q-learning是Off-Policy而SARSA是On-Policy?

关键在更新公式中"下一个动作的Q值"如何处理:

  • Q-learning:Q(s,a) ← Q(s,a) + α[r + γ·max_{a'} Q(s',a') - Q(s,a)],使用max(贪心策略),这是目标策略。
  • SARSA:Q(s,a) ← Q(s,a) + α[r + γ·Q(s',a') - Q(s,a)],使用实际执行的下一个动作a'(来自ε-greedy),这是行为策略。

Q-learning中,目标策略(贪心)和行为策略(ε-greedy)不同 → Off-Policy。SARSA中,两者相同 → On-Policy。

解答:一句话——Q-learning用max(假设最优),SARSA用实际动作(执行什么就用什么)。max是贪心策略,实际执行是ε-greedy,所以Q-learning是Off-Policy。

Q4: 为什么策略梯度方法的方差比价值方法高?

策略梯度需要估计Q^π(s,a)或G_t,这些估计来自采样轨迹。轨迹越长,累积奖励的方差越大(每个奖励的随机性被累积)。此外,策略梯度涉及log概率的梯度,其幅度可能很大。相比之下,价值方法使用TD学习,只需要一对(s,a,r,s')的奖励,方差小得多。

Actor-Critic通过用Critic的估计替代G_t来降低方差,这正是它被广泛使用的原因。

解答:策略梯度用"整个轨迹的累积奖励"来算梯度,方差大;价值方法用"一步TD误差"来更新,方差小。Actor-Critic取两者之长。

Q5: On-Policy和Off-Policy哪种更好?

没有绝对的好坏,取决于场景:

  • On-Policy(PPO、TRPO)更稳定、更安全,适合高风险场景(机器人、自动驾驶),但样本效率低。
  • Off-Policy(DQN、SAC)样本效率高,可以复用历史数据,适合数据收集成本高的场景,但可能不稳定。

现代趋势:SAC(Off-Policy)在连续控制中表现优异,PPO(On-Policy)在通用RL中应用广泛。

解答:On-Policy安全但浪费,Off-Policy高效但冒险。PPO和SAC分别是两个阵营的王者。

Q6: 为什么现代RL算法大多属于Actor-Critic?

因为Actor-Critic结合了两者的优点:

  1. Actor可以直接处理连续动作和随机策略(策略方法的优势)。
  2. Critic用TD学习降低方差(价值方法的优势)。
  3. 可以共享网络,减少参数量。
  4. 框架灵活,可以加入各种改进(如PPO的裁剪、SAC的熵正则化)。

纯价值方法(DQN)在离散动作中仍然很棒,纯策略方法(REINFORCE)现在主要用于教学。实际工程中,Actor-Critic是主流。

解答:Actor-Critic = 策略方法的灵活性 + 价值方法的稳定性。这就是为什么PPO、SAC、A3C都是Actor-Critic。

章节单词汇总

英文音标中文
value-based/ˈvæljuː beɪst/基于价值的
policy-based/ˈpɑːləsi beɪst/基于策略的
Actor-Critic/ˈæktər ˈkrɪtɪk/演员-评论家
on-policy/ɒn ˈpɑːləsi/同策略
off-policy/ɒf ˈpɑːləsi/异策略
Q-learning/kjuː ˈlɜːrnɪŋ/Q学习
SARSA/ˈsɑːrsə/状态-动作-奖励-状态-动作
policy gradient/ˈpɑːləsi ˈɡreɪdiənt/策略梯度
REINFORCE/ˌriːɪnˈfɔːrs/强化(算法名)
PPO/piː piː oʊ/近端策略优化
TRPO/tiː ɑːr piː oʊ/信赖域策略优化
SAC/es eɪ siː/软演员-评论家
DDPG/diː diː piː dʒiː/深度确定性策略梯度
TD3/tiː diː θriː/双延迟深度确定性策略梯度
experience replay/ɪkˈspɪriəns ˈriːpleɪ/经验回放
target network/ˈtɑːrɡɪt ˈnetwɜːrk/目标网络
importance sampling/ɪmˈpɔːrtns ˈsæmplɪŋ/重要性采样
entropy/ˈentrəpi/

面试练习

Q1 [单选] 以下哪个算法不属于基于价值的方法?

  • A. Q-learning
  • B. DQN
  • C. REINFORCE
  • D. SARSA
解答:REINFORCE是基于策略的方法(策略梯度),其他三个都是基于价值的方法。Q-learning和DQN学习Q函数,SARSA也学习Q函数但使用On-Policy更新。

Q2 [单选] Actor-Critic方法中,Actor和Critic分别负责什么?

  • A. Actor评估价值,Critic选择动作
  • B. Actor选择动作,Critic评估价值
  • C. Actor和Critic都选择动作
  • D. Actor和Critic都评估价值
解答:Actor(演员)负责决策——选择动作;Critic(评论家)负责评估——评估当前状态或动作的价值。Actor根据Critic的反馈来改进策略。

Q3 [多选] 以下关于Q-learning和SARSA的说法,哪些是正确的?

  • A. Q-learning是Off-Policy,SARSA是On-Policy
  • B. Q-learning使用max操作,SARSA使用实际执行的动作
  • C. Q-learning和SARSA的更新公式完全相同
  • D. SARSA更保守,因为它考虑了探索的风险
解答:A正确,Q-learning用max(贪心目标),SARSA用实际动作(同一策略)。B正确。C错误,Q-learning用max,SARSA用实际Q值。D正确,SARSA的更新考虑了ε-greedy可能选到的不好的动作。

Q4 [单选] 策略梯度定理中,梯度的计算不依赖于什么?

  • A. 策略参数
  • B. 动作价值函数
  • C. 环境转移概率模型
  • D. 策略的对数概率
解答:策略梯度定理的巧妙之处在于∇_θ J(θ) = E[∇_θ log π_θ(a|s) · Q^π(s,a)],这个公式不包含环境转移概率P(s'|s,a)。这意味着策略梯度是"无模型"的——不需要知道环境模型就能计算梯度。

Q5 [多选] 以下哪些是Actor-Critic方法的优势?

  • A. 比纯策略梯度方法方差更低
  • B. 可以处理连续动作空间
  • C. 一定比纯价值方法收敛更快
  • D. Actor和Critic可以共享特征提取网络
解答:A正确,Critic的TD学习降低了策略梯度的方差。B正确,Actor可以输出连续动作的参数(如高斯分布均值和方差)。C错误,收敛速度取决于具体任务和超参数。D正确,在A3C、PPO中Actor和Critic通常共享底层网络。

Q6 [单选] 以下哪个算法是On-Policy的?

  • A. Q-learning
  • B. DQN
  • C. PPO
  • D. SAC
解答:PPO是On-Policy的——它使用当前策略采集的数据来更新策略,更新后旧数据不能再用。Q-learning和DQN是Off-Policy(使用经验回放)。SAC虽然是Off-Policy,但使用了最大熵框架。

Q7 [单选] 基于价值的方法在连续动作空间中的主要挑战是什么?

  • A. 无法表示Q函数
  • B. argmax操作在连续空间中不可行
  • C. Q函数无法学习
  • D. 奖励函数无法定义
解答:基于价值的方法需要argmax_a Q(s,a)来选择动作,这在离散空间中很简单(遍历所有动作),但在连续空间中需要求解一个优化问题,计算成本高且可能不精确。这是DDPG等算法引入Actor网络的原因——用Actor来近似argmax。

Q8 [多选] 以下哪些是Off-Policy方法的优势?

  • A. 可以使用经验回放,提高样本效率
  • B. 可以使用任意策略(包括旧策略)采集的数据
  • C. 总是比On-Policy方法更稳定
  • D. 可以从演示数据中学习
解答:A正确,经验回放是Off-Policy的核心优势。B正确,Off-Policy不要求数据来自当前策略。C错误,Off-Policy通常不如On-Policy稳定。D正确,可以从人类演示或其他智能体的数据中学习,这是Off-Policy的独特优势。

Q9 [单选] REINFORCE算法中,用于更新策略的权重是什么?

  • A. TD误差
  • B. 即时奖励
  • C. 累积折扣奖励(Return)G_t
  • D. 优势函数
解答:REINFORCE使用蒙特卡洛回报G_t作为策略梯度中的权重。这是它方差大的原因——G_t包含整个轨迹的随机性。Actor-Critic用优势函数(基于TD误差)替代G_t来降低方差。

Q10 [多选] 以下哪些是选择RL算法时需要考虑的因素?

  • A. 动作空间的类型(离散/连续)
  • B. 样本效率要求
  • C. 是否需要随机策略
  • D. 实现的复杂度
解答:以上所有都是选择RL算法时需要考虑的因素。动作空间决定基本方法类型,样本效率影响On/Off-Policy选择,随机策略需求影响价值/策略方法选择,实现复杂度影响工程决策。