【问题标题】:Keras ImageDataGenerator for multiple inputs and image based target outputKeras ImageDataGenerator 用于多个输入和基于图像的目标输出
【发布时间】:2020-04-16 23:00:24
【问题描述】:

我有一个模型,它以两个图像作为输入并生成一个图像作为目标输出。

我所有的训练图像数据都在以下子文件夹中:

  • 输入1
  • 输入2
  • 目标

我可以在keras中使用ImageDataGenerator类和flow_from_directorymodel.fit_generator等方法来训练网络吗?

我该怎么做?因为我遇到的大多数示例都处理单个输入和基于标签的目标输出。

就我而言,我有一个非分类目标输出数据和多个输入。

请帮忙,因为您的建议真的很有帮助。

【问题讨论】:

    标签: tensorflow keras tensorflow-datasets tf.keras


    【解决方案1】:

    一种可能性是将三个ImageDataGenerator 合并为一个,使用class_mode=None(因此它们不会返回任何目标),并使用shuffle=False(重要)。确保您对每个使用相同的 batch_size,并确保每个输入位于不同的目录中,目标也在不同的目录中,并且每个目录中的图像数量完全相同。

    idg1 = ImageDataGenerator(...choose params...)
    idg2 = ImageDataGenerator(...choose params...)
    idg3 = ImageDataGenerator(...choose params...)
    
    gen1 = idg1.flow_from_directory('input1_dir',
                                    shuffle=False,
                                    class_mode=None)
    gen2 = idg2.flow_from_directory('input2_dir',
                                    shuffle=False,
                                    class_mode=None)
    gen3 = idg3.flow_from_directory('target_dir',
                                    shuffle=False,
                                    class_mode=None)
    

    创建自定义生成器:

    class JoinedGen(tf.keras.utils.Sequence):
        def __init__(self, input_gen1, input_gen2, target_gen):
            self.gen1 = input_gen1
            self.gen2 = input_gen2
            self.gen3 = target_gen
    
            assert len(input_gen1) == len(input_gen2) == len(target_gen)
    
        def __len__(self):
            return len(self.gen1)
    
        def __getitem__(self, i):
            x1 = self.gen1[i]
            x2 = self.gen2[i]
            y = self.gen3[i]
    
            return [x1, x2], y
    
        def on_epoch_end(self):
            self.gen1.on_epoch_end()
            self.gen2.on_epoch_end()
            self.gen3.on_epoch_end()
    

    用这个生成器训练:

    my_gen = JoinedGen(gen1, gen2, gen3)
    model.fit_generator(my_gen, ...)
    

    或者创建一个自定义生成器。它的所有结构如上所示。

    【讨论】:

    • 感谢丹尼尔的回答。我目前正在尝试您的解决方案。我也有同样的想法,但你的课真的很有用。但是,当我运行 mode.fit_generator() 时,我会为每个加载的图像收到以下警告消息:“警告:tensorflow:multiprocessing 可能与 TensorFlow 交互不良,导致不确定性死锁。对于高性能数据管道,建议使用 tf.data。”这似乎也减慢了训练速度,已经六个多小时了,我还没有看到 7000 张训练图像的结果
    • 我明白了....我从来没有见过这个错误...你是在fig_generator中设置multiprocessing=True吗?通常你不需要那个。你只需要多线程(标准),通过设置workers=...。它仍将是并行加载。
    • 嗨丹尼尔,我已经解决了我的问题,在禁用多进程后,您的解决方案效果很好。如何在 JoinedGen.__len__() 方法中调用 length 方法?
    • 你需要一个生成器的实例,答案是my_gen。您可以拨打length = len(my_gen),也可以拨打length = my_gen.__length__()
    【解决方案2】:

    Daniel Möller 的帖子中显示的自定义类 JoinedGen 如果不想(或不需要)打乱训练示例,则效果很好。然而,对于学习过程来说,在每个 epoch 结束时重新洗牌通常是非常可取的。幸运的是,这也很容易实现。首先,三个ImageDataGenerators应该使用shuffle = True(重要)。

    idg1 = ImageDataGenerator(...choose params...)
    idg2 = ImageDataGenerator(...choose params...)
    idg3 = ImageDataGenerator(...choose params...)
    
    gen1 = idg1.flow_from_directory('input1_dir',
                                    shuffle=True,
                                    class_mode=None)
    gen2 = idg2.flow_from_directory('input2_dir',
                                    shuffle=True,
                                    class_mode=None)
    gen3 = idg3.flow_from_directory('target_dir',
                                    shuffle=True,
                                    class_mode=None)
    

    如果什么都不做,三个生成器将在第一个 epoch 后不同步,因为它们中的每一个都会在第一个 epoch 结束时以不同的方式重新洗牌。为了使它们保持同步,需要在函数on_epoch_end(self) 的末尾添加两行。也就是说,最后两个生成器应该得到与第一个生成器相同的索引数组:

    class JoinedGen(tf.keras.utils.Sequence):
        def __init__(self, input_gen1, input_gen2, target_gen):
            self.gen1 = input_gen1
            self.gen2 = input_gen2
            self.gen3 = target_gen
    
            assert len(input_gen1) == len(input_gen2) == len(target_gen)
    
        def __len__(self):
            return len(self.gen1)
    
        def __getitem__(self, i):
            x1 = self.gen1[i]
            x2 = self.gen2[i]
            y = self.gen3[i]
    
            return [x1, x2], y
    
        def on_epoch_end(self):
            self.gen1.on_epoch_end()
            self.gen2.on_epoch_end()
            self.gen3.on_epoch_end()
            self.gen2.index_array = self.gen1.index_array
            self.gen3.index_array = self.gen1.index_array
    

    【讨论】:

      【解决方案3】:

      您可以创建一个处理整个操作的外部函数。

      generator_imgs = ImageDataGenerator(...choose params...)
      

      创建函数

      def generate_generator_multiple(generator):
          gen1 = generator.flow_from_directory('input1_dir', 
                                                shuffle=False, 
                                                class_mode=None,
                                                batch_size=32)
      
          gen2 = generator.flow_from_directory('input2_dir', 
                                                shuffle=False, 
                                                class_mode=None,
                                                batch_size=32)
      
          gen3 = generator.flow_from_directory('target_dir', 
                                                shuffle=False, 
                                                class_mode=None,
                                                batch_size=32)
          while True:
              x1 = gen1.next()
              x2 = gen2.next()
              y  = gen3.next()
              
              yield [x1, x2], y
      

      现在只需调用生成函数:

      my_gen = generate_generator_multiple(generator_imgs)
      model.fit_generator(my_gen, ...)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-09-15
        • 2021-03-04
        • 1970-01-01
        • 1970-01-01
        • 2022-07-23
        • 1970-01-01
        • 1970-01-01
        • 2018-02-26
        相关资源
        最近更新 更多