【问题标题】:Sampling for large class and augmentation for small classes in each batch在每批中对大类进行抽样和对小类进行扩充
【发布时间】:2022-01-13 19:12:04
【问题描述】:

假设我们有 2 个类,一个小,第二个大。

我想用于类似于ImageDataGenerator 的数据增强 对于小班,并从每批中抽样,以这样的方式,即每 batch 将是 平衡。 (从小类增强到大类采样)。

另外,我想继续使用image_dataset_from_directory(因为数据集不适合 RAM)。

【问题讨论】:

    标签: tensorflow image-processing keras sampling data-augmentation


    【解决方案1】:

    怎么样 sample_from_datasets 功能?

    import tensorflow as tf
    from tensorflow.python.data.experimental import sample_from_datasets
    
    def augment(val):
        # Example of augmentation function
        return val - tf.random.uniform(shape=tf.shape(val), maxval=0.1)
    
    big_dataset_size = 1000
    small_dataset_size = 10
    
    # Init some datasets
    dataset_class_large_positive = tf.data.Dataset.from_tensor_slices(tf.range(100, 100 + big_dataset_size, dtype=tf.float32))
    dataset_class_small_negative = tf.data.Dataset.from_tensor_slices(-tf.range(1, 1 + small_dataset_size, dtype=tf.float32))
    
    # Upsample and augment small dataset
    dataset_class_small_negative = dataset_class_small_negative \
        .repeat(big_dataset_size // small_dataset_size) \
        .map(augment)
    
    dataset = sample_from_datasets(
        datasets=[dataset_class_large_positive, dataset_class_small_negative], 
        weights=[0.5, 0.5]
    )
    
    dataset = dataset.shuffle(100)
    dataset = dataset.batch(6)
    
    iterator = dataset.as_numpy_iterator()
    for i in range(5):
        print(next(iterator))
    
    # [109.        -10.044552  136.        140.         -1.0505208  -5.0829906]
    # [122.        108.        141.         -4.0211563 126.        116.       ]
    # [ -4.085523  111.         -7.0003924  -7.027302   -8.0362625  -4.0226436]
    # [ -9.039093  118.         -1.0695585 110.        128.         -5.0553837]
    # [100.        -2.004463  -9.032592  -8.041705 127.       149.      ]
    

    sample_from_datasetsweights 参数中设置类之间所需的平衡。

    正如人们注意到的那样 Yaoshiang, 最后一批不平衡,数据集长度不同。这可以通过

    来避免
    # Repeat infinitely both datasets and augment the small one
    dataset_class_large_positive = dataset_class_large_positive.repeat()
    dataset_class_small_negative = dataset_class_small_negative.repeat().map(augment)
    

    而不是

    # Upsample and augment small dataset
    dataset_class_small_negative = dataset_class_small_negative \
        .repeat(big_dataset_size // small_dataset_size) \
        .map(augment)
    

    但是这种情况下,数据集是无限的,epoch中的batch数还需要进一步控制。

    【讨论】:

    • 如何将数据增强应用于“small_negative_class”?
    • @MichaelD 像往常一样,例如dataset_class_small_negative = dataset_class_small_negative.map(augmentation_func)
    • 据我了解,您需要训练从不平衡类构建的平衡批次。做到这一点的唯一方法是对小数据集进行过采样。因此,为了产生相同数量的样本,自然需要repeat。可能您只需要在 repeat 之后扩充数据集。
    • 建议了我的更改版本。数据集carnality 需要在开始之前处理整个数据集,这是不可取的。所以如果你不想使用无限repeat,我们必须提前知道数据集的大小。
    • @Yaoshiang,这两个问题都可以通过简单地将'.repeat()'应用于两个数据集而不是.repeat(int(dataset_size_ration)) 仅应用于小数据集来解决。在这种情况下,您必须在您的 epoch 期间手动限制采样批次的数量。这是上面讨论的几个按摩这里。这个问题的作者更喜欢有限重复方法。这激发了当前版本的答案。
    【解决方案2】:

    您可以使用 tf.data.Dataset.from_generator 来更好地控制数据生成,而无需将所有数据加载到 RAM 中。

    def generator():
     i=0   
     while True :
       if i%2 == 0:
          elem = large_class_sample()
       else :
          elem =small_class_augmented()
    
       yield elem
       i=i+1
      
    
    ds= tf.data.Dataset.from_generator(
             generator,
             output_signature=(
                 tf.TensorSpec(shape=yourElem_shape , dtype=yourElem_ype))
        
    

    这个生成器会在两个类之间改变样本,你可以添加更多的数据集操作(batch , shuffle..)

    【讨论】:

      【解决方案3】:

      我没有完全理解这个问题。伪代码会起作用吗?也许tf.data.Dataset上有一些运营商足以解决你的问题。

      ds = image_dataset_from_directory(...)
      
      ds1=ds.filter(lambda image, label: label == MAJORITY)
      ds2=ds.filter(lambda image, label: label != MAJORITY)
      
      ds2 = ds2.map(lambda image, label: data_augment(image), label)
      
      ds1.batch(int(10. / MAJORITY_RATIO))
      ds2.batch(int(10. / MINORITY_RATIO))
      
      ds3 = ds1.zip(ds2)
      
      ds3 = ds3.map(lambda left, right: tf.concat(left, right, axis=0)
      

      【讨论】:

      • 我会尝试将其转换为代码并测试然后更新。
      • 你能澄清int(10. / MAJORITY_RATIO)的目的吗?我试图做一个简单的例子,但它不起作用。缺了点什么。可能是 large class* 的重采样。此外,每批似乎并不平衡。您可以添加一些以 range(100)-range(10) 作为输入的示例 吗?
      【解决方案4】:

      您可以使用 tf.data.Dataset.from_tensor_slices 分别加载两个类别的图像,并对少数类进行数据增强。现在您有了两个数据集,将它们与 tf.data.Dataset.sample_from_datasets 结合起来。

      # assume class1 is the minority class
      files_class1 = glob('class1\\*.jpg')
      files_class2 = glob('class2\\*.jpg')
      
      def augment(filepath):
          class_name = tf.strings.split(filepath, os.sep)[0]
          image = tf.io.read_file(filepath)
          image = tf.expand_dims(image, 0)
          if tf.equal(class_name, 'class1'):
              # do all the data augmentation
              image_flip = tf.image.flip_left_right(image)
          return [[image, class_name],[image_flip, class_name]]
      
      # apply data augmentation for class1
      train_class1 = tf.data.Dataset.from_tensor_slices(files_class1).\
      map(augment,num_parallel_calls=tf.data.AUTOTUNE)
      train_class2 = tf.data.Dataset.from_tensor_slices(files_class2)
      
      dataset = tf.python.data.experimental.sample_from_datasets(
      datasets=[train_class1,train_class2], 
      weights=[0.5, 0.5])
      
      dataset = dataset.batch(BATCH_SIZE)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-02-18
        • 1970-01-01
        • 1970-01-01
        • 2022-11-21
        • 2013-07-28
        • 1970-01-01
        • 1970-01-01
        • 2021-01-08
        相关资源
        最近更新 更多