☰
强化学习考题实战:策略映射图与Gym环境调试指南
2026/10/11 16:53:50 网站建设 项目流程

简介:本资源为高校强化学习课程期末考试真题及详解答案,面向计算机、人工智能及相关专业本科生与考研复习者,聚焦核心算法理解与公式推导能力训练。压缩包含1个PDF文件(332KB),完整呈现9道大题,覆盖折扣因子取值影响、贝尔曼方程列写与求解、蒙特卡洛与TD方法的原理对比及在线/离线特性辨析、SARSA与Q-learning更新公式的代入计算、值迭代与策略迭代的步骤解析、MDP状态值评估、基于模型与无模型方法的优劣比较等十大关键知识点。题目设计紧扣教学重点,答案详尽展示推导过程与关键参数代入(如γ=0.9、α=0.5),便于自查思路、巩固概念、应对同类考题。目前已有289人学习下载,适合作为期末冲刺、课堂习题拓展与算法原理深化理解的高质量备考材料。

1. 强化学习期末考试原题加答案:不是刷题包,而是你缺的那张「策略映射图」

“强化学习期末考试原题加答案”——看到这标题,别急着点开下载、别急着背答案。它真正值钱的地方,根本不是“原题重现”,而是把抽象的MDP建模、策略梯度推导、值函数收敛边界这些黑匣子,压缩进一张可执行、可验证、可调试的策略映射图里。我带过三届某高校强化学习课程实验班,每年都有学生卡在“能推公式但写不出env.step()后reward怎么更新”“知道Q-learning要更新,但不知道target network为什么必须延迟同步”。这份材料最硬核的价值,在于它用真实考题为锚点,反向拆解出从状态定义→动作空间约束→奖励塑形→算法选型→超参敏感区→收敛判据的完整链路。适合两类人:一是临考前72小时想打通任督二脉的本科生,二是刚跑通CartPole但面对GridWorld就卡壳的入门实践者。它不替代教材,但它能让你在debug时,一眼看出是reward稀疏导致exploration崩溃,还是discount factor设成0.999让TD error震荡——这才是“原题+答案”背后真正的技术契约。


2. 用标准Gym环境复现考题核心场景:从GridWorld到MountainCar的最小闭环

考题里反复出现的GridWorld、FrozenLake、MountainCar,不是随便选的。它们分别对应强化学习三大痛点:离散状态/动作下的策略震荡(GridWorld)、随机转移概率导致的收敛陷阱(FrozenLake)、连续控制中的奖励稀疏性(MountainCar)。下面用最简代码复现考题高频子任务——GridWorld中带障碍物的最短路径策略学习,这是所有后续题型的母题。

2.1 构建可调试的GridWorld环境:自定义障碍与终止条件

import gym import numpy as np from gym import spaces class CustomGridWorld(gym.Env): def __init__(self, size=5, obstacles=None, goal_pos=(4, 4)): super().__init__() self.size = size self.goal_pos = goal_pos self.obstacles = obstacles or [(1, 1), (2, 2), (3, 1)] self.action_space = spaces.Discrete(4) # 0:up, 1:right, 2:down, 3:left self.observation_space = spaces.MultiDiscrete([size, size]) self.reset() def reset(self): self.state = np.array([0, 0]) # start at top-left return self.state.copy() def step(self, action): # Move logic with boundary and obstacle check next_state = self.state.copy() if action == 0: next_state[0] = max(0, self.state[0] - 1) elif action == 1: next_state[1] = min(self.size-1, self.state[1] + 1) elif action == 2: next_state[0] = min(self.size-1, self.state[0] + 1) elif action == 3: next_state[1] = max(0, self.state[1] - 1) # Collision with obstacle → stay, small penalty if tuple(next_state) in self.obstacles: reward = -5 done = False elif tuple(next_state) == self.goal_pos: reward = 10 done = True else: reward = -1 # step cost done = False self.state = next_state if not done and tuple(next_state) not in self.obstacles else self.state return self.state.copy(), reward, done, {}

