我目前正在 OpenAI Gym 中为 FrozenLake-v1 环境实施 Q-learning。然而,我的 Q 表似乎在训练期间没有更新,并且仍然充满零。我已经多次检查了我的代码,但我无法查明问题所在。
这是我正在使用的代码:
import gymnasium as gym
import numpy as np
import random
def run():
env = gym.make("FrozenLake-v1") # setup env
Q = np.zeros((env.observation_space.n, env.action_space.n)) # empty q_table
alpha = 0.7
gamma = 0.95
epsilon = 0.9
epsilon_decay = 0.005
epsilon_min = 0.01
episode = 0
episodes = 10000
state, info = env.reset()
print("Before training")
print(Q)
while episode < episodes:
if epsilon > epsilon_min:
epsilon -= epsilon_decay
if random.random() < epsilon:
action = env.action_space.sample()
else:
action = np.argmax(Q[state])
new_state, reward, terminated, truncated, info = env.step(action)
Q[state, action] = Q[state, action] + alpha * (float(reward) + gamma * np.max(Q[new_state]) - Q[state, action])
state = new_state
if terminated or truncated:
episode += 1
state, info = env.reset() # Reset the environment
print("After training")
print(Q)
env.close()
run()
我怀疑这个问题可能与我更新 Q 表或处理环境状态的方式有关。任何识别和解决问题的帮助将不胜感激。
我添加了打印语句来显示中间值,包括训练期间选定的操作、奖励和 Q 表本身。这是为了检查值是否按预期更新。我尝试用较少数量的剧集来训练代理,以简化问题并观察 Q 表是否开始更新。然而,即使剧集数量减少,Q 表仍然充满零。我重新审视了 Q 表更新公式,以确保它与 Q 学习算法保持一致。公式看起来是正确的,但问题仍然存在。
我预计 Q 表会在训练期间逐渐更新,反映智能体对状态-动作对的学习值。然而,即使在运行指定次数的训练循环后,Q 表也保持不变,并用零填充。
该问题是由两个问题共同造成的:
如果数组中有多个最大值,
np.argmax
将返回出现最大值的第一个索引。最初,Q 表中的所有值都是 0,因此每当您采取利用步骤时,您将采取第一个操作,在本例中是“向左移动”。
除了到达目标状态外,所有奖励都是零,因此只有在您第一次找到目标状态(并获得奖励1)之后,Q表才会开始包含非零值。 你的智能体不太可能在前几百集中找到目标状态,并且由于 epsilon 很快衰减到 0.01,所以你大部分时间都在采取利用步骤(即向左移动),获得 0 的奖励,并且不使对 Q 表进行任何有意义的更新。
我建议使用以下函数,而不是
np.argmax
,该函数返回出现最大值的随机索引:
def argmax(arr):
arr_max = np.max(arr)
return np.random.choice(np.where(arr == arr_max)[0])
此外,epsilon 的这些超参数更加合理。 使用此方法,epsilon 将在一半左右达到最小值 培训内容:
epsilon = 1
epsilon_decay = (2 * epsilon) / episodes
epsilon_min = 0.001