我在具有1个GPU(GTX 1070)和4个CPU的ubuntu 16.04上运行我的神经网络。

我的数据集包含大约35,000张图像,但数据集不平衡:0类占90%,而1,2,3,4类则占另外10%。因此,我通过使用dataset.repeat(class_weight) [我也使用函数来应用随机增强]对1-4类进行了过度采样,然后对其进行concatenate编码。

重采样策略是:

1)首先,将class_weight[n]设置为一个较大的数字,以便每个类将具有与类0相同的图像数量。

2)随着训练的进行,历元数增加,并且权重将根据历元数下降,从而使分布变得更接近实际分布。

因为我的class_weight会随着时代的变化而变化,所以我不能在一开始就对整个数据集进行洗牌。取而代之的是,我必须逐个类别地接收数据,并在连接每个类别的过采样数据后重新整理整个数据集。而且,为了实现平衡的批次,我必须逐个元素地对整个数据集进行洗牌。

以下是我的代码的一部分。

def my_estimator_func():
    d0 = tf.data.TextLineDataset(train_csv_0).map(_parse_csv_train)
    d1 = tf.data.TextLineDataset(train_csv_1).map(_parse_csv_train)
    d2 = tf.data.TextLineDataset(train_csv_2).map(_parse_csv_train)
    d3 = tf.data.TextLineDataset(train_csv_3).map(_parse_csv_train)
    d4 = tf.data.TextLineDataset(train_csv_4).map(_parse_csv_train)
    d1 = d1.repeat(class_weight[1])
    d2 = d2.repeat(class_weight[2])
    d3 = d3.repeat(class_weight[3])
    d4 = d4.repeat(class_weight[4])
    dataset = d0.concatenate(d1).concatenate(d2).concatenate(d3).concatenate(d4)
    dataset = dataset.shuffle(180000) # <- This is where the issue comes from
    dataset = dataset.batch(100)
    iterator = dataset.make_one_shot_iterator()
    feature, label = iterator.get_next()
    return feature, label

def _parse_csv_train(line):
    parsed_line= tf.decode_csv(line, [[""], []])
    filename = parsed_line[0]
    label = parsed_line[1]
    image_string = tf.read_file(filename)
    image_decoded = tf.image.decode_jpeg(image_string, channels=3)
    # my_random_augmentation_func will apply random augmentation on the image.
    image_aug = my_random_augmentation_func(image_decoded)
    image_resized = tf.image.resize_images(image_aug, image_resize)
    return image_resized, label

为了明确起见,让我逐步说明为什么要面对此问题:
  • 因为数据集中的类不平衡,所以我想对那些少数类进行过度采样。
  • 由于1,我想在这些类上应用随机扩充,然后将多数类(类0)与它们连接。
  • 经过研究,我发现如果其中有一个随机函数,repeat()会产生不同的结果,因此我将repeat()与my_random_augmentation_func一起使用以实现2。
  • 现在,已经达到2,我想合并所有数据集,所以我使用concatenate()
  • 在4之后。我现在面临一个问题:总共大约有40,000-180,000张图像(因为class_weight逐个时代地变化,开始时总共有180,000张图像,最后大约有40,000张),它们如果按类别进行级联,则数据集将类似于[0000-1111-2222-3333-4444],因此,批次大小为100,没有任何改组,每个批次中几乎总是只有一个类别,这意味着分布在每一批将是不平衡的。
  • 为了解决5中的“批次不平衡”问题,我想到了应该对整个数据集进行混洗的想法,因此我使用shuffle(180000)
  • 最后,我的计算机停滞不前,因为它涉及洗洗数据集中的180000个项目。

  • 因此,是否有更好的方法可以使批次平衡,但仍保持所需的特征(例如,一个时期地更改分配时期)?

    ---编辑:问题已解决---

    原来我不应该应用map函数
    在开头
    。我应该只接受文件名而不是真实文件,然后随机播放文件名,然后将它们映射到真实文件。

    更详细地,删除map(_parse_csv_train)和其他4行之后的d0 = tf.data.TextLineDataset(train_csv_0)部分,并在 shuffle(180000)之后添加新行dataset = dataset.map(_parse_csv_train)

    我也想对@ P-Gn表示感谢,他的“改组”部分中的博客链接确实很有帮助。它回答了我脑海中的一个问题,但我没有问:“我可以通过使用许多小混洗与一个大混洗来获得类似的随机性吗?” (我不会在这里给出答案,请查看该博客!)该博客中的方法也可能是此问题的潜在解决方案,但我还没有尝试过。

    最佳答案

    我建议依赖 tf.contrib.data.choose_from_datasets ,并由 tf.multinomial 分布选择标签。与基于样本拒绝的其他功能相比,此方法的优点是您不会失去读取未使用样本的I/O带宽。

    这是一个与您的案例类似的工作示例,带有一个虚拟数据集:

    import tensorflow as tf
    
    # create dummy datasets
    class_num_samples = [900, 25, 25, 25, 25]
    class_start = [0, 1000, 2000, 3000, 4000]
    ds = [
      tf.data.Dataset.range(class_start[0], class_start[0] + class_num_samples[0]),
      tf.data.Dataset.range(class_start[1], class_start[1] + class_num_samples[1]),
      tf.data.Dataset.range(class_start[2], class_start[2] + class_num_samples[2]),
      tf.data.Dataset.range(class_start[3], class_start[3] + class_num_samples[3]),
      tf.data.Dataset.range(class_start[4], class_start[4] + class_num_samples[4])
    ]
    
    # pick from dataset according to a parameterizable distribution
    class_relprob_ph = tf.placeholder(tf.float32, shape=len(class_num_samples))
    pick = tf.data.Dataset.from_tensor_slices(
      tf.multinomial(tf.log(class_relprob_ph)[None], max(class_num_samples))[0])
    ds = tf.contrib.data.choose_from_datasets(ds, pick).repeat().batch(20)
    
    iterator = ds.make_initializable_iterator()
    batch = iterator.get_next()
    
    with tf.Session() as sess:
      # choose uniform distribution
      sess.run(iterator.initializer, feed_dict={class_relprob_ph: [1, 1, 1, 1, 1]})
      print(batch.eval())
    # [   0 1000 1001    1 3000 4000 3001 4001    2    3 1002 1003 2000    4    5 2001 3002 1004    6 2002]
    
      # now follow input distribution
      sess.run(iterator.initializer, feed_dict={class_relprob_ph: class_num_samples})
      print(batch.eval())
    # [   0    1 4000    2    3    4    5 3000    6    7    8    9 2000   10   11   12   13 4001   14   15]
    

    注意,“历元”的长度现在由多项式采样的长度定义。我在这里将其任意地设置为max(class_num_samples) -在开始混合不同长度的数据集时,确实没有很好的选择来定义纪元。

    但是,有一个具体的原因,使其至少与最大的数据集一样大:正如您所注意到的,调用iterator.initializer会从头开始重新启动Dataset。因此,既然改组缓冲区比数据小得多(通常是这种情况),重要的是不要提早开始以确保训练能够看到所有数据。

    关于改组

    该答案解决了使用自定义权重交织数据集的问题,而不是数据集混洗的问题,这是一个不相关的问题。对大型数据集进行改组需要做出让步-在不以某种方式牺牲内存和性能的情况下,您将无法进行有效的动态改组。例如,关于该主题的this excellent blog post以图形方式说明了缓冲区大小对混洗质量的影响。

    关于python - 如何混合不平衡的数据集以达到每个标签所需的分布?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/52325149/

    10-12 18:32
    查看更多