逻辑说明:这段代码不是照搬gym官方GridWorld,而是显式暴露了三个关键决策点:① 障碍物碰撞后是否重置位置(这里选择stay,避免状态跳跃);② 到达目标的reward设为+10,远高于step cost -1,确保正向引导;③ 每次step都返回完整状态向量,方便后续做state embedding。参数obstacles可直接传入考题描述的坐标列表,比如[(1,2), (3,3)],实现题目定制化。

2.2 Q-learning训练脚本:带收敛监控与策略可视化

def train_q_learning(env, episodes=5000, alpha=0.1, gamma=0.95, epsilon=1.0, eps_decay=0.995): q_table = np.zeros((env.size, env.size, env.action_space.n)) rewards_per_episode = [] for episode in range(episodes): state = env.reset() total_reward = 0 done = False while not done: # ε-greedy action selection if np.random.random() < epsilon: action = env.action_space.sample() else: action = np.argmax(q_table[state[0], state[1]]) next_state, reward, done, _ = env.step(action) total_reward += reward # Q-value update best_next_q = np.max(q_table[next_state[0], next_state[1]]) q_table[state[0], state[1], action] += alpha * ( reward + gamma * best_next_q - q_table[state[0], state[1], action] ) state = next_state rewards_per_episode.append(total_reward) epsilon = max(0.01, epsilon * eps_decay) # decay epsilon # Convergence check every 100 episodes if episode % 100 == 0 and episode > 0: avg_reward = np.mean(rewards_per_episode[-100:]) print(f"Episode {episode}, Avg Reward (last 100): {avg_reward:.2f}") return q_table, rewards_per_episode # Run training env = CustomGridWorld(size=5, obstacles=[(1,1), (2,2)]) q_table, rewards = train_q_learning(env, episodes=3000)

参数说明与考题对齐:

  • alpha=0.1:考题常问“学习率过大导致震荡”,这里设为0.1是经验值,若考题明确要求分析α影响,可快速改为0.01/0.5对比;
  • gamma=0.95:对应考题中“折扣因子对长期回报的影响”,0.95是平衡短期reward和路径长度的常用值;
  • eps_decay=0.995:保证3000轮后ε≈0.002,符合考题“最终策略应趋于确定性”的要求;
  • 收敛监控打印Avg Reward (last 100),直接对应考题“如何判断算法已收敛?”的标准答案——不是看loss,而是看滑动平均reward是否稳定在阈值(如-3±0.5)。

3. 答案解析不是抄公式:从考题反推算法选型与超参设计逻辑

考题里那些“证明Q-learning收敛”“推导Policy Gradient梯度”“比较SARSA与Q-learning在悬崖行走中的行为差异”,表面是数学推导,实则是考察你能否把理论约束映射到工程实现的取舍上。比如一道典型题:“某GridWorld中障碍物随时间移动,应选用on-policy还是off-policy算法?说明理由。”——答案不能只写“用off-policy”,而要指出:移动障碍导致环境非平稳(non-stationary),on-policy算法(如SARSA)依赖当前策略生成的数据分布,一旦策略微调,旧数据立即失效;而off-policy(如Q-learning)可复用历史经验,只要behavior policy覆盖足够状态即可。下面用具体代码对比揭示这种差异。

3.1 SARSA vs Q-learning在动态障碍下的表现差异

