Tensorflow:如何在语义分割过程中忽略特定的标签



我使用tensorflow进行语义分割。在计算逐像素损失时,我如何告诉tensorflow忽略特定标签?

我在这篇文章中读到,对于图像分类,可以将标签设置为-1,它将被忽略。如果这是真的,给定标签张量,我如何修改我的标签,使某些值更改为-1

在Matlab中,它将类似于:

ignore_label = 255
myLabelTensor(myLabelTensor == ignore_label) = -1

但我不知道如何在TF中做到这一点?

一些背景信息:
这就是标签的加载方式:

label_contents = tf.read_file(input_queue[1])
label = tf.image.decode_png(label_contents, channels=1)

这就是目前损失的计算方式:

raw_output = net.layers['fc1_voc12']
prediction = tf.reshape(raw_output, [-1, n_classes])
label_proc = prepare_label(label_batch, tf.pack(raw_output.get_shape()[1:3]),n_classes)
gt = tf.reshape(label_proc, [-1, n_classes])
# Pixel-wise softmax loss.
loss = tf.nn.softmax_cross_entropy_with_logits(prediction, gt)
reduced_loss = tf.reduce_mean(loss)

带有

def prepare_label(input_batch, new_size, n_classes):
"""Resize masks and perform one-hot encoding.
Args:
input_batch: input tensor of shape [batch_size H W 1].
new_size: a tensor with new height and width.
Returns:
Outputs a tensor of shape [batch_size h w 21]
with last dimension comprised of 0's and 1's only.
"""
with tf.name_scope('label_encode'):
input_batch = tf.image.resize_nearest_neighbor(input_batch, new_size) # as labels are integer numbers, need to use NN interp.
input_batch = tf.squeeze(input_batch, squeeze_dims=[3]) # reducing the channel dimension.
input_batch = tf.one_hot(input_batch, depth=n_classes)
return input_batch

我使用的是tensorflow deeplab resnet模型,它将在Caffe中实现的resnet模型转换为使用Caffe tensorflow的tensorflow。

根据文档,必须在labels上使用有效的概率分布来调用tf.nn.softmax_cross_entropy_with_logits,否则计算将不正确,并且使用带有负标签的tf.nn.sparse_softmax_cross_entropy_with_logits(在您的情况下可能更方便)将导致错误或返回NaN值。我不会指望它会忽略一些标签。

我要做的是在那些像素中用无穷大替换被忽略类的logits,其中正确的类是被忽略的类,所以它们对损失没有任何贡献:

ignore_label = ...
# Make zeros everywhere except for the ignored label
input_batch_ignored = tf.concat(input_batch.ndims - 1,
[tf.zeros_like(input_batch[:, :, :, :ignore_label]),
tf.expand_dims(input_batch[:, :, :, ignore_label], -1),
tf.zeros_like(input_batch[:, :, :, ignore_label + 1:])])
# Make corresponding logits "infinity" (a big enough number)
predictions_fix = tf.select(input_batch_ignored > 0,
1e30 * tf.ones_like(predictions), predictions)
# Compute loss with fixed logits
loss = tf.nn.softmax_cross_entropy_with_logits(prediction, gt)

唯一的问题是,您考虑到被忽略类的像素总是被正确预测的,这意味着包含大量这些像素的图像的损失将被人为地更小。根据情况,这可能很重要,也可能不重要,但如果你想真正准确,你必须根据不被忽略的像素数量来加权每个图像的损失,而不仅仅是取平均值。

# Count relevant pixels on each image
input_batch_relevant = 1 - input_batch_ignored
input_batch_weight = tf.reduce_sum(input_batch_relevant, [1, 2, 3])
# Compute relative weights
input_batch_weight = input_batch_weight / tf.reduce_sum(input_batch_weight)
# Compute reduced loss according to weights
reduced_loss = tf.reduce_sum(loss * input_batch_weight)

其中一个解决方案是编写一个自定义损失函数,忽略对所选标签的预测。

我无法提供语义分割的确切代码,但这就是我在处理一个简单的命名实体识别问题时解决的问题,在计算模型训练期间的损失时,我希望忽略对特定令牌(如[PAD][SEP]等)的预测。

tokens = ['My', 'name', 'is', 'Robert', '[PAD]']
classes = tf.constant([[0, 0, 0, 1, -1]])
predictions = tf.constant([[-0.5, -0.6, -0.9, 1.2, 0.1]])

这是一个二进制分类,因此可以使用BinaryCrossentropy损失:

tf.keras.losses.BinaryCrossentropy(from_logits=True)(classes, predictions)
>>> <tf.Tensor: shape=(), dtype=float32, numpy=0.47207958>

然而,仅仅使用TensorFlow的BinaryCrossentropy不会忽略对标签为-1的元素的预测。要忽略它们,我们可以使用自定义丢失功能:

def custom_loss(batch_labels, batch_predictions):
loss = tf.constant(0, dtype=tf.float32)
binary_crossentropy = tf.keras.losses.BinaryCrossentropy(from_logits=True)

batch_size = tf.shape(batch_labels)[0]

for i in range(batch_size):
labels = batch_labels[i, :]
predictions = batch_predictions[i, :]

ix_to_eval = labels != -1

predictions = predictions[ix_to_eval]
labels = labels[ix_to_eval]
element_loss = binary_crossentropy(labels, predictions)
loss = loss + element_loss

return loss/tf.cast(batch_size, tf.float32)

自定义丢失将一批数据点的预测和标签作为输入。在这里,我对批进行迭代,并仅针对带有与-1不同标签的句子中的标记计算BinaryCrossentropy

注意区别:

custom_loss(classes, predictions)
>>> <tf.Tensor: shape=(), dtype=float32, numpy=0.3790003>

自定义损失可以像任何其他内置损失函数一样在训练循环中使用,例如:

model.compile(
optimizer=optimizer,
loss=custom_loss)

对不起,我是新手,但我相信https://github.com/tensorflow/models/blob/master/research/deeplab/g3doc/faq.md,这里提到需要添加新的数据集。在"segmentation_dataset.py"中,在每个数据集中,都可以指定ignore_label。例如,

_PASCAL_VOC_SEG_INFORMATION = DatasetDescriptor(
splits_to_sizes={
'train': 1464,
'trainval': 2913,
'val': 1449,
},
num_classes=21,
ignore_label=255,
)

最新更新