趋近智
构建和试验一个简单基于模型的智能体,展示了环境模型学习及其规划应用的实际应用。该智能体实现了Dyna-Q算法的一个基本版本,该算法巧妙地结合了直接强化学习 (reinforcement learning)(从真实经验中学习)与模型学习和规划(从模拟经验中学习)。
这项实践练习旨在巩固你对所学模型如何通过允许智能体使用模拟转移进行额外更新来加速学习的理解。我们将使用一个简单的环境,以使重点完全放在基于模型的工作原理上。
为了在不增加不必要复杂性的情况下说明核心思想,我们将使用一个小型、确定性网格。想象一个1x5的网格(5个状态,编号0到4)。智能体从状态0开始,并想达到状态4的目标。
我们的智能体需要学习最优策略(总是向右移动)来快速达到目标。
我们的Dyna-Q智能体将由三个主要部分组成:
直接强化学习 (reinforcement learning): 一个标准的Q学习组件,它根据与环境的实际交互更新动作值。更新规则是:
其中 是在真实环境中经历的一次转移。
模型学习: 一个学习转移函数 和奖励函数 的组件。由于我们的网格是确定性的,我们可以简单地将模型存储为字典,将观察到的 对映射到它们的结果 。
model_T[s][a] = s'model_R[s][a] = r
我们只更新智能体实际尝试过的状态-动作对的模型。规划: 一个使用学习到的模型生成模拟经验并执行额外Q学习更新的组件。规划过程通常包括:
这个规划步骤在每次真实环境步骤后重复固定的次数 ()。
让我们用伪代码概述核心逻辑。
# 任意初始化 Q(s, a)(例如,设为0)
# 将 Model_T 和 Model_R 初始化为空字典/结构
# 跟踪观察到的 (状态, 动作) 对: observed_sa = set()
# 对每个回合进行循环:
# s = 初始状态
# 对回合的每一步进行循环:
# 从 s 中使用基于 Q 的策略选择动作 a(例如,epsilon-贪婪)
# 执行动作 a,观察真实奖励 r 和下一个状态 s'
# # 1. 直接强化学习更新
# Q(s, a) <- Q(s, a) + alpha * [r + gamma * max_a'(Q(s', a')) - Q(s, a)]
# # 2. 模型学习
# 更新模型:存储 (s, a) -> (r, s')
# model_T[s][a] = s'
# model_R[s][a] = r
# 将 (s, a) 添加到 observed_sa
# # 3. 规划(重复 n 次)
# 循环 n 次:
# 采样一个之前观察到的状态-动作对
# 获取 r_sim = model_R[s_sim][a_sim]
# 获取 s'_sim = model_T[s_sim][a_sim]
# # 规划更新(基于模拟经验的Q学习)
# Q(s_sim, a_sim) <- Q(s_sim, a_sim) + alpha * [r_sim + gamma * max_a'(Q(s'_sim, a')) - Q(s_sim, a_sim)]
# 更新状态
# s = s'
# 如果 s 是终止状态,中断内循环(结束回合)
这里是一些Python代码片段,说明了重要部分,假设是基于NumPy的简单实现。
初始化:
import numpy as np
import random
from collections import defaultdict
num_states = 5
num_actions = 2 # 0: left, 1: right
alpha = 0.1 # Learning rate
gamma = 0.95 # Discount factor
epsilon = 0.1 # Exploration rate
n_planning_steps = 10 # Number of planning steps per real step
# 初始化Q表
q_table = np.zeros((num_states, num_actions))
# 模型(使用字典进行稀疏存储)
# defaultdict 允许轻松添加新状态/动作
model_T = defaultdict(lambda: defaultdict(int))
model_R = defaultdict(lambda: defaultdict(float))
observed_sa_pairs = set()
# 简单的确定性环境函数
def grid_step(state, action):
if action == 0: # left
next_state = max(0, state - 1)
else: # right
next_state = min(num_states - 1, state + 1)
reward = 1.0 if next_state == num_states - 1 else 0.0
terminal = (next_state == num_states - 1)
return reward, next_state, terminal
主循环片段(在回合循环内):
# 假设 'current_state' 保存当前状态
# 使用epsilon-贪婪选择动作
if random.random() < epsilon:
action = random.choice([0, 1]) # 探索
else:
action = np.argmax(q_table[current_state]) # 利用
# 在真实环境中执行动作
reward, next_state, terminal = grid_step(current_state, action)
# 1. 直接强化学习更新
td_target = reward + gamma * np.max(q_table[next_state]) * (not terminal)
td_error = td_target - q_table[current_state, action]
q_table[current_state, action] += alpha * td_error
# 2. 模型学习
# 如果状态更复杂,使用元组作为字典键
state_action_tuple = (current_state, action)
if current_state not in model_T or action not in model_T[current_state]:
model_T[current_state][action] = next_state
model_R[current_state][action] = reward
observed_sa_pairs.add(state_action_tuple)
# 3. 规划
if observed_sa_pairs: # 检查模型是否有数据
for _ in range(n_planning_steps):
# 采样一个之前观察到的状态-动作对
s_plan, a_plan = random.choice(list(observed_sa_pairs))
# 查询模型
r_plan = model_R[s_plan][a_plan]
s_prime_plan = model_T[s_plan][a_plan]
terminal_plan = (s_prime_plan == num_states - 1) # 从状态推断终止
# Q规划更新
td_target_plan = r_plan + gamma * np.max(q_table[s_prime_plan]) * (not terminal_plan)
td_error_plan = td_target_plan - q_table[s_plan, a_plan]
q_table[s_plan, a_plan] += alpha * td_error_plan
# 更新状态
current_state = next_state
如果你运行这个Dyna-Q智能体并将其学习速度(例如,每个回合达到目标的步数)与标准Q学习智能体(等同于 n_planning_steps = 0 的Dyna-Q)进行比较,你会观察到Dyna-Q学习显著加快。规划步骤使得智能体能够通过模型,更有效地在状态空间中传播从单个真实步骤中学到的价值信息。
我们可以通过绘制两种算法每个回合所用步数来可视化这种差异。
此图表显示了在简单网格任务上,标准Q学习和Dyna-Q的示例学习曲线。由于规划更新,Dyna-Q更快地收敛到最优策略(4步)。
这个简单的例子说明了Dyna-Q的核心原理:使用学习到的模型通过模拟经验来补充真实经验,从而加速学习。
局限性与注意事项:
这项实践练习为理解更复杂的基于模型的算法提供初步认识。诸如使用学习模型进行轨迹采样、集成蒙特卡洛树搜索(MCTS)等搜索算法以及处理模型不确定性等技术,都是建立在这些基本想法之上的扩展。尝试不同的 n_planning_steps 值或尝试一个稍微复杂(可能是随机的)的环境,可以提供更多关于基于模型智能体行为的启发。
这部分内容有帮助吗?
© 2026 ApX Machine Learning用心打造