我正在使用1个GPU(GTX 1070)和4个CPU的ubuntu 16.04运行我的神经网络。
我的数据集包含大约35,000张图像,但数据集不平衡:0类占90%,而1,2,3,4类则占另外10%。因此,我使用dataset.repeat(class_weight)
[我也使用一个函数来应用随机增强],然后concatenate
对它们进行了1-4级过采样。
重新采样策略是:
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(_parse_csv_train)
和其他4行之后的d0 = tf.data.TextLineDataset(train_csv_0)
部分,并在shuffle(180000)之后添加一个新行dataset = dataset.map(_parse_csv_train)
。。 >
我还要对@ P-Gn表示感谢,他的“改组”部分中的博客链接确实很有帮助。它回答了我脑海中的一个问题,但我没有问:“我可以通过使用许多小混洗与一个大混洗来获得类似的随机性吗?” (我不会在这里给出答案,请检查该博客!)该博客中的方法也可能是此问题的潜在解决方案,但我还没有尝试过。
答案 0 :(得分:1)
我建议使用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,以图形方式说明了缓冲区大小对混洗质量的影响。