Tensorflow 数据集 API 中的过采样功能

Posted

技术标签:

【中文标题】Tensorflow 数据集 API 中的过采样功能【英文标题】:Oversampling functionality in Tensorflow dataset API 【发布时间】:2018-04-24 11:46:19 【问题描述】:

我想问一下当前的数据集API是否允许实现过采样算法?我处理高度不平衡的班级问题。我在想,在数据集解析(即在线生成)期间对特定类进行过度采样会很好。我已经看到了rejection_resample 函数的实现,但是这会删除样本而不是复制它们,并且它会减慢批量生成(当目标分布与初始分布有很大不同时)。我想要实现的是:举个例子,看看它的类概率决定是否重复它。然后调用dataset.shuffle(...)dataset.batch(...) 并获取迭代器。最好的(在我看来)方法是对低概率类进行过采样,并对最可能的类进行二次抽样。我想在网上做,因为它更灵活。

【问题讨论】:

你应该接受你的答案:) 【参考方案1】:

此问题已在问题 #14451 中解决。 只需在此处发布 anwser 以使其对其他开发人员更可见。

示例代码对低频率类进行过采样,对高频率类进行欠采样,其中class_target_prob 在我的情况下只是均匀分布。我想从最近的手稿中检查一些结论A systematic study of the class imbalance problem in convolutional neural networks

特定类的过采样是通过调用来完成的:

dataset = dataset.flat_map(
    lambda x: tf.data.Dataset.from_tensors(x).repeat(oversample_classes(x))
)

这里是完整的 sn-p,它可以做所有的事情:

# sampling parameters
oversampling_coef = 0.9  # if equal to 0 then oversample_classes() always returns 1
undersampling_coef = 0.5  # if equal to 0 then undersampling_filter() always returns True

def oversample_classes(example):
    """
    Returns the number of copies of given example
    """
    class_prob = example['class_prob']
    class_target_prob = example['class_target_prob']
    prob_ratio = tf.cast(class_target_prob/class_prob, dtype=tf.float32)
    # soften ratio is oversampling_coef==0 we recover original distribution
    prob_ratio = prob_ratio ** oversampling_coef 
    # for classes with probability higher than class_target_prob we
    # want to return 1
    prob_ratio = tf.maximum(prob_ratio, 1) 
    # for low probability classes this number will be very large
    repeat_count = tf.floor(prob_ratio)
    # prob_ratio can be e.g 1.9 which means that there is still 90%
    # of change that we should return 2 instead of 1
    repeat_residual = prob_ratio - repeat_count # a number between 0-1
    residual_acceptance = tf.less_equal(
                        tf.random_uniform([], dtype=tf.float32), repeat_residual
    )

    residual_acceptance = tf.cast(residual_acceptance, tf.int64)
    repeat_count = tf.cast(repeat_count, dtype=tf.int64)

    return repeat_count + residual_acceptance


def undersampling_filter(example):
    """
    Computes if given example is rejected or not.
    """
    class_prob = example['class_prob']
    class_target_prob = example['class_target_prob']
    prob_ratio = tf.cast(class_target_prob/class_prob, dtype=tf.float32)
    prob_ratio = prob_ratio ** undersampling_coef
    prob_ratio = tf.minimum(prob_ratio, 1.0)

    acceptance = tf.less_equal(tf.random_uniform([], dtype=tf.float32), prob_ratio)

    return acceptance


dataset = dataset.flat_map(
    lambda x: tf.data.Dataset.from_tensors(x).repeat(oversample_classes(x))
)

dataset = dataset.filter(undersampling_filter)

dataset = dataset.repeat(-1)
dataset = dataset.shuffle(2048)
dataset = dataset.batch(32)

sess.run(tf.global_variables_initializer())

iterator = dataset.make_one_shot_iterator()
next_element = iterator.get_next()

更新 #1

这是一个简单的jupyter notebook,它在玩具模型上实现了上述过采样/欠采样。

【讨论】:

你能举个例子,看看dataset.flat_map( lambda x: tf.data.Dataset.from_tensors(x).repeat(oversample_classes(x)) )dataset的内容应该是什么样子的? 看看我添加的原始帖子中的更新。 谢谢。笔记本真的很有帮助。 谢谢!这种实现在速度上是否与从过采样数据构建 tfrecord 并将其提供给 Dataset API 的速度相当? 问题是数据集通常是从不包含“class_pro”和“class_target_pro”属性的tf_record文件中读取的。如果不需要写新的 tfrecord 文件会更有用,否则为什么不在写新的 tfrecord 文件时过采样?【参考方案2】:

tf.data.experimental.rejection_resample 似乎是更好的方法,因为它不需要“class_prob”和“class_target_prob”功能。 虽然它是欠采样而不是过采样,但在目标分布和训练步骤相同的情况下,它的工作原理是一样的。

【讨论】:

【参考方案3】:

这个 QnA 对我很有帮助。所以我用我的相关经验写了一篇关于它的博客文章。

https://vallum.github.io/Optimizing_parallel_performance_of_resampling_with_tensorflow.html

我希望对使用重采样的 Tensorflow 输入管道优化感兴趣的人可以从中获得一些想法。

有些操作可能是不必要的冗余,但在我个人的情况下并不会影响性能。

 dataset = dataset.map(undersample_filter_fn, num_parallel_calls=num_parallel_calls) 
 dataset = dataset.flat_map(lambda x : x) 

带有标识 lambda 函数的 flat_map 仅用于合并幸存(和空)的记录

# Pseudo-code for understanding of flat_map after maps
#parallel calls of map('A'), map('B'), and map('C')
map('A') = 'AAAAA' # replication of A 5 times
map('B') = ''      # B is dropped
map('C') = 'CC'    # replication of C twice
# merging all map results
flat_map('AAAA,,CC') = 'AAAACC'

【讨论】:

以上是关于Tensorflow 数据集 API 中的过采样功能的主要内容,如果未能解决你的问题,请参考以下文章

Pyspark 中的过采样或 SMOTE

分类问题中的过采样和欠采样

如何解决样本不均衡问题

TensorFlow 中的计划采样

在AD和DA转换中的过采样和噪声形成

tensorflow 的数据集 API 返回的大小不是恒定的