
在使用Keras构建深度强化学习模型并结合`keras-rl`库中的`DQNAgent`时,模型输出形状错误是一个常见问题。本文旨在详细解释当Keras模型突然输出带有额外维度(例如`(None, 1, num_actions)`)的张量,导致与`DQNAgent`期望的扁平输出形状(`(None, num_actions)`)不兼容时,如何诊断并解决这一问题。核心解决方案在于正确配置Keras `InputLayer`的`input_shape`,确保其与强化学习环境的观测空间以及`DQNAgent`的期望输入格式保持一致。
在使用keras-rl库中的DQNAgent进行训练时,一个常见的错误是模型输出的形状与DQNAgent所期望的不符。具体表现为,模型可能输出形如Tensor("dense_2/BiasAdd:0", shape=(None, 1, 2), dtype=float32)的张量,而DQNAgent则明确要求输出形状为(None, nb_actions),其中nb_actions是动作空间的大小。这种不匹配通常会导致ValueError: Model output "..." has invalid shape. DQN expects a model that has one dimension for each action...。
这个问题的根本原因往往不在于TensorFlow内部的调试设置(例如tensorflow.compat.v1.experimental.output_all_intermediates(True)),而在于Keras模型定义中的InputLayer配置。当InputLayer被设置为接受一个序列维度时,即使后续层是全连接层,也可能保留这个序列维度,从而导致最终输出多出一个不必要的维度。
考虑以下示例代码片段,它展示了问题的典型场景:
import gymnasium as gym
import numpy as np
from rl.agents import DQNAgent
from rl.memory import SequentialMemory
from rl.policy import BoltzmannQPolicy
from tensorflow.python.keras.layers import InputLayer, Dense
from tensorflow.python.keras.models import Sequential
from tensorflow.python.keras.optimizer_v2.adam import Adam
if __name__ == '__main__':
env = gym.make("CartPole-v1")
nb_actions = env.action_space.n # 通常为2
model = Sequential()
# 问题所在:input_shape=(1, 4) 引入了不必要的序列维度
model.add(InputLayer(input_shape=(1, env.observation_space.shape[0])))
model.add(Dense(24, activation="relu"))
model.add(Dense(24, activation="relu"))
model.add(Dense(nb_actions, activation="linear")) # 期望输出形状 (None, nb_actions)
model.build()
print(model.summary())
# 此时 model.summary() 会显示输出形状为 (None, 1, nb_actions)
# ...在上述代码中,InputLayer(input_shape=(1, env.observation_space.shape[0]))的定义是导致问题的关键。对于CartPole这类环境,其观测空间是一个扁平的向量(例如4维),DQNAgent通常期望直接接收这个扁平向量作为输入,并输出对应每个动作的Q值。input_shape=(1, 4)错误地为输入引入了一个长度为1的序列维度,使得模型后续的全连接层虽然处理了数据,但这个序列维度仍然被保留,最终导致模型输出形状变为(None, 1, nb_actions)。
解决这个问题的关键在于将InputLayer的input_shape设置为与环境的观测空间完全匹配的扁平形状。对于CartPole环境,其观测空间是一个4维向量,因此正确的input_shape应该是(4,),而不是(1, 4)。
修正后的Keras模型定义应如下所示:
import gymnasium as gym
import numpy as np
from rl.agents import DQNAgent
from rl.memory import SequentialMemory
from rl.policy import BoltzmannQPolicy
from tensorflow.python.keras.layers import InputLayer, Dense
from tensorflow.python.keras.models import Sequential
from tensorflow.python.keras.optimizer_v2.adam import Adam
if __name__ == '__main__':
env = gym.make("CartPole-v1")
nb_actions = env.action_space.n # 通常为2
model = Sequential()
# 修正后的InputLayer:直接使用环境观测空间的形状
model.add(InputLayer(input_shape=(env.observation_space.shape[0],)))
model.add(Dense(24, activation="relu"))
model.add(Dense(24, activation="relu"))
model.add(Dense(nb_actions, activation="linear"))
model.build()
print(model.summary())
# 此时 model.summary() 会显示输出形状为 (None, nb_actions),符合DQNAgent期望
agent = DQNAgent(
model=model,
memory=SequentialMemory(limit=50000, window_length=1),
policy=BoltzmannQPolicy(),
nb_actions=nb_actions,
nb_steps_warmup=100,
target_model_update=0.01
)
agent.compile(Adam(learning_rate=0.001), metrics=["mae"])
agent.fit(env, nb_steps=100000, visualize=False, verbose=1)
results = agent.test(env, nb_episodes=10, visualize=True)
print(np.mean(results.history["episode_reward"]))
env.close()通过将input_shape从(1, 4)改为(4,),模型将正确地将观测值视为一个扁平向量,并通过全连接层输出每个动作对应的Q值,其形状为(None, nb_actions),从而满足DQNAgent的要求。
当Keras模型与keras-rl的DQNAgent集成时出现输出形状不匹配的ValueError时,最常见的原因是InputLayer的input_shape配置不当。通过将input_shape精确地设置为与环境观测空间匹配的扁平维度,可以有效地解决这一问题。理解并遵循DQNAgent对模型输入输出形状的期望,以及利用model.summary()进行诊断,是构建稳定高效强化学习模型的关键实践。
以上就是解决Keras模型与DQNAgent输出形状不匹配问题的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号