Я использую определенные ниже пользовательские функции потерь (dice_loss
и pixelwise_weighted_binary_crossentropy
) для обучения U- Net двухклассной сегментации с помощью Keras.
Я думал, что понял, что они это делают, и значения, которые они возвращают - более конкретно, я ожидал, что они вернут массив потерь, как определено здесь .
Но теперь я увидел, что качество моих моделей существенно зависит от размер партии, где модель, обученная с большим размером партии, намного хуже в отношении производительности для каждой выборки (Примечание: здесь я рисую dice_metric
; функция dice_loss
, определенная ниже, равна 1 - dice_metric
):
введите описание изображения здесь
Кроме того, оценка плохой модели слева с исходным размером пакета, с которым она была обучена (batch_size=180
), дает мне Dice-metri c ~ 0,96, но с усреднением на выборку Показатели игры в кости (нанесенные на график) дают мне ~ 0,76.
Это наводит на мысль, что большой размер партии может «скрывать» образцы, которые недостаточно представлены в обучающем наборе. Итак, мой вопрос:
Это нормальное поведение при обучении модели?
Или: это проблема с моими функциями потерь, где они, возможно, бросают все пиксельные значения партии в одна потеря, и это скрывает недостаточно представленные наборы данных?
Я использую следующие функции потерь:
'''This is the Dice-loss as found here: https://stackoverflow.com/questions/51973856/how-is-the-smooth-dice-loss-differentiable'''
def dice_loss(y_true, y_pred):
[masks, weights] = tf.unstack(y_true, 2, axis=-1)
y_true = masks
smooth = 1
y_true_f = K.flatten(y_true)
y_pred_f = K.flatten(y_pred)
intersection = K.sum(y_true_f * y_pred_f)
return 1 - (2. * intersection + smooth) / (K.sum(y_true_f) + K.sum(y_pred_f) + smooth)
def pixelwise_weighted_binary_crossentropy(y_true, y_pred):
'''
This function implements pixel-wise weighted, binary cross-entropy
(Important to detect the small borders between cells for segmentation)
The code is adapted from the Keras TF backend.
(see their github)
'''
try:
# The weights are passed as part of the y_true tensor:
[seg, weight] = tf.unstack(y_true, 2, axis=-1) # unpack segmentation GT and weights
seg = tf.expand_dims(seg, -1) # add dimension at end
weight = tf.expand_dims(weight, -1) # add dimension at end
except:
pass
epsilon = tf.convert_to_tensor(K.epsilon(), y_pred.dtype.base_dtype) # create epsilon tensor
y_pred = tf.clip_by_value(y_pred, epsilon, 1. - epsilon) # clip values to range [epsilon, 1-epsilon]
y_pred = math_ops.log(y_pred / (1 - y_pred)) # calculate Logits
zeros = array_ops.zeros_like(y_pred, dtype=y_pred.dtype)
cond = (y_pred >= zeros) # setup condition for ReLU non-linearity
relu_logits = math_ops.select(cond, y_pred, zeros) # set y_pred for True, zero for False
neg_abs_logits = math_ops.select(cond, -y_pred, y_pred) # get negative absolute value
entropy = math_ops.add(relu_logits - y_pred * seg, math_ops.log1p(math_ops.exp(neg_abs_logits)), name=None) # tf.math.log1p(x) := ln(1+x) element-wise
return K.mean(math_ops.multiply(weight, entropy), axis=-1) # apply weights and average to obtain final over the entropy