强化学习


文档摘要

强化学习 强化学习训练智能体通过试错、最大化累积奖励来做序列决策。本文件涵盖 MDP、价值函数、贝尔曼方程、Q-learning、策略梯度、actor-critic 方法、PPO 和 RLHF——它是下棋打游戏智能体以及语言模型对齐背后的框架。 监督学习需要带标签的数据。无监督学习从无标签数据里找模式。强化学习(reinforcement learning, RL)与两者都不同:智能体通过与环境的交互来学习——采取行动、获得奖励。没有所谓的正确标签;智能体必须通过试错自己发现好行为。 想象教一只狗新把戏。你不会给它看一份"正确行为"的数据集。相反,它去尝试各种动作,做得好你就给零食,久而久之它就摸索出你想要什么。RL 把这个过程形式化了。 RL 的设定有五个核心要素。

强化学习

强化学习训练智能体通过试错、最大化累积奖励来做序列决策。本文件涵盖 MDP、价值函数、贝尔曼方程、Q-learning、策略梯度、actor-critic 方法、PPO 和 RLHF——它是下棋打游戏智能体以及语言模型对齐背后的框架。

  • 监督学习需要带标签的数据。无监督学习从无标签数据里找模式。**强化学习(reinforcement learning, RL)**与两者都不同:智能体通过与环境的交互来学习——采取行动、获得奖励。没有所谓的正确标签;智能体必须通过试错自己发现好行为。

  • 想象教一只狗新把戏。你不会给它看一份"正确行为"的数据集。相反,它去尝试各种动作,做得好你就给零食,久而久之它就摸索出你想要什么。RL 把这个过程形式化了。

  • RL 的设定有五个核心要素。智能体(agent)是学习和决策者。环境(environment)是智能体之外、与之交互的一切。在每个时间步,智能体观察到一个状态(state) s_t,选择一个动作(action) a_t,得到一个奖励(reward) r_t,并转移到新状态 s_{t+1}。智能体的目标是最大化它随时间累积的总奖励。

