Tensorflow 数据集:有没有办法只修改一定比例的标签?
Posted
技术标签:
【中文标题】Tensorflow 数据集:有没有办法只修改一定比例的标签?【英文标题】:Tensorflow Datasets: Is there a way to only modify a certain percentage of labels? 【发布时间】:2021-05-06 04:22:55 【问题描述】:我正在使用以下示例来分析计算机视觉系统根据数据质量的性能。
Keras 实现 Retinanet:https://keras.io/examples/vision/retinanet/
我的目标是破坏(拉伸、移动)所有图像中总边界框的某些百分比(10%、20%、30%)。这意味着应该随机选择图像,并且它们的一些边界框会损坏,因此总体目标百分比会受到影响。
我使用 tensorflow 数据集作为我的训练数据(例如https://www.tensorflow.org/datasets/catalog/kitti)。
我的基本想法是生成一个大小与盒子总数相同的数组,并用 1(修改盒子)和 0(忽略盒子)填充它,然后遍历所有盒子:
random_array = np.concatenate((np.ones(int(error_rate_size*TOTAL_NUMBER_OF_BOXES)+1,dtype=int),np.zeros(int((1-error_rate_size)*TOTAL_NUMBER_OF_BOXES)+1,dtype=int)))
问题是我正在使用的实现严重依赖图形实现,特别是映射函数 (https://www.tensorflow.org/api_docs/python/tf/data/Dataset#map)。我想遵循这种模式以保持实现的数据管道。
我希望做的是将 map 函数与全局计数器结合使用,这样我就可以遍历数组并在给定条件时进行修改。它应该大致如下所示:
COUNT = 0
def damage_data(box):
scaling_range = 2.0
global COUNT
COUNT += 1
if random_array[COUNT]== 1:
new_box = tf.stack(
[
box[0]*scaling_range*tf.random.uniform(shape=(),minval=0.0,maxval=1.0,dtype=tf.float32,seed=1), # x center
box[1]*scaling_range*tf.random.uniform(shape=(),minval=0.0,maxval=1.0,dtype=tf.float32,seed=2), # y center
box[2]*scaling_range*tf.random.uniform(shape=(),minval=0.0,maxval=1.0,dtype=tf.float32,seed=3), # width,
box[3]*scaling_range*tf.random.uniform(shape=(),minval=0.0,maxval=1.0,dtype=tf.float32,seed=4), # height,
],
axis=-1,)
else:
tf.print("Not Changed")
new_box = tf.stack(
[
box[0],
box[1], # y center
box[2], # width,
box[3], # height,
],
axis=-1,)
return new_box
def damage_data_cross_sequential(image, bbox, class_id):
# bbox format [x_center, y_center, width, height]
bbox = tf.map_fn(damage_data,bbox)
return image, bbox, class_id
train_dataset = train_dataset.map(damage_data_cross_sequential,num_parallel_calls=1)
但是使用此代码,变量 COUNT 不会全局递增,而是每个 map() 调用都从初始值 0 开始。我认为这是通过图形实现和 map() 中的并行过程引起的。
现在的问题是,是否有任何方法可以通过 map 函数全局增加计数器,或者我是否可以使用唯一标识符扩展给定的数据集(例如 add box[5] = id)。
我希望问题很清楚,并且已经感谢了! :)
-------------更新 1--------------------------------------------
@Lescurel 描述的第二种方法是我想要做的。
关于数据集结构的一些说明。
每张图片的盒子数量不一样。它会随着图片的变化而变化。
例如样本1:((x_dim,y_dim,3),(4,4)),样本2:((x_dim,y_dim,3),(2,4))
为了更好地理解结构,可以使用以下内容进行复制:
import tensorflow as tf
import tensorflow_datasets as tfds
import numpy as np
valid_ds = tfds.load('kitti', split='validation') # validation is a smaller set
def select_relevant_info(sample):
image = sample["image"]
bbox = sample["objects"]["bbox"]
class_id = tf.cast(sample["objects"]["type"], dtype=tf.int32)
return image, bbox, class_id
valid_ds = valid_ds.map(select_relevant_info)
for sample in valid_ds.take(1):
print(sample)
【问题讨论】:
【参考方案1】:出于多种原因,使用global state is not a terribly good idea,但在像这样的并发上下文中可能会更糟。
至少有两种其他方法可以实现您想要的:
使用带有阈值的随机样本作为修改标签的条件 将你的随机数组放入数据集中作为修改标签的条件。我个人更喜欢第一种,比较简单。
一个例子。
让我们生成一些随机数据,并创建一个tf.Dataset
。在该示例中,样本总数为1000
:
imgs = tf.random.uniform((1000, 4, 4))
boxes = tf.ones((1000, 4))
ds = tf.data.Dataset.from_tensor_slices((imgs, boxes))
第一个选项:随机样本
此函数将在 0 和 1 之间均匀绘制一个数字。如果此数字高于阈值prob
,则不会发生任何事情。否则,我们修改标签。在该示例中,修改标签的几率为 0.05%。
def change_label_with_prob(label, prob=0.05, scaling_range=2.):
return tf.cond(
tf.random.uniform(()) > prob,
lambda: label,
lambda: label*scaling_range*tf.random.uniform((4,), 0., 1., dtype=tf.float32),
)
你可以简单地用Dataset.map
调用它:
new_ds = ds.map(lambda img, box: (img, change_label_with_prob(box)))
第二个选项:传递条件数组
首先,我们生成一个填充了我们的条件的数组:1
如果我们要修改数组,则为 0。
# lets set the number to change to 200
N_TO_CHANGE = 200
# randomly generated array with 200 "1" and "800" 0.
cond_array = tf.random.shuffle(
tf.concat([tf.ones((N_TO_CHANGE,),dtype=tf.bool), tf.zeros((1000 - N_TO_CHANGE,),dtype=tf.bool)], axis=0)
)
然后我们可以根据该条件数组创建一个数据集,并将其与我们之前的数据集一起压缩:
# creating a dataset from the conditional array
ds_cond = tf.data.Dataset.from_tensor_slices(cond_array)
# zipping the two datasets together
ds_data_and_cond = tf.data.Dataset.zip((ds, ds_cond))
# each element of that dataset is ((img, box), cond)
我们可以编写我们的函数,大致和之前一样:
def change_label_with_cond(label, cond, scaling_range=2.0):
# if true, modifies, do nothing otherwise
return tf.cond(
cond,
lambda: label
* scaling_range
* tf.random.uniform((4,), 0.0, 1.0, dtype=tf.float32),
lambda: label,
)
然后将函数映射到我们的新数据集上,注意数据集每个元素的嵌套形状:
ds_changed_label = ds_data_and_cond.map(
lambda img_and_box, z: (img_and_box[0], change_label_with_cond(img_and_box[1], z))
)
# New dataset has a shape (img, box), same as before the zipping
【讨论】:
首先感谢您的帮助。我使用了您已经提到的第一种方法。这里的问题是改变多少盒子的数量不是固定的,在我的情况下,定义确切的数字很重要。 第二种方法是我希望做的。然而,它有点复杂,因为每个图像的框数不一样。在我使用的数据中,它更像是以下内容:'sample = ((Xdim,Ydim, 3),(NoBoxInImage,4)) 我想以描述的方式压缩,但与嵌套结构斗争。 填充是一个选项吗?它会让你的生活更轻松。但我想,如果您在压缩方面遇到困难,最好问另一个问题,而不是复杂化这个问题。 我想像您描述的那样将它压缩在一起,但问题是它不是 1:1 匹配的。有时图像具有例如7 个盒子有时只有 1 个。这就是为什么我希望有一个计数器来跟踪每个地图函数操作并帮助我遍历“随机条件数组”我在原始帖子中添加了一些额外的信息来澄清数据结构。以上是关于Tensorflow 数据集:有没有办法只修改一定比例的标签?的主要内容,如果未能解决你的问题,请参考以下文章
机器学习在用到mnist数据集报错No module named 'tensorflow.examples.tutorials'解决办法