Я пытаюсь выполнить семантическую сегментацию в API-интерфейсе Keras в TensorFlow 1.10 (с использованием Python) с помощью функции обобщенной потери кубиков :
def generalized_dice_loss(onehots_true, logits):
smooth = tf.constant(1e-17)
onehots_true, logits = mask(onehots_true, logits) # Not all of my pixels contain ground truth, and I filter those pixels out, which results in shape [num_gt_pixels, num_classes]-shaped labels and logits.
probabilities = tf.nn.softmax(logits)
weights = 1.0 / (tf.reduce_sum(onehots_true, axis=0)**2)
weights = tf.clip_by_value(weights, 1e-17, 1.0 - 1e-7) # Is this the correct way of dealing with inf values (the results of zero divisions)?
numerator = tf.reduce_sum(onehots_true * probabilities, axis=0)
numerator = tf.reduce_sum(weights * numerator)
denominator = tf.reduce_sum(onehots_true + probabilities, axis=0)
denominator = tf.reduce_sum(weights * denominator)
loss = 1.0 - 2.0 * (numerator + smooth) / (denominator + smooth)
return loss
Однако я изо всех сил пытаюсь получитьлюбая значимая потеря, которая не всегда 1. Что я здесь делаю не так?
После того, как начальные веса (по одному для каждого класса) вычислены, они содержат много inf
от нулевых делений, как обычнотолько небольшое подмножество всех классов присутствует в образце изображения.Поэтому я подрезаю веса к диапазону [1e-17, 1-1e-17] (это хорошая идея?), После чего они выглядят так:
tf.Tensor(
[4.89021e-05 2.21410e-10 5.43187e-11 1.00000e+00 1.00000e+00 4.23855e-07
5.87461e-09 3.13044e-09 2.95369e-07 1.00000e+00 1.00000e+00 2.22499e-05
1.00000e+00 1.73611e-03 9.47212e-10 1.12608e-05 2.77563e-09 1.00926e-08
7.74787e-10 1.00000e+00 1.34570e-07], shape=(21,), dtype=float32)
, что мне кажется хорошимхотя они довольно маленькие.Числители (tf.reduce_sum(onehots_true * probabilities, axis=0)
до их взвешивания) выглядят следующим образом:
tf.Tensor(
[3.42069e+01 0.00000e+00 9.43506e+03 7.88478e+01 1.50554e-02 0.00000e+00
1.22765e+01 4.36149e-01 1.75026e+02 0.00000e+00 2.33183e+02 1.81064e-01
0.00000e+00 1.60128e+02 1.48867e+04 0.00000e+00 3.87697e+00 4.49753e+02
5.87062e+01 0.00000e+00 0.00000e+00], shape=(21,), dtype=float32)
tf.Tensor(1.0, shape=(), dtype=float32)
, что также выглядит разумно, поскольку они в основном имеют соответствующие размеры меток, умноженные на уверенность сети относительно них (что, вероятно,низкий в начале обучения).Знаменатели (tf.reduce_sum(onehots_true + probabilities, axis=0)
, до взвешивания) также выглядят хорошо:
tf.Tensor(
[ 14053.483 25004.557 250343.36 66548.234 6653.863 3470.502
5318.3926 164206.19 19914.338 1951.0701 3559.3235 7248.4717
5984.786 7902.9004 133984.66 41497.473 25010.273 22232.062
26451.926 66250.39 6497.735 ], shape=(21,), dtype=float32)
Они большие, но этого следует ожидать, поскольку вероятности класса пикселя составляют сумму 1, а следовательно, и суммуэти знаменатели должны более или менее равняться количеству пикселей с истинностью.
Тем не менее, суммирование числителей дает очень небольшую сумму (~ 0,001, хотя иногда она находится в диапазоне из одной цифры), в то время как знаменатель суммирует оченьбольшие значения.Это приводит к тому, что моя последняя потеря - 1 или что-то очень похожее на это.Как я могу смягчить этот эффект и получить стабильные градиенты?Я в значительной степени реализовал точную формулу потери игральных костей.Что мне здесь не хватает?