01 - 时序差分学习基础¶
学习时间: 3-4小时 重要性: ⭐⭐⭐⭐⭐ 强化学习最核心的算法框架 前置知识: 蒙特卡洛方法、贝尔曼方程
🎯 学习目标¶
完成本章后,你将能够: - 理解TD学习的基本思想和TD误差 - 掌握TD(0)算法的完整推导和实现 - 理解TD与MC、DP的关系和区别 - 掌握TD学习的收敛性分析 - 能够实现并调试TD预测算法
1. 时序差分学习简介¶
1.1 从MC到TD:关键洞察¶
蒙特卡洛方法的局限: - 必须等待episode结束才能更新 - 高方差(使用实际回报) - 不适用于连续任务(无终止状态)
动态规划的局限: - 需要完整的模型(转移概率P和奖励R) - 计算量大
时序差分学习的突破:
结合MC的采样和DP的自举,实现在线学习
MC: G_t = R_{t+1} + γR_{t+2} + γ²R_{t+3} + ... (等待完整episode)
DP: V(s) = Σ π(a|s) Σ p(s',r|s,a)[r + γV(s')] (需要模型)
TD: V(s) ← V(s) + α[R_{t+1} + γV(s') - V(s)] (自举+采样)
1.2 TD的核心思想¶
自举(Bootstrapping): - 使用当前的估计值来更新估计值 - 不需要等待最终回报 - 可以在线学习(每步都更新)
关键公式:
直观理解:
1.3 TD误差¶
定义TD误差:
TD误差的意义: - 衡量当前估计与"更好的估计"之间的差距 - 如果δ > 0:当前状态比预期好,应该增加V(s) - 如果δ < 0:当前状态比预期差,应该减少V(s)
与MC误差的对比:
2. TD(0)算法¶
2.1 算法流程¶
TD(0)用于策略评估:
初始化: V(s) = 0, ∀s
对每个episode:
初始化状态s
当s不是终止状态时:
执行动作a ~ π(·|s), 观察r, s'
TD误差: δ = r + γV(s') - V(s)
更新: V(s) ← V(s) + α × δ
s ← s'
2.2 代码实现¶
import numpy as np
from collections import defaultdict
def td_zero_prediction(env, policy, alpha=0.1, gamma=0.9, num_episodes=1000):
"""
TD(0)策略评估
参数:
env: 环境
policy: 策略,state -> action probabilities
alpha: 学习率
gamma: 折扣因子
num_episodes: episode数量
返回:
V: 状态值函数估计
"""
V = defaultdict(float) # defaultdict访问不存在的键时返回默认值
for episode in range(num_episodes):
state, _ = env.reset()
done = False
while not done:
# 根据策略选择动作
action = np.random.choice(len(policy[state]), p=policy[state])
# 执行动作
next_state, reward, terminated, truncated, _ = env.step(action)
done = terminated or truncated
# TD更新
td_target = reward + gamma * V[next_state] * (not done)
td_error = td_target - V[state]
V[state] += alpha * td_error
state = next_state
if (episode + 1) % 100 == 0:
# 计算当前策略的平均回报
avg_reward = evaluate_policy(env, policy, V)
print(f"Episode {episode + 1}/{num_episodes}, Avg Reward: {avg_reward:.2f}")
return V
def evaluate_policy(env, policy, V, num_episodes=10):
"""评估策略的平均回报"""
total_rewards = []
for _ in range(num_episodes):
state, _ = env.reset()
done = False
episode_reward = 0
while not done:
action = np.argmax(policy[state])
next_state, reward, terminated, truncated, _ = env.step(action)
done = terminated or truncated
episode_reward += reward
state = next_state
total_rewards.append(episode_reward)
return np.mean(total_rewards)
2.3 学习率的选择¶
学习率α的影响:
| α值 | 效果 |
|---|---|
| 太大(如0.5) | 震荡,可能不收敛 |
| 适中(如0.1) | 稳定收敛 |
| 太小(如0.01) | 收敛慢 |
| 衰减(如1/t) | 理论上保证收敛 |
常用学习率方案:
# 1. 常数学习率
alpha = 0.1
# 2. 线性衰减
alpha = max(0.01, 0.1 - episode * 0.0001)
# 3. 指数衰减
alpha = 0.1 * (0.995 ** episode)
# 4. 理论上保证收敛
alpha = 1 / (episode + 1)
3. TD vs MC vs DP¶
3.1 三者的关系¶
3.2 详细对比¶
| 特性 | DP | MC | TD(0) |
|---|---|---|---|
| 需要模型 | 是 | 否 | 否 |
| 自举 | 是 | 否 | 是 |
| 采样 | 否 | 是 | 是 |
| 在线更新 | 否 | 否 | 是 |
| 每步计算 | O(S²A) | O(1) | O(1) |
| 偏差 | 0 | 0 | >0 |
| 方差 | 低 | 高 | 低 |
| 连续任务 | 是 | 否 | 是 |
3.3 偏差-方差权衡¶
MC目标:\(G_t\)(实际回报) - 无偏:\(\mathbb{E}[G_t] = V^\pi(S_t)\) - 高方差:包含很多随机步骤的累积
TD目标:\(R_{t+1} + \gamma V(S_{t+1})\)(自举目标) - 有偏:\(\mathbb{E}[R_{t+1} + \gamma V(S_{t+1})] \neq V^\pi(S_t)\)(除非V已经收敛) - 低方差:只包含一步随机性
直观理解: - MC像"事后总结"(准确但慢) - TD像"边做边学"(快但可能有偏差)
4. TD学习的收敛性¶
4.1 收敛定理¶
定理(TD(0)的收敛性):
在满足以下条件时,TD(0)以概率1收敛到\(V^\pi\): 1. 策略\(\pi\)是固定的 2. 所有状态被无限次访问 3. 步长满足Robbins-Monro条件: $\(\sum_t \alpha_t = \infty, \quad \sum_t \alpha_t^2 < \infty\)$
4.2 收敛速率¶
定理(TD(0)的收敛速率):
使用步长\(\alpha_t = 1/t\):
与MC的比较: - MC:\(O(1/\sqrt{n})\) 收敛速率 - TD:\(O(1/t)\) 收敛速率
TD收敛更快,但有偏差。
4.3 随机逼近视角¶
TD作为随机逼近:
TD更新可以写成:
其中: - \(h(V) = T^\pi V - V\)(贝尔曼残差) - \(\xi_t\) 是噪声
这符合Robbins-Monro随机逼近框架。
5. 批量TD学习¶
5.1 批量更新¶
思想:收集一批数据后,进行多次更新。
def batch_td_learning(env, policy, alpha=0.1, gamma=0.9,
batch_size=100, num_batches=100):
"""
批量TD学习
收集batch_size个transition,然后进行多次更新
"""
V = defaultdict(float)
for batch_idx in range(num_batches):
# 收集数据
transitions = []
for _ in range(batch_size):
state, _ = env.reset()
done = False
while not done and len(transitions) < batch_size:
action = np.random.choice(len(policy[state]), p=policy[state])
next_state, reward, terminated, truncated, _ = env.step(action)
done = terminated or truncated
transitions.append((state, action, reward, next_state, done))
state = next_state
# 批量更新(多次遍历数据)
for _ in range(10): # 每个batch更新10次
for state, action, reward, next_state, done in transitions:
td_target = reward + gamma * V[next_state] * (not done)
td_error = td_target - V[state]
V[state] += alpha * td_error
if (batch_idx + 1) % 10 == 0:
print(f"Batch {batch_idx + 1}/{num_batches}")
return V
5.2 批量vs在线¶
| 特性 | 在线TD | 批量TD |
|---|---|---|
| 内存需求 | 低 | 高 |
| 计算效率 | 高 | 中 |
| 收敛稳定性 | 中 | 高 |
| 样本效率 | 中 | 高 |
6. 实践练习¶
练习1:实现TD(0)并观察收敛¶
# 使用附录中的GridWorld环境
from gridworld import GridWorld
env = GridWorld(size=5)
# 定义随机策略
n_states = env.get_state_space()
n_actions = env.get_action_space()
random_policy = np.ones((n_states, n_actions)) / n_actions
# 运行TD(0)
V = td_zero_prediction(env, random_policy, alpha=0.1, num_episodes=1000)
# 可视化值函数
import matplotlib.pyplot as plt
V_matrix = np.array([V[s] for s in range(n_states)]).reshape(5, 5) # 重塑张量形状 # np.array创建NumPy数组
plt.imshow(V_matrix, cmap='viridis')
plt.colorbar()
plt.title('Value Function from TD(0)')
plt.show()
练习2:比较不同学习率¶
alphas = [0.01, 0.05, 0.1, 0.3, 0.5]
results = {}
for alpha in alphas:
V = td_zero_prediction(env, random_policy, alpha=alpha, num_episodes=500)
results[alpha] = V
# 比较最终值函数的差异
for alpha, V in results.items():
print(f"α={alpha}: Mean V = {np.mean(list(V.values())):.3f}")
练习3:TD vs MC对比¶
# 实现MC预测(首次访问)
def mc_prediction(env, policy, gamma=0.9, num_episodes=1000):
V = defaultdict(float)
returns = defaultdict(list)
for episode in range(num_episodes):
episode_data = []
state, _ = env.reset()
done = False
while not done:
action = np.random.choice(len(policy[state]), p=policy[state])
next_state, reward, terminated, truncated, _ = env.step(action)
done = terminated or truncated
episode_data.append((state, reward))
state = next_state
# 计算回报
G = 0
visited = set()
for t in range(len(episode_data) - 1, -1, -1):
state, reward = episode_data[t]
G = gamma * G + reward
if state not in visited: # 首次访问
visited.add(state)
returns[state].append(G)
V[state] = np.mean(returns[state])
return V
# 对比TD和MC
td_V = td_zero_prediction(env, random_policy, alpha=0.1, num_episodes=1000)
mc_V = mc_prediction(env, random_policy, gamma=0.9, num_episodes=1000)
# 计算差异
diffs = [abs(td_V[s] - mc_V[s]) for s in range(n_states)]
print(f"Mean absolute difference: {np.mean(diffs):.4f}")
7. 本章总结¶
核心概念回顾¶
时序差分学习 = 采样 + 自举
关键公式:
├── TD更新: V(s) ← V(s) + α[R + γV(s') - V(s)]
├── TD误差: δ = R + γV(s') - V(s)
└── 目标: R + γV(s') (自举目标)
与MC和DP的关系:
├── MC: 使用实际回报 G_t (无自举)
├── TD: 使用自举目标 (自举+采样)
└── DP: 使用期望 (无采样)
收敛性:
├── 步长条件: Σα=∞, Σα²<∞
├── 收敛速率: O(1/t)
└── 有偏但低方差
学习路径¶
✅ 自测问题¶
-
TD误差和MC误差有什么区别?为什么TD误差可以每步计算?
-
TD学习为什么有偏差?这种偏差会随着训练消失吗?
-
比较TD(0)和MC的优缺点。在什么情况下你会选择TD而不是MC?
-
Robbins-Monro条件的两个要求分别有什么意义?
-
设计一个实验验证TD(0)的收敛性。你会如何设置实验?
📚 延伸阅读¶
- Sutton & Barto《Reinforcement Learning: An Introduction》
-
第6章:Temporal-Difference Learning
-
Sutton (1988)
- "Learning to predict by the methods of temporal differences"
-
TD学习的开创性论文
-
Dayan (1992)
- "The convergence of TD(λ) for general λ"
- TD(λ)收敛性的经典证明
准备好学习具体的TD控制算法了吗? 下一章我们将学习SARSA算法。
→ 下一步:02-SARSA算法.md