使用Keras Functional API训练OpenAI LunarLander-v2模型时遭遇输入形状不兼容ValueError
看起来你遇到的核心问题是数据在记忆存储和提取环节出现了错位,再加上一些小的代码错误,导致输入模型的张量形状完全不符合预期。我们一步步来拆解修复:
1. 最致命的错误:记忆存储时混入了多余的self
看你的addToMemory方法:
def addToMemory(self, state, action, reward, next_state, done): self.memory.append((self, state, action, reward, next_state, done))
你把self(Agent实例)作为元组的第一个元素加入了记忆队列!这直接导致后面extractFromSample的时候:
states = np.array([i[0] for i in sample])实际取到的是一堆Agent对象,而不是环境状态- 真正的state被存在了
i[1],next_state在i[4],done在i[5],完全错位了
修正这个方法,去掉多余的self:
def addToMemory(self, state, action, reward, next_state, done): self.memory.append((state, action, reward, next_state, done))
2. 记忆采样函数的错误
你的sampleFromMemory用了np.random.sample,这个函数是用来生成[0,1)之间的随机浮点数,不是从序列里采样样本。应该用random.sample(Python标准库)来抽取记忆样本:
def sampleFromMemory(self): # 使用random.sample从记忆中抽取指定数量的样本 sample = random.sample(self.memory, self.config.batch_size) return sample
记得在文件顶部导入import random。
3. 修正样本提取的索引与形状处理
因为上面修正了addToMemory的元组顺序,现在extractFromSample的索引需要对应正确的位置,同时注意squeeze的使用场景:
def extractFromSample(self, sample): states = np.array([i[0] for i in sample]) actions = np.array([i[1] for i in sample]) rewards = np.array([i[2] for i in sample]) next_states = np.array([i[3] for i in sample]) done_list = np.array([i[4] for i in sample]) # 只有当state本身是(1,8)形状时才需要squeeze,如果是(8,)则不需要 # 堆叠后的states默认是(batch_size,8),正好匹配模型输入要求 # states = np.squeeze(states) # next_states = np.squeeze(next_states) return states, actions, rewards, next_states, done_list
4. 策略动作生成时的输入形状处理
在policyAct方法中,环境返回的state通常是一维数组((8,)),而Keras模型期望输入是批量数据(形状为(batch_size, 8)),所以需要给state增加一个维度:
def policyAct(self, state): # 给state增加batch维度,从(8,)变成(1,8) state = np.expand_dims(state, axis=0) predicted_actions = self.model.predict(state, verbose=0) return np.argmax(predicted_actions[0])
加上verbose=0可以避免每次predict都打印日志,训练过程更清爽。
5. 验证模型输入输出形状
你的Functional模型定义是正确的,输入(None,8)对应批量的8维状态,输出(None,4)对应每个状态的4个动作值(LunarLander-v2的动作空间是4)。修正上面的问题后,输入模型的states和next_states应该都是(batch_size,8),和模型期望的形状完全匹配。
把这些修正点全部应用后,你的Functional模型应该就能和Sequential模型一样正常运行了。
内容的提问来源于stack exchange,提问作者DeepNoise