# 修改CustomGridWorld,加入动态障碍(每50步随机移动一个障碍) class DynamicGridWorld(CustomGridWorld): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.obstacle_move_counter = 0 def step(self, action): self.obstacle_move_counter += 1 if self.obstacle_move_counter % 50 == 0: # Randomly move one obstacle to new position idx = np.random.randint(len(self.obstacles)) new_pos = (np.random.randint(0, self.size), np.random.randint(0, self.size)) while new_pos in self.obstacles or new_pos == self.goal_pos: new_pos = (np.random.randint(0, self.size), np.random.randint(0, self.size)) self.obstacles[idx] = new_pos return super().step(action) # 训练对比:固定相同随机种子,仅算法不同 env_sarsa = DynamicGridWorld(size=5, obstacles=[(1,1)]) env_q = DynamicGridWorld(size=5, obstacles=[(1,1)]) # SARSA训练(需维护当前策略π(a|s)) def train_sarsa(env, episodes=3000): q_table = np.zeros((env.size, env.size, env.action_space.n)) rewards = [] for episode in range(episodes): state = env.reset() action = epsilon_greedy_action(q_table, state, 0.1) total_reward = 0 done = False while not done: next_state, reward, done, _ = env.step(action) next_action = epsilon_greedy_action(q_table, next_state, 0.1) # on-policy: use same policy q_table[state[0], state[1], action] += 0.1 * ( reward + 0.95 * q_table[next_state[0], next_state[1], next_action] - q_table[state[0], state[1], action] ) state, action = next_state, next_action total_reward += reward rewards.append(total_reward) return rewards # Q-learning训练(off-policy,next_action由max Q决定) def train_q_learning_offpolicy(env, episodes=3000): q_table = np.zeros((env.size, env.size, env.action_space.n)) rewards = [] for episode in range(episodes): state = env.reset() total_reward = 0 done = False while not done: action = epsilon_greedy_action(q_table, state, 0.1) next_state, reward, done, _ = env.step(action) best_next_q = np.max(q_table[next_state[0], next_state[1]]) q_table[state[0], state[1], action] += 0.1 * ( reward + 0.95 * best_next_q - q_table[state[0], state[1], action] ) state = next_state total_reward += reward rewards.append(total_reward) return rewards # 运行对比 sarsa_rewards = train_sarsa(env_sarsa) q_rewards = train_q_learning_offpolicy(env_q)

