Python 如何混合不平衡的数据集以达到每个标签所需的分布?

Python 如何混合不平衡的数据集以达到每个标签所需的分布?,python,tensorflow,tensorflow-datasets,Python,Tensorflow,Tensorflow Datasets,我在ubuntu 16.04上运行我的神经网络,有1个GPU(GTX 1070)和4个CPU 我的数据集包含大约35000张图像,但数据集并不平衡:0类占90%,1、2、3、4类占10%。因此,我使用数据集对1-4类进行了抽样。重复(类权重)[我还使用一个函数应用随机增强],然后将它们串联起来 重新采样策略是: 1) 在最开始时,class\u weight[n]将被设置为一个较大的数字,这样每个类将具有与类0相同数量的图像 2) 随着训练的进行,历元数会增加,权重会随着历元数的增加而下降,从而

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

我的数据集包含大约35000张图像,但数据集并不平衡:0类占90%,1、2、3、4类占10%。因此,我使用
数据集对1-4类进行了抽样。重复(类权重)
[我还使用一个函数应用随机增强],然后
将它们串联起来

重新采样策略是:

1) 在最开始时,
class\u weight[n]
将被设置为一个较大的数字,这样每个类将具有与类0相同数量的图像

2) 随着训练的进行,历元数会增加,权重会随着历元数的增加而下降,从而使分布更接近实际分布

因为我的
class\u权重
会随时代而变化,所以我不能在一开始就对整个数据集进行洗牌。相反,我必须一类接一类地接收数据,并在连接每个类的过采样数据后洗牌整个数据集。而且,为了实现均衡的批处理,我必须对整个数据集进行元素洗牌

下面是我代码的一部分

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
def my_estimator_func():
d0=tf.data.TextLineDataset(train\u csv\u 0).map(\u parse\u csv\u train)
d1=tf.data.TextLineDataset(train\u csv\u 1).map(\u parse\u csv\u train)
d2=tf.data.TextLineDataset(train\u csv\u 2).map(\u parse\u csv\u train)
d3=tf.data.TextLineDataset(train\u csv\u 3).map(\u parse\u csv\u train)
d4=tf.data.TextLineDataset(train\u csv\u 4).map(\u parse\u csv\u train)
d1=d1。重复(1级重量)
d2=d2。重复(等级重量[2])
d3=d3。重复(第3级重量)
d4=d4.重复(等级重量[4])
dataset=d0.连接(d1).连接(d2).连接(d3).连接(d4)

dataset=dataset.shuffle(180000)#我建议使用发行版选择的标签。与基于样本拒绝的其他函数相比,此函数的优点是,在读取未使用的样本时不会丢失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\u num\u samples)
——当您开始混合不同长度的数据集时,对于历元的定义确实没有好的选择

但是,有一个具体的原因使其至少与最大的数据集一样大:正如您所注意到的,调用
迭代器.initializer
从头开始重新启动
数据集。因此,现在您的洗牌缓冲区比数据小得多(通常是这样),重要的是不要过早地重新启动,以确保培训可以看到所有数据

关于洗牌
这个答案解决了使用自定义权重交错数据集的问题,而不是数据集洗牌的问题,这是一个不相关的问题。洗牌一个大数据集需要做出妥协-你不能有一个有效的动态洗牌而不牺牲内存和性能。例如,该主题以图形方式说明了缓冲区大小对洗牌质量的影响。

感谢您的回答!但是,iterator.initializer将重置所有内容,批处理将始终从头开始,因此对于多数类(在本例中为类0),它将始终是要采样的前几个元素。如果在for循环中运行`sess.run(iterator.initializer,feed_dict={class_relprob_ph:[1,1,1,1]})print(batch.eval())`时,输出中的类0元素将始终是0-5,这意味着类0中的元素6-900从未被使用。如何解决这个问题?我觉得我必须回来洗牌()。虽然这次我只需要按类洗牌数据集(最多35000个,约为180000个数据集的1/5),但它仍然相当大,而且会很耗时。@user10253771我在回答中添加了关于洗牌的注释。谢谢!将洗牌可视化的博客有很大帮助。