04 - 探索与利用¶
学习时间: 2-3小时 重要性: ⭐⭐⭐⭐ 强化学习的核心权衡问题 前置知识: SARSA、Q-Learning
🎯 学习目标¶
完成本章后,你将能够: - 理解探索-利用权衡的本质 - 掌握ε-贪婪、UCB等探索策略 - 实现乐观初始化和梯度Bandit - 分析不同探索策略的优劣 - 在实际问题中选择合适的探索策略
1. 探索-利用权衡¶
1.1 问题定义¶
核心问题:
智能体应该利用已知的最优动作,还是探索可能更好的未知动作?
类比: - 去餐厅:去最喜欢的餐厅(利用)vs 尝试新餐厅(探索) - 广告推荐:展示点击率最高的广告(利用)vs 测试新广告(探索)
1.2 形式化定义¶
遗憾(Regret):
其中: - \(\mu^*\):最优动作的期望奖励 - \(\mu_{a_t}\):第t步选择动作的期望奖励 - \(T\):总步数
目标:最小化累积遗憾
2. ε-贪婪策略¶
2.1 算法¶
思想:以ε概率随机探索,以1-ε概率选择当前最优动作。
def epsilon_greedy(q_values, epsilon):
"""
ε-贪婪策略
参数:
q_values: 各动作的Q值估计
epsilon: 探索概率
返回:
选择的动作
"""
if np.random.random() < epsilon:
# 探索:随机选择
return np.random.randint(len(q_values))
else:
# 利用:选择Q值最大的动作
return np.argmax(q_values)
2.2 ε的衰减¶
固定ε的问题: - 太小:探索不足,可能陷入局部最优 - 太大:过度探索,收敛慢
解决方案:随时间衰减
# 线性衰减
epsilon = max(0.01, 0.1 - episode * 0.0001)
# 指数衰减
epsilon = 0.1 * (0.995 ** episode)
# 反比衰减
epsilon = 1 / (episode + 1)
3. 乐观初始化¶
3.1 思想¶
核心:将所有动作的初始Q值设为很高,鼓励早期探索。
原理: - 高初始值 → 实际奖励 < 估计 → TD误差为负 → Q值下降 - 被选择过的动作Q值下降,未被选择的相对更高 - 自然鼓励探索未被选择的动作
3.2 代码实现¶
def q_learning_optimistic(env, num_episodes=1000, alpha=0.1,
gamma=0.9, epsilon=0.1, init_value=10.0):
"""
带乐观初始化的Q-Learning
参数:
init_value: 乐观初始值(通常设为可能的最大回报)
"""
n_actions = env.get_action_space()
# 乐观初始化
Q = defaultdict(lambda: np.ones(n_actions) * init_value) # defaultdict访问不存在的键时返回默认值
for episode in range(num_episodes):
state, _ = env.reset()
done = False
while not done:
# 可以使用更小的epsilon,因为乐观初始化已经鼓励探索
if np.random.random() < epsilon:
action = np.random.randint(n_actions)
else:
action = np.argmax(Q[state])
next_state, reward, terminated, truncated, _ = env.step(action)
done = terminated or truncated
# Q-Learning更新
if not done:
td_target = reward + gamma * np.max(Q[next_state])
else:
td_target = reward
Q[state][action] += alpha * (td_target - Q[state][action])
state = next_state
return Q
4. 上置信界(UCB)¶
4.1 思想¶
核心:不仅考虑Q值,还考虑不确定性。
其中: - \(Q_t(a)\):动作a的Q值估计 - \(N_t(a)\):动作a被选择的次数 - \(c\):探索参数 - \(t\):总时间步
直观: - 选择次数少 → 第二项大 → 鼓励探索 - 选择次数多 → 第二项小 → 主要利用
4.2 代码实现¶
def ucb_action(q_values, action_counts, total_count, c=2.0):
"""
UCB动作选择
参数:
q_values: 各动作的Q值
action_counts: 各动作被选择的次数
total_count: 总选择次数
c: 探索参数
返回:
选择的动作
"""
n_actions = len(q_values)
ucb_values = np.zeros(n_actions)
for a in range(n_actions):
if action_counts[a] == 0:
# 从未选择的动作,赋予无穷大UCB值
ucb_values[a] = float('inf')
else:
exploitation = q_values[a]
exploration = c * np.sqrt(np.log(total_count) / action_counts[a])
ucb_values[a] = exploitation + exploration
return np.argmax(ucb_values)
def ucb_q_learning(env, num_episodes=1000, alpha=0.1, gamma=0.9, c=2.0):
"""使用UCB探索的Q-Learning"""
n_actions = env.get_action_space()
Q = defaultdict(lambda: np.zeros(n_actions))
action_counts = defaultdict(lambda: np.zeros(n_actions))
total_steps = 0
for episode in range(num_episodes):
state, _ = env.reset()
done = False
while not done:
# UCB动作选择
action = ucb_action(Q[state], action_counts[state], total_steps, c)
action_counts[state][action] += 1
total_steps += 1
next_state, reward, terminated, truncated, _ = env.step(action)
done = terminated or truncated
# Q-Learning更新
if not done:
td_target = reward + gamma * np.max(Q[next_state])
else:
td_target = reward
Q[state][action] += alpha * (td_target - Q[state][action])
state = next_state
return Q
5. 梯度Bandit¶
5.1 思想¶
核心:使用softmax分布选择动作,基于偏好(preference)而非Q值。
偏好更新:
其中: - \(H_t(a)\):动作a的偏好 - \(\bar{R}_t\):平均奖励(基线) - \(\pi_t(a)\):选择动作a的概率
5.2 Softmax策略¶
5.3 代码实现¶
class GradientBandit:
"""梯度Bandit算法"""
def __init__(self, n_actions, alpha=0.1):
self.n_actions = n_actions
self.alpha = alpha
self.preferences = np.zeros(n_actions)
self.avg_reward = 0
self.count = 0
def softmax(self):
"""计算softmax概率"""
exp_prefs = np.exp(self.preferences - np.max(self.preferences))
return exp_prefs / np.sum(exp_prefs)
def select_action(self):
"""根据softmax选择动作"""
probs = self.softmax()
return np.random.choice(self.n_actions, p=probs)
def update(self, action, reward):
"""更新偏好"""
self.count += 1
self.avg_reward += (reward - self.avg_reward) / self.count
probs = self.softmax()
# 更新所有动作的偏好
for a in range(self.n_actions):
if a == action:
self.preferences[a] += self.alpha * (reward - self.avg_reward) * (1 - probs[a])
else:
self.preferences[a] -= self.alpha * (reward - self.avg_reward) * probs[a]
6. 探索策略对比¶
| 策略 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| ε-贪婪 | 简单直观 | 随机探索可能低效 | 通用场景 |
| 乐观初始化 | 无需调参 | 需要知道最大回报 | 早期探索 |
| UCB | 有理论保证 | 需要维护计数 | 有限动作空间 |
| 梯度Bandit | 平滑探索 | 需要调学习率 | 连续动作偏好 |
7. 本章总结¶
核心概念¶
探索-利用权衡:
├── ε-贪婪: 以ε概率随机探索
├── 乐观初始化: 高初始值鼓励探索
├── UCB: 基于不确定性探索
└── 梯度Bandit: 基于偏好softmax探索
选择建议:
├── 通用场景: ε-贪婪(衰减)
├── 需要理论保证: UCB
└── 大规模动作空间: 梯度方法
✅ 自测问题¶
-
为什么需要探索-利用权衡?纯利用或纯探索有什么问题?
-
UCB中的c参数有什么作用?如何选择合适的c值?
-
乐观初始化和ε-贪婪相比有什么优势?
-
设计一个实验比较不同探索策略的性能。
→ 下一步:05-多步方法.md