自定义Keras Sequence生成器报错:'generator'无'shape'属性
解决Keras自定义Sequence生成器的AttributeError问题
我之前帮朋友排查过几乎一模一样的问题,卡了好几天的错误终于找到根源了!先看看你遇到的报错:
/usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/engine/data_adapter.py in __init__(self, x, y, sample_weights, workers, use_multiprocessing, max_queue_size, model, **kwargs) 794 lambda x: model(x, training=False), args=(concrete_x,)) 795 --> 796 self._first_batch_size = int(nest.flatten(peek)[0].shape[0]) 797 798 def _get_dynamic_shape(t): AttributeError: 'generator' object has no attribute 'shape'
问题根源分析
你的自定义Mygenerator继承了Keras的Sequence类,但犯了几个关键错误:
- __getitem__不该用yield:
Sequence的设计逻辑是让这个方法直接返回单个batch的数据集,而不是作为生成器返回。你用了yield之后,Keras拿到的不是张量batch,而是一个生成器对象,自然会去访问它的shape属性,这就触发了错误。 - __len__计算逻辑错误:你用
len(self.x[1])来计算总步数,这完全不对——应该基于总样本数来计算batch数量,而且要保证所有输入的样本数一致。 - 切片写法错误:
self.x[:][int(idx * self.batch_size):int((idx + 1) * self.batch_size)[:]]这种写法是无效的,对于多输入的列表,需要遍历每个输入元素单独截取batch区间。 - 不必要的数组转换:在
__init__里把x_set和y_set转成np.array是多余的,HDF5Matrix本身支持切片操作,在取batch的时候再转成数组就好。
修正后的自定义生成器代码
class Mygenerator(Sequence): def __init__(self, x_set, y_set, batch_size): # x_set是多输入列表,每个元素对应模型的一个输入,形状为(n_samples, n_steps, n_features) # y_set是多输出列表,每个元素对应模型的一个输出,形状为(n_samples, 1) self.x = x_set self.y = y_set self.batch_size = batch_size # 总样本数,假设所有输入/输出的样本数一致 self.n_samples = len(self.x[0]) def __len__(self): # 计算总batch数,向上取整确保最后一个batch也被处理 return int(np.ceil(self.n_samples / self.batch_size)) def __getitem__(self, idx): # 计算当前batch的起始和结束索引,避免越界 start_idx = idx * self.batch_size end_idx = min((idx + 1) * self.batch_size, self.n_samples) # 为每个输入截取对应的batch,并转成numpy数组 X_batch = [np.array(x[start_idx:end_idx]) for x in self.x] # 为每个输出截取对应的batch,并转成numpy数组 y_batch = [np.array(y[start_idx:end_idx]) for y in self.y] # 直接返回batch,不要用yield! return X_batch, y_batch
额外的模型构建小问题
你的模型构建代码里还有个小bug:concatenate之后直接用了dense变量,但这个变量没有定义,需要先加一个Dense层:
merge = concatenate([output_y[s] for s in range(len(output_y))]) # 新增这一行,定义dense层 dense = Dense(64, activation=lambda x: tf.nn.leaky_relu(x, alpha=0.2))(merge) listDense = list() for x in range(7): #len(floodplain) output = Dense(1, activation= lambda x : tf.nn.leaky_relu(x, alpha=0.2), name="output{}".format(x+1))(dense) listDense.append(output) update_progress((x+1)/7)
修正后的fit调用
不需要手动指定steps_per_epoch,因为Sequence的__len__已经返回了总步数,Keras会自动识别:
hist = model.fit( Mygenerator([X_tr_list[i] for i in range(len(X_tr_list))], [Y_tr[i] for i in range(Y_tr.shape[0])], 32), epochs=1000, verbose=1, callbacks=[checkpoint, es, best], validation_data=Mygenerator([X_te_list[i] for i in range(len(X_te_list))], [Y_te[i] for i in range(Y_te.shape[0])], 32) )
这些修改应该能彻底解决你遇到的AttributeError问题,核心就是严格遵循Sequence类的设计规范,别把它当成普通生成器来写。
内容的提问来源于stack exchange,提问作者손상우
相关产品推荐
相关产品推荐