智能体-环境循环:智能体观察状态、采取动作、获得奖励,环境转移到新状态

  • 策略(policy) \pi 是智能体的策略:从状态到动作的映射。确定性策略对每个状态给出一个动作:a = \pi(s)。随机策略给出动作上的概率分布:\pi(a \mid s)。RL 的目标是找到最优策略——那个最大化期望累积奖励的策略。

  • RL 的数学框架是马尔可夫决策过程(Markov Decision Process, MDP),由一个元组 (S, A, P, R, \gamma) 定义:状态集合 S、动作集合 A、转移概率 P(s' \mid s, a)、奖励函数 R(s, a) 以及折扣因子 \gamma

  • 马尔可夫性(Markov property)(来自第 5 章)说的是:未来只取决于当前状态,而不依赖于你是怎么到达这里的:P(s_{t+1} \mid s_t, a_t, s_{t-1}, \ldots) = P(s_{t+1} \mid s_t, a_t)。这意味着状态已经包含了做决策所需的全部信息。

  • 折扣因子 \gamma \in [0, 1) 决定了智能体有多看重未来奖励相对于眼前奖励。从时刻 t 起的折扣回报是:

G_t = r_t + \gamma r_{t+1} + \gamma^2 r_{t+2} + \cdots = \sum_{k=0}^{\infty} \gamma^k r_{t+k}
  • \gamma = 0 时,智能体完全短视,只在乎下一个奖励。\gamma 接近 1 时,智能体目光长远。折扣因子还保证了级数收敛(只要奖励有界),这对数学上的良定性很重要。

  • **价值函数(value function)**估计身处某个状态(或在某状态下采取某个动作)有多好。状态价值函数 V^\pi(s) 是从状态 s 出发并遵循策略 \pi 的期望回报:

V^\pi(s) = \mathbb{E}_\pi \left[ G_t \mid s_t = s \right]
  • 动作价值函数 Q^\pi(s, a) 是从状态 s 出发、先采取动作 a、然后遵循 \pi 的期望回报:
Q^\pi(s, a) = \mathbb{E}_\pi \left[ G_t \mid s_t = s, a_t = a \right]
  • 两者的关系:V^\pi(s) = \sum_a \pi(a \mid s) \, Q^\pi(s, a)。状态价值是动作价值按策略加权的平均。

  • **贝尔曼方程(Bellman equation)**表达了一种递归关系:一个状态的价值等于即时奖励加上下一状态的折扣价值。对于状态价值函数:

V^\pi(s) = \sum_a \pi(a \mid s) \sum_{s'} P(s' \mid s, a) \left[ R(s, a) + \gamma \, V^\pi(s') \right]
  • 对于最优价值函数 V^{*}(s),智能体总是选最好的动作:
V^{*}(s) = \max_a \sum_{s'} P(s' \mid s, a) \left[ R(s, a) + \gamma \, V^{*}(s') \right]
  • 类似地,Q^{*}贝尔曼最优性方程
Q^{*}(s, a) = \sum_{s'} P(s' \mid s, a) \left[ R(s, a) + \gamma \max_{a'} Q^{*}(s', a') \right]
  • 一旦你有了 Q^{*},最优策略就唾手可得:永远选 Q 值最高的那个动作:\pi^{*}(s) = \arg\max_a Q^{*}(s, a)

  • **动态规划(dynamic programming)**方法适用于你已知转移概率和奖励(完整模型)的 MDP。**策略评估(policy evaluation)**通过反复套用贝尔曼方程直至收敛,来计算给定策略的 V^\pi。**策略改进(policy improvement)**则拿价值函数构造一个更好的策略——按贪心方式行动:\pi'(s) = \arg\max_a \sum_{s'} P(s' \mid s, a)[R(s,a) + \gamma V^\pi(s')]

  • **策略迭代(policy iteration)**在评估和改进之间交替,直到策略不再变化。它保证收敛到最优策略。

  • **价值迭代(value iteration)**把两步合二为一:它反复套用贝尔曼最优性方程直到 V^{*} 收敛,然后再提取策略。

V(s) \leftarrow \max_a \sum_{s'} P(s' \mid s, a) \left[ R(s, a) + \gamma \, V(s') \right]
  • 动态规划要求已知 P(s' \mid s, a),这通常不现实。在大多数真实问题里,智能体并不知道环境的运行规律,只能与之交互。这就是**无模型(model-free)**方法的用武之地。

  • 时序差分(Temporal Difference, TD)学习在不知道模型的情况下从经验中学习。关键思想是自举(bootstrapping):与其等到一回合结束才算真实回报 G_t,不如用当前的价值函数来估计它:

V(s_t) \leftarrow V(s_t) + \alpha \left[ r_t + \gamma \, V(s_{t+1}) - V(s_t) \right]
  • 方括号里的那一项就是TD 误差(TD error)TD 目标r_t + \gamma V(s_{t+1}))与当前估计 V(s_t) 之差。如果 TD 误差为正,说明这个状态比预期更好,我们就抬高它的价值;为负则下调。

状态转移,显示 TD 目标:当前价值、奖励以及自举的下一状态价值,并附更新公式

  • TD 学习每走一步就更新一次(而不是等整回合结束),这使它比蒙特卡洛方法高效得多。它还适用于持续(非回合制)的环境。

  • SARSA(State-Action-Reward-State-Action)是把 TD 学习用在 Q 值上。智能体在状态 s 采取动作 a,观察到奖励 r 和下一状态 s',然后按自己的策略选择下一个动作 a'

Q(s, a) \leftarrow Q(s, a) + \alpha \left[ r + \gamma \, Q(s', a') - Q(s, a) \right]
  • SARSA 是**同策略(on-policy)**的:它用智能体实际采取的动作来更新,而这个动作里包含了探索。这使 SARSA 更保守,它学到的是把自身探索噪声也考虑在内的策略。

  • Q-learning 是最著名的 RL 算法。它像 SARSA,但用的不是智能体实际采取的动作,而是可能的最优动作:

Q(s, a) \leftarrow Q(s, a) + \alpha \left[ r + \gamma \max_{a'} Q(s', a') - Q(s, a) \right]
  • Q-learning 是**离策略(off-policy)**的:不管遵循的是什么策略,它都在学最优的 Q 值。智能体可以随机探索,却仍然学到最优的动作价值。这使 Q-learning 更激进、往往收敛更快,但它可能会高估价值。

  • **探索与利用(exploration vs exploitation)**是根本性的两难:智能体该利用它已知的(选估计价值最高的动作),还是去探索未知的动作(也许会更好)?

  • 最简单的策略是 epsilon-greedy(\epsilon-贪心):以概率 \epsilon 随机选一个动作(探索),以概率 1 - \epsilon 选贪心动作(利用)。常见的调度是从高 \epsilon(大量探索)开始,随时间衰减。

  • 表格方法(为每个状态-动作对在表里存一个值)适用于小的、离散的状态空间。对于大的或连续的状态空间,你需要函数近似。**深度 Q 网络(Deep Q-Network, DQN)**用一个神经网络来近似 Q(s, a; \theta),其中 \theta 是网络权重。

  • DQN 引入了两项关键的稳定化技术。经验回放(experience replay):不再从连续的转移中学习(它们高度相关),而是把转移存进一个回放缓冲区,训练时随机采样小批量。这打破了相关性并高效复用数据。

  • 目标网络(target network):用一个独立的、缓慢更新的网络副本来计算 TD 目标。没有它的话,每次更新网络目标都会移动,造成"自己追自己尾巴"式的不稳定。目标网络定期更新(每 N 步硬更新)或连续更新(软更新:\theta^{-} \leftarrow \tau\theta + (1-\tau)\theta^{-})。

  • DQN 的损失就是预测 Q 值与 TD 目标之间的 MSE:

\mathcal{L}(\theta) = \mathbb{E} \left[ \left( r + \gamma \max_{a'} Q(s', a'; \theta^{-}) - Q(s, a; \theta) \right)^2 \right]
  • 到此为止的所有方法都是先学价值函数,再从中导出策略。**策略梯度(policy gradient)**方法换个思路:直接把策略参数化为 \pi(a \mid s; \theta),通过对期望回报做梯度上升来优化它。

  • 策略梯度定理给出了期望回报关于策略参数的梯度:

\nabla_\theta J(\theta) = \mathbb{E}_\pi \left[ \nabla_\theta \log \pi(a \mid s; \theta) \cdot G_t \right]
  • 这句话的意思是:提高那些带来高回报的动作的概率,降低那些带来低回报的动作的概率。对数概率梯度指明了该往哪个方向改策略,而 G_t 决定了改多少。

  • REINFORCE 是最简单的策略梯度算法。跑一回合,为每步算出回报 G_t,然后更新:

\theta \leftarrow \theta + \alpha \, \nabla_\theta \log \pi(a_t \mid s_t; \theta) \cdot G_t
  • REINFORCE 方差很高,因为 G_t 是期望回报的一个嘈杂的单样本估计。常见的解决办法是减去一个基线(baseline)(通常是平均回报或学到的价值函数),在不引入偏差的情况下降低方差:
\theta \leftarrow \theta + \alpha \, \nabla_\theta \log \pi(a_t \mid s_t; \theta) \cdot (G_t - b)
  • **Actor-Critic(演员-评论家)**方法用两个网络。**演员(actor)**是策略 \pi(a \mid s; \theta)。**评论家(critic)**是价值函数 V(s; \phi),充当基线。优势 A_t = r_t + \gamma V(s_{t+1}) - V(s_t) 取代了 G_t - b
\theta \leftarrow \theta + \alpha \, \nabla_\theta \log \pi(a_t \mid s_t; \theta) \cdot A_t
  • 评论家像基于价值的方法那样,通过最小化 TD 误差来更新。演员则用策略梯度更新,由评论家的优势估计来降低方差。这就是两全其美。

双头架构:演员输出动作概率,评论家输出价值估计,优势信号引导演员更新

  • PPO(Proximal Policy Optimization,近端策略优化)是实践中用得最广的策略梯度算法。它针对一个关键问题:如果一次策略更新太大,性能可能灾难性地崩塌。

  • PPO 用一个裁剪的代理目标(clipped surrogate objective)。令 r_t(\theta) = \frac{\pi(a_t | s_t; \theta)}{\pi(a_t | s_t; \theta_{\text{old}})} 为新旧策略之间的概率比。损失是:

\mathcal{L}^{\text{CLIP}}(\theta) = \mathbb{E} \left[ \min\!\left( r_t(\theta) A_t, \; \text{clip}(r_t(\theta), 1-\epsilon, 1+\epsilon) A_t \right) \right]
  • 裁剪(通常 \epsilon = 0.2)阻止概率比偏离 1 太远,从而让更新保持小而稳。如果优势为正(动作好),比例被限制在 1 + \epsilon;如果为负(动作差),比例被限制在 1 - \epsilon。这比早期的信赖域方法(TRPO)更简单也更稳定。

  • PPO 正是用来通过 RLHF(Reinforcement Learning from Human Feedback,基于人类反馈的强化学习)训练 ChatGPT 式模型的方法。在 RLHF 中,先在人类偏好数据上(两个输出里人类更偏好哪个?)训练一个奖励模型,然后 PPO 优化语言模型的策略以最大化这个学到的奖励。

  • DPO(Direct Preference Optimization,直接偏好优化)通过彻底去掉奖励模型来简化 RLHF。它不再先训奖励模型再做 RL,而是推导出一个解析损失,直接从偏好数据优化策略:

\mathcal{L}_{\text{DPO}}(\theta) = -\mathbb{E} \left[ \log \sigma\!\left( \beta \log \frac{\pi_\theta(y_w \mid x)}{\pi_{\text{ref}}(y_w \mid x)} - \beta \log \frac{\pi_\theta(y_l \mid x)}{\pi_{\text{ref}}(y_l \mid x)} \right) \right]
  • 这里 y_w 是偏好(获胜)的回复,y_l 是不偏好(落败)的回复。DPO 提高偏好输出的相对概率,实现起来比基于 PPO 的 RLHF 简单得多。

  • RL 算法里有两个重要区分。同策略 vs 离策略(on-policy vs off-policy):同策略方法(SARSA、PPO)从当前策略生成的数据中学习;离策略方法(Q-learning、DQN)可以从任何策略生成的数据中学习。离策略方法样本效率更高(能复用旧数据),但可能不够稳定。

  • 基于模型 vs 无模型(model-based vs model-free):无模型方法(到此为止讨论的所有内容)直接从经验中学价值或策略。基于模型的方法先学一个环境模型(P(s' \mid s, a)R(s, a)),再用它做规划(在脑中想象未来轨迹,而不真去行动)。基于模型的方法样本效率更高,但增加了学习一个准确模型的复杂性。

  • 总结一下 RL 的版图:

方法 类型 核心思想 优势
价值迭代 DP,基于模型 贝尔曼最优性 精确解(小 MDP)
SARSA TD,同策略 同策略学 Q 保守、安全
Q-Learning TD,离策略 学 Q*,贪心目标 简单有效
DQN 深度,离策略 神经 Q + 回放 + 目标网络 可扩展到高维状态
REINFORCE 策略梯度 对数概率梯度 × 回报 简单的策略优化
Actor-Critic PG + 价值 演员 + 评论家降方差 实用且灵活
PPO PG,带裁剪 类信赖域的稳定性 业界标准
DPO 直接偏好 跳过奖励模型 更简单的 RLHF

编程练习(使用 CoLab 或 notebook)

  1. 为一个简单的网格世界实现价值迭代。计算最优价值函数并提取最优策略。把两者分别可视化为热力图和箭头图。
import jax.numpy as jnp import matplotlib.pyplot as plt # 4x4 网格世界:目标在 (3,3),每步奖励 -1,目标处为 0 grid_size = 4 gamma = 0.99 goal = (3, 3) # 动作:上、下、左、右 actions = [(-1, 0), (1, 0), (0, -1), (0, 1)] action_names = ['up', 'down', 'left', 'right'] action_arrows = ['\u2191', '\u2193', '\u2190', '\u2192'] def step(s, a): """确定性转移。""" ns = (max(0, min(grid_size-1, s[0]+a[0])), max(0, min(grid_size-1, s[1]+a[1]))) return ns # 价值迭代 V = jnp.zeros((grid_size, grid_size)) for iteration in range(100): V_new = jnp.array(V) for i in range(grid_size): for j in range(grid_size): if (i, j) == goal: continue values = [] for a in actions: ns = step((i, j), a) values.append(-1 + gamma * float(V[ns[0], ns[1]])) V_new = V_new.at[i, j].set(max(values)) if jnp.max(jnp.abs(V_new - V)) < 1e-6: print(f"Converged in {iteration+1} iterations") break V = V_new # 提取策略 policy = [['' for _ in range(grid_size)] for _ in range(grid_size)] for i in range(grid_size): for j in range(grid_size): if (i, j) == goal: policy[i][j] = 'G' continue best_a = max(range(4), key=lambda a: -1 + gamma * float(V[step((i,j), actions[a])[0], step((i,j), actions[a])[1]])) policy[i][j] = action_arrows[best_a] fig, axes = plt.subplots(1, 2, figsize=(10, 4)) im = axes[0].imshow(V, cmap='YlOrRd_r') axes[0].set_title("Optimal Value Function") for i in range(grid_size): for j in range(grid_size): axes[0].text(j, i, f"{V[i,j]:.1f}", ha='center', va='center', fontsize=10) plt.colorbar(im, ax=axes[0]) axes[1].imshow(jnp.ones((grid_size, grid_size)), cmap='Greys', vmin=0, vmax=2) axes[1].set_title("Optimal Policy") for i in range(grid_size): for j in range(grid_size): axes[1].text(j, i, policy[i][j], ha='center', va='center', fontsize=18) plt.tight_layout(); plt.show()
  1. 在一个简单的网格世界上实现表格 Q-learning。训练智能体,画出学习曲线,并展示学到的 Q 值。
import jax import jax.numpy as jnp import matplotlib.pyplot as plt grid_size = 5 goal = (4, 4) actions = [(-1,0), (1,0), (0,-1), (0,1)] # Q 表 Q = {} for i in range(grid_size): for j in range(grid_size): Q[(i,j)] = [0.0] * 4 alpha = 0.1 gamma = 0.95 epsilon = 1.0 epsilon_decay = 0.995 min_epsilon = 0.01 def step(s, a_idx): a = actions[a_idx] ns = (max(0, min(grid_size-1, s[0]+a[0])), max(0, min(grid_size-1, s[1]+a[1]))) r = 0.0 if ns == goal else -1.0 done = ns == goal return ns, r, done key = jax.random.PRNGKey(42) rewards_per_episode = [] for ep in range(500): s = (0, 0) total_reward = 0 for _ in range(100): key, subkey = jax.random.split(key) if float(jax.random.uniform(subkey)) < epsilon: key, subkey = jax.random.split(key) a = int(jax.random.randint(subkey, (), 0, 4)) else: a = max(range(4), key=lambda i: Q[s][i]) ns, r, done = step(s, a) total_reward += r # Q-learning 更新 Q[s][a] += alpha * (r + gamma * max(Q[ns]) - Q[s][a]) s = ns if done: break rewards_per_episode.append(total_reward) epsilon = max(min_epsilon, epsilon * epsilon_decay) plt.figure(figsize=(8, 4)) # 平滑曲线 window = 20 smoothed = [sum(rewards_per_episode[max(0,i-window):i+1])/min(i+1, window) for i in range(len(rewards_per_episode))] plt.plot(smoothed, color='#3498db', linewidth=1.5) plt.xlabel("Episode"); plt.ylabel("Total Reward (smoothed)") plt.title("Q-Learning on Gridworld") plt.grid(alpha=0.3); plt.show() # 展示学到的策略 arrow = ['\u2191', '\u2193', '\u2190', '\u2192'] print("Learned policy:") for i in range(grid_size): row = "" for j in range(grid_size): if (i,j) == goal: row += " G " else: row += f" {arrow[max(range(4), key=lambda a: Q[(i,j)][a])]} " print(row)
  1. 在多臂老虎机问题上实现 REINFORCE。展示策略在训练过程中如何逐渐偏向最佳摇臂。
import jax import jax.numpy as jnp import matplotlib.pyplot as plt # 5 臂老虎机,期望奖励各不相同 true_rewards = jnp.array([0.2, 0.5, 0.8, 0.3, 0.1]) n_arms = len(true_rewards) # 策略:logits 上的 softmax logits = jnp.zeros(n_arms) lr = 0.1 key = jax.random.PRNGKey(42) policy_history = [] reward_history = [] for step in range(2000): probs = jax.nn.softmax(logits) policy_history.append(probs) # 采样动作 key, subkey = jax.random.split(key) action = jax.random.choice(subkey, n_arms, p=probs) # 获得奖励(伯努利) key, subkey = jax.random.split(key) reward = float(jax.random.uniform(subkey) < true_rewards[action]) reward_history.append(reward) # REINFORCE 更新 # grad log pi(a) = e_a - probs(对于 softmax 参数化) grad_log_pi = -probs.at[action].add(1.0) # one-hot(a) - probs logits = logits + lr * reward * grad_log_pi policy_history = jnp.stack(policy_history) fig, axes = plt.subplots(1, 2, figsize=(12, 4)) colors = ['#3498db', '#e74c3c', '#27ae60', '#9b59b6', '#f39c12'] for i in range(n_arms): axes[0].plot(policy_history[:, i], color=colors[i], label=f'Arm {i} (true={true_rewards[i]:.1f})', linewidth=1.5) axes[0].set_xlabel("Step"); axes[0].set_ylabel("P(arm)") axes[0].set_title("Policy Evolution (REINFORCE)") axes[0].legend(fontsize=8); axes[0].grid(alpha=0.3) # 平滑后的奖励 window = 50 smoothed = [sum(reward_history[max(0,i-window):i+1])/min(i+1,window) for i in range(len(reward_history))] axes[1].plot(smoothed, color='#27ae60', linewidth=1.5) axes[1].axhline(y=0.8, color='#e74c3c', linestyle='--', alpha=0.5, label='Best arm') axes[1].set_xlabel("Step"); axes[1].set_ylabel("Avg Reward") axes[1].set_title("Reward Over Time"); axes[1].legend() axes[1].grid(alpha=0.3) plt.tight_layout(); plt.show()

作者与出处
原作者: HenryNdubuaku
来源:HenryNdubuaku
许可证:Apache-2.0
整理: 灏天文库整理
由灏天文库结构化整理,提供目录导航、全文检索与在线阅读,便于系统化学习
发布者: 作者: HenryNdubuaku 转发
评论区 (0)
U