构建和试验一个简单基于模型的智能体,展示了环境模型学习及其规划应用的实际应用。该智能体实现了Dyna-Q算法的一个基本版本,该算法巧妙地结合了直接强化学习(从真实经验中学习)与模型学习和规划(从模拟经验中学习)。这项实践练习旨在巩固你对所学模型如何通过允许智能体使用模拟转移进行额外更新来加速学习的理解。我们将使用一个简单的环境,以使重点完全放在基于模型的工作原理上。环境:一个简单网格为了在不增加不必要复杂性的情况下说明核心思想,我们将使用一个小型、确定性网格。想象一个1x5的网格(5个状态,编号0到4)。智能体从状态0开始,并想达到状态4的目标。状态: $S = {0, 1, 2, 3, 4}$动作: $A = {\text{left}, \text{right}}$转移: 从状态 $s$ 向‘左’移动将智能体带到 $\max(0, s-1)$。向‘右’移动将智能体带到 $\min(4, s+1)$。环境是确定性的。奖励: 到达状态4会得到+1的奖励。所有其他转移的奖励为0。回合终止: 当智能体到达状态4时,一个回合结束。我们的智能体需要学习最优策略(总是向右移动)来快速达到目标。Dyna-Q智能体组成部分我们的Dyna-Q智能体将由三个主要部分组成:直接强化学习: 一个标准的Q学习组件,它根据与环境的实际交互更新动作值。更新规则是: $$ Q(s, a) \leftarrow Q(s, a) + \alpha [r + \gamma \max_{a'} Q(s', a') - Q(s, a)] $$ 其中 $(s, a, r, s')$ 是在真实环境中经历的一次转移。模型学习: 一个学习转移函数 $T(s, a) \rightarrow s'$ 和奖励函数 $R(s, a) \rightarrow r$ 的组件。由于我们的网格是确定性的,我们可以简单地将模型存储为字典,将观察到的 $(s, a)$ 对映射到它们的结果 $(r, s')$。model_T[s][a] = s'model_R[s][a] = r 我们只更新智能体实际尝试过的状态-动作对的模型。规划: 一个使用学习到的模型生成模拟经验并执行额外Q学习更新的组件。规划过程通常包括:随机选择一个之前访问过的状态 $s_{sim}$。随机选择一个之前在状态 $s_{sim}$ 中采取过的动作 $a_{sim}$。查询模型以获得模拟的下一个状态 $s'{sim} = \text{model_T}[s{sim}][a_{sim}]$ 和奖励 $r_{sim} = \text{model_R}[s_{sim}][a_{sim}]$。使用这个模拟转移 $(s_{sim}, a_{sim}, r_{sim}, s'{sim})$ 执行Q学习更新: $$ Q(s{sim}, a_{sim}) \leftarrow Q(s_{sim}, a_{sim}) + \alpha [r_{sim} + \gamma \max_{a'} Q(s'{sim}, a') - Q(s{sim}, a_{sim})] $$ 这个规划步骤在每次真实环境步骤后重复固定的次数 ($n$)。实现概述让我们用伪代码概述核心逻辑。# 任意初始化 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实现片段这里是一些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学习显著加快。规划步骤使得智能体能够通过模型,更有效地在状态空间中传播从单个真实步骤中学到的价值信息。我们可以通过绘制两种算法每个回合所用步数来可视化这种差异。{"data":[{"y":[150, 120, 95, 70, 50, 40, 30, 25, 20, 15, 10, 8, 6, 5, 4, 4, 4, 4, 4, 4],"type":"scatter","mode":"lines","name":"Q学习 (n=0)","line":{"color":"#4263eb"}},{"y":[80, 45, 20, 10, 6, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4],"type":"scatter","mode":"lines","name":"Dyna-Q (n=10)","line":{"color":"#f76707"}}],"layout":{"title":{"text":"学习速度比较"},"xaxis":{"title":{"text":"回合数"}},"yaxis":{"title":{"text":"每回合步数"}}}}此图表显示了在简单网格任务上,标准Q学习和Dyna-Q的示例学习曲线。由于规划更新,Dyna-Q更快地收敛到最优策略(4步)。讨论与后续这个简单的例子说明了Dyna-Q的核心原理:使用学习到的模型通过模拟经验来补充真实经验,从而加速学习。局限性与注意事项:模型准确性: 在这个确定性环境中,模型在一次观察后就完美了。在随机环境中,学习精确的模型更难,可能需要对奖励进行平均或学习转移概率。模型误差可能对规划产生负面影响。表格表示: 我们使用表格来表示Q值和模型。对于更大的状态空间,Q函数和模型都需要函数近似(如神经网络)。计算成本: 规划增加了每个真实步骤的计算成本。规划工作量 ($n$) 与学习速度之间的权衡是一个重要考虑因素。探索: 虽然规划有助于传播已知信息,但在真实环境中进行探索对于发现新状态和动作以及改进模型仍然非常重要。这项实践练习为理解更复杂的基于模型的算法提供初步认识。诸如使用学习模型进行轨迹采样、集成蒙特卡洛树搜索(MCTS)等搜索算法以及处理模型不确定性等技术,都是建立在这些基本想法之上的扩展。尝试不同的 n_planning_steps 值或尝试一个稍微复杂(可能是随机的)的环境,可以提供更多关于基于模型智能体行为的启发。