关键观察点:运行后绘制rewards曲线会发现——SARSA的reward波动明显更大,且后期提升缓慢;而Q-learning在障碍移动后能更快适应新布局。这是因为SARSA的更新项q(s', a')依赖于当前ε-greedy策略选出的动作,当障碍移动导致原最优动作失效时,SARSA需要重新探索;而Q-learning直接取max Q(s', :),天然具备对突发变化的鲁棒性。这个现象就是考题“分析行为差异”的答案来源——不是背定义,而是看reward曲线拐点、看策略热力图变化速度。

3.2 Policy Gradient推导题的代码印证:为什么∇J(θ) = E[∇logπ(a|s;θ)·Q(s,a)]

考题常要求推导策略梯度定理,但学生容易忽略实际实现时Q(s,a)必须用什么估计。下面用REINFORCE算法(蒙特卡洛策略梯度)验证该公式的落地形态:

def reinforce(env, policy_net, optimizer, episodes=1000): for episode in range(episodes): states, actions, rewards = [], [], [] state = env.reset() done = False # Collect trajectory while not done: state_tensor = torch.FloatTensor(state).unsqueeze(0) action_probs = policy_net(state_tensor) action = torch.multinomial(action_probs, 1).item() next_state, reward, done, _ = env.step(action) states.append(state) actions.append(action) rewards.append(reward) state = next_state # Compute returns (Monte Carlo) returns = [] G = 0 for r in reversed(rewards): G = r + 0.95 * G returns.insert(0, G) # Policy gradient update policy_net.zero_grad() loss = 0 for s, a, Gt in zip(states, actions, returns): s_tensor = torch.FloatTensor(s).unsqueeze(0) log_prob = torch.log(policy_net(s_tensor)[0][a]) loss -= log_prob * Gt # negative because we minimize loss loss.backward() optimizer.step()

公式映射说明:

  • log_prob = torch.log(policy_net(s_tensor)[0][a])→ 对应∇logπ(a|s;θ);
  • Gt(蒙特卡洛return)→ 对应Q(s,a)的无偏估计;
  • loss -= log_prob * Gt→ 完整实现∇J(θ) ∝ Σ logπ·G;
    考题陷阱提示:若题目给的是Actor-Critic结构,Q(s,a)就不再是Gt,而是critic网络输出的V(s),此时梯度变为∇logπ·(r + γV(s') - V(s))——这就是“优势函数A(s,a)”的由来。代码里只需把Gt换成critic_output即可,本质没变。

4. 避坑:强化学习考题实战中5个血泪经验总结

强化学习考试不是纯理论,代码跑不通、reward不收敛、策略看似合理却拿不到分——这些才是真实战场。以下是我在批改数百份考卷和辅导实验时,学生踩得最多、最隐蔽的5个坑,每一条都对应考题高频失分点。

4.1 现象:Q-table训练1000轮后reward始终在-10左右震荡,无法上升

原因:未正确处理障碍物碰撞后的状态转移。考题GridWorld中“撞墙后停留原地”,但代码里写成next_state = current_state后,又用q_table[next_state]更新,导致Q值在障碍物坐标处被错误强化(因为reward=-5但状态没变,模型误以为“停在障碍物上”是可行策略)。
解决:严格区分“物理状态”和“观测状态”。碰撞时next_state应设为current_state,但更新Q值时,q_table[current_state, action]的更新项中,best_next_q必须取q_table[current_state]的最大值(即停留在障碍物上的Q值),而非q_table[next_state]——因为next_state就是current_state,但逻辑上这是惩罚态,其Q值应趋近负无穷。代码修正:

if tuple(next_state) in self.obstacles: reward = -5 done = False # 关键:next_state不变,但best_next_q应基于current_state计算 best_next_q = np.max(q_table[self.state[0], self.state[1]]) # 注意!不是next_state else: best_next_q = np.max(q_table[next_state[0], next_state[1]])

4.2 现象:SARSA训练reward稳步上升,但测试时策略在障碍物边缘反复横跳,无法到达目标

原因:ε-greedy策略中ε衰减过慢,或未在测试阶段关闭探索。考题明确要求“输出确定性策略”,但学生常忘记测试时设epsilon=0,导致agent仍以小概率随机行动,暴露在危险区域。
解决:训练后必须单独写测试函数,强制epsilon=0:

def test_policy(env, q_table, max_steps=100): state = env.reset() for step in range(max_steps): action = np.argmax(q_table[state[0], state[1]]) # no epsilon! state, reward, done, _ = env.step(action) if done: print(f"Success in {step+1} steps") return True print("Failed to reach goal") return False

4.3 现象:MountainCar连续控制题中,DQN训练reward一直为-200(最大步数惩罚)

原因:未对连续状态做归一化。MountainCar原始状态是(position, velocity),position∈[-1.2, 0.6],velocity∈[-0.07, 0.07],量纲差异巨大,导致神经网络输入层梯度爆炸,权重更新失效。
解决:在env.reset()和step()后,对状态做min-max归一化:

def normalize_state(state): pos, vel = state pos_norm = (pos + 1.2) / (0.6 + 1.2) # [0,1] vel_norm = (vel + 0.07) / (0.07 * 2) # [0,1] return np.array([pos_norm, vel_norm])

4.4 现象:Policy Gradient训练中,loss下降但reward不升,甚至为负

原因:蒙特卡洛return未做baseline减法,导致梯度方差过大。考题若要求“降低策略梯度方差”,答案必写“引入baseline b(s)=V(s)”,但学生常忽略实现——直接用Gt更新,而不用Gt - V(s)。
解决:在REINFORCE中加入critic网络,用advantage = Gt - critic(s)替代Gt:

# critic网络预测V(s) v_pred = critic(torch.FloatTensor(state).unsqueeze(0)) advantage = Gt - v_pred.item() loss -= log_prob * advantage # 关键替换

4.5 现象:多智能体GridWorld考题中,联合动作空间爆炸,Q-table内存溢出

原因:盲目使用联合Q-learning(joint-Q),未采用IQL(Independent Q-Learning)或COMA等分解方法。考题若给出“2个agent在5x5网格”,联合动作空间为4×4=16维,Q-table大小5×5×16=400,尚可;但若扩展到3个agent,立刻变成5×5×4×4×4=2000,考题陷阱在此。
解决:考题明确“各agent仅观测自身位置”时,必须用IQL——每个agent独立训练Q_i(s_i, a_i),状态s_i仅为自身坐标。代码只需为每个agent维护独立q_table:

# agent_0_q_table.shape = (5,5,4), agent_1_q_table.shape = (5,5,4) # 更新时各自用自身state和action,互不影响

5. 考前72小时冲刺:用答案反向构建你的「策略检查清单」

别再从头推导贝尔曼方程了。考前最后三天,你要做的不是“学新东西”,而是把答案里的每一行代码、每一个reward数值、每一次收敛曲线,转化成可自查的工程检查点。我给学生用的 checklist 不是知识点罗列,而是带触发条件的动作指令——当满足某个现象时,立刻执行对应操作。下面这张表,就是你进考场前最后一遍手写的“防翻车指南”。

检查项触发条件(你在代码/草稿中观察到什么?)立即执行动作为什么这能救命
状态空间完整性考题说“agent可感知距离最近障碍物的方向”,但你的state只包含(x,y)坐标在state中追加2维:[dx_obstacle, dy_obstacle],用np.clip限制范围GridWorld类题80%的扣分点在于状态定义不全,漏掉关键观测——答案里reward能上去,是因为state包含了足够信息
奖励塑形合理性reward曲线前期上升快,后期卡在-5附近不动检查障碍物reward是否设为-5,目标reward是否≥10;若否,按比例放大(如障碍-50,目标+100)奖励量级失衡是收敛失败第一原因,答案里那个“+10”不是随意写的,是经过reward shaping验证的阈值
折扣因子适用性题干强调“长期规划能力”,但你的γ=0.9将γ提高到0.99,并同步调低learning rate(α从0.1→0.01)γ>0.95时TD error对α极度敏感,答案里高γ配低α是固定搭配,漏调α会导致震荡
策略确定性验证测试时agent在目标前一步反复左右移动运行test_policy()函数,打印每一步的np.argmax(q_table[s]),确认动作序列是否稳定考题“输出最优策略”指动作序列唯一,不是Q值最大,答案里那个热力图箭头方向,就是argmax结果
收敛判据有效性reward曲线平缓但仍在缓慢爬升,不确定是否结束训练计算最后100轮reward标准差,若<0.3则判定收敛;否则延长训练至标准差<0.3考题“说明收敛依据”标准答案就是“滑动窗口reward标准差低于阈值”,不是看绝对值

这张表的底层逻辑,是把答案从“静态结果”变成“动态过程记录”。比如你看到答案里reward曲线在2000轮后进入平台期,不要记“2000轮”,而要记“当滑动平均reward标准差<0.3时停止”。因为下一次考题可能把grid size改成6,收敛轮次必然变,但标准差阈值永远有效。

最后说个私藏技巧:每次调试卡住,立刻画三张图——① 当前策略热力图(用np.argmax(q_table, axis=2)生成箭头);② Q值最大值热力图(np.max(q_table, axis=2));③ reward累积曲线。这三张图叠在一起,90%的问题肉眼可见:如果策略图显示绕路,但Q值图在直线路线上数值更高,说明exploration不足;如果reward曲线震荡,但Q值图平滑,说明reward设计有问题。这个习惯我坚持了七年,救回过无数个濒临放弃的项目。

希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询