【问题标题】:Generator "TypeError: 'generator' object is not an iterator"生成器“TypeError:‘生成器’对象不是迭代器”
【发布时间】:2019-02-08 10:51:10
【问题描述】:

由于 RAM 内存的限制,我按照these 的说明构建了一个生成器,它可以绘制小批量,并将它们传递到 Keras 的 fit_generator 中。 但是即使我继承了序列,Keras 也无法使用多处理准备队列。

这是我的多处理生成器。

class My_Generator(Sequence):
    def __init__(self, image_filenames, labels, batch_size):
        self.image_filenames, self.labels = image_filenames, labels
        self.batch_size = batch_size

    def __len__(self):
        return np.ceil(len(self.image_filenames) / float(self.batch_size))

    def __getitem__(self, idx):
        batch_x = self.image_filenames[idx * self.batch_size:(idx + 1) * self.batch_size]
        batch_y = self.labels[idx * self.batch_size:(idx + 1) * self.batch_size]

    return np.array([
        resize(imread(file_name), (200, 200))
           for file_name in batch_x]), np.array(batch_y)

主要功能:

batch_size = 100
num_epochs = 10
train_fnames = []
mask_training = []
val_fnames = [] 
mask_validation = []

我希望生成器通过 ID 在不同线程中分别读取文件夹中的批次(其中 ID 类似于:{number}.csv 用于原始图像,{number}_label.csv 用于掩码图像)。我最初构建了另一个更优雅的类来将每个数据存储在一个 .h5 文件而不是目录中。但阻止了同样的问题。因此,如果您有执行此操作的代码,我也接受。

for dirpath, _, fnames in os.walk('./train/'):
    for fname in fnames:
        if 'label' not in fname:
            training_filenames.append(os.path.abspath(os.path.join(dirpath, fname)))
        else:
            mask_training.append(os.path.abspath(os.path.join(dirpath, fname)))
for dirpath, _, fnames in os.walk('./validation/'):
    for fname in fnames:
        if 'label' not in fname:
            validation_filenames.append(os.path.abspath(os.path.join(dirpath, fname)))
        else:
            mask_validation.append(os.path.abspath(os.path.join(dirpath, fname)))


my_training_batch_generator = My_Generator(training_filenames, mask_training, batch_size)
my_validation_batch_generator = My_Generator(validation_filenames, mask_validation, batch_size)
num_training_samples = len(training_filenames)
num_validation_samples = len(validation_filenames)

在此,模型超出范围。相信不是模型的问题所以就不贴了。

mdl = model.compile(...)
mdl.fit_generator(generator=my_training_batch_generator,
              steps_per_epoch=(num_training_samples // batch_size),
              epochs=num_epochs,
              verbose=1,
              validation_data=None, #my_validation_batch_generator,
              # validation_steps=(num_validation_samples // batch_size),
              use_multiprocessing=True,
              workers=4,
              max_queue_size=2)

报错说明我创建的类不是Iterator:

Traceback (most recent call last):
File "test.py", line 141, in <module> max_queue_size=2)
File "/anaconda3/lib/python3.6/site-packages/tensorflow/python/keras/engine/training.py", line 2177, in fit_generator
initial_epoch=initial_epoch)
File "/anaconda3/lib/python3.6/site-packages/tensorflow/python/keras/engine/training_generator.py", line 147, in fit_generator
generator_output = next(output_generator)
File "/anaconda3/lib/python3.6/site-packages/tensorflow/python/keras/utils/data_utils.py", line 831, in get six.reraise(value.__class__, value, value.__traceback__)
File "/anaconda3/lib/python3.6/site-packages/six.py", line 693, in reraise
raise value
TypeError: 'My_Generator' object is not an iterator

【问题讨论】:

  • 这很奇怪。您无需指定steps_per_epoch,它会在您传递Sequence 对象时自动从__len__ 计算得出。
  • 可以看到def __getitem__(self, idx):中的return语句不属于函数。是错字还是您确实没有返回?
  • @DmytroPrylipko idx 可能是 Keras 的 fit_generator 的内在特性。在链接中,它本身也是悬空的。我不知道更多细节。
  • @nuric 如果我不指定steps_per_epoch,我得到ValueError: 'steps_per_epoch=None' is only valid for a generator based on the 'keras.utils.Sequence' class. Please specify 'steps_per_epoch' or use the 'keras.utils.Sequence' class. 错误
  • 听起来您没有从正确的keras.utils.Sequence 继承。 My_Generator的超类型是什么?

标签: python keras neural-network multiprocessing


【解决方案1】:

我遇到了同样的问题,我通过定义 __next__ 方法设法解决了这个问题:

class My_Generator(Sequence):
    def __init__(self, image_filenames, labels, batch_size):
        self.image_filenames, self.labels = image_filenames, labels
        self.batch_size = batch_size
        self.n = 0
        self.max = self.__len__()


    def __len__(self):
        return np.ceil(len(self.image_filenames) / float(self.batch_size))

    def __getitem__(self, idx):
        batch_x = self.image_filenames[idx * self.batch_size:(idx + 1) * self.batch_size]
        batch_y = self.labels[idx * self.batch_size:(idx + 1) * self.batch_size]

        return np.array([
        resize(imread(file_name), (200, 200))
           for file_name in batch_x]), np.array(batch_y)

    def __next__(self):
        if self.n >= self.max:
           self.n = 0
        result = self.__getitem__(self.n)
        self.n += 1
        return result

请注意,我在__init__ 函数中声明了两个新变量。

【讨论】:

  • 为什么设置self.n = 0?这是因为我们希望能够遍历每个 epoch 的数据吗?
  • 通常你可能想要训练你的模型很长时间。这意味着您可能需要多次检查数据。通过设置self.n = 0,您将确保生成器在到达数据末尾时不会遇到错误,它只会重新开始。请注意,这并不一定意味着在每个时期循环数据,但这是一种可能的情况。
【解决方案2】:

我收到了同样的错误,说我的生成器类继承自 keras.utils.Sequence 对象 is not an iterator

添加__next__ 方法或在keras.utils.Sequencetf.keras.utils.Sequence 之间进行更改都没有帮助。

对我来说,我的 __getitem__ 类没有正确实现。在尝试使用所有数据时,最后一批是我没有正确处理的部分批次。当我正确处理这个问题时,object is not an iterator 错误就消失了。因此,我建议您仔细检查您的 __getitem__() 实现并考虑所有传递给 __getitem__()index 值。

【讨论】:

    【解决方案3】:

    我想通了。 我的模特是from keras.models import Model 但是生成器是从class DataGenerator(tf.keras.utils.Sequence): 扩展而来的 这导致了错误! 因此,只需将生成器 class DataGenerator(tf.keras.utils.Sequence) 更改为 class DataGenerator(keras.utils.Sequence):

    【讨论】:

    • 这没有修复错误“对象不是迭代器”。可能这个错误与kerastf 版本有关。
    猜你喜欢
    • 2016-03-13
    • 1970-01-01
    • 2017-12-18
    • 2019-05-28
    • 2018-05-16
    • 2018-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多