Проблема с написанием пользовательской функции потерь без массивов numpy с использованием tensorflow 2.0 для сегментации изображений

#python #tensorflow #conv-neural-network #image-segmentation #loss-function

Вопрос:

Я пытаюсь изменить рабочую реализацию функции потерь Тверского, которая определяется как:

Тверская потеря

где TP: Истинно положительный, FP: Ложноположительный, FN: ложноотрицательный, 0<дельта

У меня есть метки истинности (64×64), которые разрежены и имеют несколько масок (равных одной) размером (3×3), как показано в этом примере:

ярлык основной истины

Моя цель состоит в том, чтобы автоматически установить нулевое значение прогнозируемых пикселей, близких к области, в которой метка основной истины равна единице. В данном примере это означало бы принудительное значение зеленых пикселей, равных нулю в прогнозируемой метке.

метка истинности основания с контуром

Здесь у меня есть реализация, которая использует массивы numpy, что, как я знаю, невозможно для функции потерь (сделано только для одного класса).:

 import tensorflow.keras.backend as K
import tensorflow as tf
import numpy as np
 
def identify_axis(shape):
    # Three dimensional
    if len(shape) == 5 : return [1,2,3]
    # Two dimensional
    elif len(shape) == 4 : return [1,2]
    # Exception - Unknown
    else : raise ValueError('Metric: Shape of tensor is neither 2D or 3D.')

def modified_tversky_loss(y_true, y_pred):
    """
    Paper: Tversky loss function for image segmentation using 3D fully convolutional deep networks
    Link: https://arxiv.org/abs/1706.05721
    delta: controls weight given to false positive and false negatives. 
    this equates to the Tversky index when delta = 0.7
    smooth: smoothing constant to prevent division by zero errors
    """
    delta = 0.9
    smooth = 0.00001

    axis = identify_axis(y_true.get_shape())
    
    #### Part of the code that needs to be changed ####

    y_true_copy = y_true.numpy()
    y_pred_copy = y_pred.numpy()

    for batch in range(np.shape(y_true_copy)[0]):

        #### This part is used to get the bottom left coordinates of ground truth masks ####

        is_not_all_zero = np.any(y_true_copy[batch,:,:,0])
        pos_list = [] # list containing bottom left coordinates of ground truth masks
        while is_not_all_zero:
            pos = np.unravel_index(y_true_copy[batch,:,:,0].argmax(), y_true_copy.shape)
            y_true_copy[batch, pos[1]:pos[1] 3, pos[2]:pos[2] 3, 0] = 0
            pos_list.append(pos)
            is_not_all_zero = np.any(y_true_copy[batch,:,:,0])

        #### Using the bottom left coordinates of ground truth masks, we set to zero neighbour pixels in the predicted label ####

        for k in range(len(pos_list)):
            y_pred_copy[batch, pos_list[k][1]-3:pos_list[k][1], pos_list[k][2]-3:pos_list[k][2] 6, 0] = 0
            y_pred_copy[batch, pos_list[k][1]:pos_list[k][1] 3, pos_list[k][2]-3:pos_list[k][2], 0] = 0
            y_pred_copy[batch, pos_list[k][1]:pos_list[k][1] 3, pos_list[k][2] 3:pos_list[k][2] 6, 0] = 0
            y_pred_copy[batch, pos_list[k][1] 3:pos_list[k][1] 6, pos_list[k][2]-3:pos_list[k][2] 6, 0] = 0
   
    y_pred = tf.convert_to_tensor(y_pred_copy, dtype=tf.float32)

    #### END ####

    # Calculate true positives (tp), false negatives (fn) and false positives (fp)  

    tp = K.sum(y_true * y_pred, axis=axis)
    fn = K.sum(y_true * (1-y_pred), axis=axis)
    fp = K.sum((1-y_true) * y_pred, axis=axis)
    tversky_class = (tp   smooth)/(tp   delta*fn   (1-delta)*fp   smooth)
    # Sum up classes to one score
    tversky_loss = K.sum(1-tversky_class, axis=[-1])
    # adjusts loss to account for number of classes
    num_classes = K.cast(K.shape(y_true)[-1],'float32')
    tversky_loss = tversky_loss / num_classes

    return tversky_loss
 

Эта реализация была запущена с использованием

 model.compile(loss=modified_tversky_loss, optimizer=optimizer, metrics=metrics, run_eagerly=True)
 

и возвращает следующую ошибку

Ошибка значения: градиенты не предусмотрены ни для одной переменной:

что, я полагаю, происходит из-за некоторой проблемы, связанной с недифференцируемыми операциями.

Я пытался написать эту функцию, используя только функции tensorflow, но среди всех моих проблем самая большая проблема, которую я нахожу, заключается в том, что невозможно присвоить значения тензору tf аналогично массивам numpy.

Есть ли способ написать эту функцию, используя только тензоры ? Спасибо

Комментарии:

1. Вы пробовали переместить функцию identify_axis внутрь функции потерь?

2. У меня нет, но функция работает правильно с identity_axis снаружи, когда я удаляю изменения, внесенные в исходную функцию потери Тверского.