我正在ubuntu 16.04上运行神经网络,使用1个GPU(GTX 1070)和4个CPU。我的数据集包含约35,000张图片,但数据集不平衡:类别0占90%,而类别1、2、3、4共占另外10%。因此,我通过使用
重新采样策略如下:
1)在最开始时,
2)随着训练的进行,epoch数量递增,权重会根据epoch数量递减,使分布更接近实际分布。
由于我的
以下是我的部分代码。
为了清楚起见,让我逐步描述为什么我会遇到这个问题:
1. 因为我的数据集中的类别不平衡,我想对那些少数类进行过采样。 2. 因为有了上述原因,我想对那些类别应用随机增强技术,并将多数类(类别 0)与它们连接起来。 3. 经过一些调研,我发现如果其中包含一个随机函数,则 repeat() 生成的结果会有所不同,因此我使用 repeat() 以及 my_random_augmentation_func 来实现上述目标。 4. 现在,完成了第 2 步,我想将所有数据集合并,因此我使用 concatenate()。 5. 在完成第 4 步之后,我遇到了一个问题:总共大约有 40,000 至 180,000 张图片(因为 class_weight 每个 epoch 都会变化,因此开始时总共有 180,000 张图像,最终大约有 40,000 张),它们按类别串联起来,数据集的形式将会是 [0000-1111-2222-3333-4444],因此每批次的大小为 100,没有任何混洗,几乎每个批次只包含一个类别,这意味着每个批次内的分布是不平衡的。 6. 为了解决第 5 步中的“不平衡批次”问题,我想到一个思路:对整个数据集进行混洗,因此我使用 shuffle(180000)。 7. 最后,我的电脑在对这个数据集的 180,000 项进行混洗时卡住了。
那么,有更好的方法可以获得平衡的批次,但仍然保持我想要的特性(例如按 epoch 更改分布)吗?
--- 编辑:问题已解决 ---
事实证明,我不应该一开始就应用 map 函数。我应该只取文件名而不是真实文件,并仅对文件名进行混洗,然后将其映射到真实文件中。
更详细地说,在 d0 = tf.data.TextLineDataset(train_csv_0) 和其他 4 行之后删除 map(_parse_csv_train) 部分,再添加一行 dataset = dataset.map(_parse_csv_train) 在 shuffle(180000) 之后。
我还要感谢 @P-Gn,他在“混洗”部分中提供的博客链接非常有帮助。它回答了我心中的一个问题,但我没有问出来:“我可以通过多次小混洗或一次大混洗来获得类似的随机性吗?”(我不会在这里给出答案,请查看那篇博客!)该博客中的方法也可能是解决此问题的潜在解决方案,但我还没有尝试过。
dataset.repeat(class_weight)
进行过采样,以及使用随机增强函数,然后将它们合并来过采样类别1-4。重新采样策略如下:
1)在最开始时,
class_weight[n]
将设置为一个较大的数字,以使每个类别拥有与类别0相同数量的图像。2)随着训练的进行,epoch数量递增,权重会根据epoch数量递减,使分布更接近实际分布。
由于我的
class_weight
会随着epoch而变化,因此无法在一开始对整个数据集进行洗牌。相反,我必须逐个类别地取出数据,并在连接每个类别的过采样数据之后对整个数据集进行洗牌。为了实现平衡的批处理,我必须对整个数据集进行逐元素的洗牌。以下是我的部分代码。
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. 因为我的数据集中的类别不平衡,我想对那些少数类进行过采样。 2. 因为有了上述原因,我想对那些类别应用随机增强技术,并将多数类(类别 0)与它们连接起来。 3. 经过一些调研,我发现如果其中包含一个随机函数,则 repeat() 生成的结果会有所不同,因此我使用 repeat() 以及 my_random_augmentation_func 来实现上述目标。 4. 现在,完成了第 2 步,我想将所有数据集合并,因此我使用 concatenate()。 5. 在完成第 4 步之后,我遇到了一个问题:总共大约有 40,000 至 180,000 张图片(因为 class_weight 每个 epoch 都会变化,因此开始时总共有 180,000 张图像,最终大约有 40,000 张),它们按类别串联起来,数据集的形式将会是 [0000-1111-2222-3333-4444],因此每批次的大小为 100,没有任何混洗,几乎每个批次只包含一个类别,这意味着每个批次内的分布是不平衡的。 6. 为了解决第 5 步中的“不平衡批次”问题,我想到一个思路:对整个数据集进行混洗,因此我使用 shuffle(180000)。 7. 最后,我的电脑在对这个数据集的 180,000 项进行混洗时卡住了。
那么,有更好的方法可以获得平衡的批次,但仍然保持我想要的特性(例如按 epoch 更改分布)吗?
--- 编辑:问题已解决 ---
事实证明,我不应该一开始就应用 map 函数。我应该只取文件名而不是真实文件,并仅对文件名进行混洗,然后将其映射到真实文件中。
更详细地说,在 d0 = tf.data.TextLineDataset(train_csv_0) 和其他 4 行之后删除 map(_parse_csv_train) 部分,再添加一行 dataset = dataset.map(_parse_csv_train) 在 shuffle(180000) 之后。
我还要感谢 @P-Gn,他在“混洗”部分中提供的博客链接非常有帮助。它回答了我心中的一个问题,但我没有问出来:“我可以通过多次小混洗或一次大混洗来获得类似的随机性吗?”(我不会在这里给出答案,请查看那篇博客!)该博客中的方法也可能是解决此问题的潜在解决方案,但我还没有尝试过。
sess.run(iterator.initializer, feed_dict={class_relprob_ph: [1, 1, 1, 1, 1]}) print(batch.eval())
,则输出中的类0元素将始终为0-5,这意味着类0中的元素6-900永远不会被使用。 - user10253771