多类别细分的广义骰子损失:keras实现

Man*_*anu 6 machine-learning deep-learning keras loss-function semantic-segmentation

我刚刚在keras中实现了广义骰子损失(骰子损失的多类版本),如ref中所述:

(我的目标定义为:(batch_size,image_dim1,image_dim2,image_dim3,nb_of_classes))

def generalized_dice_loss_w(y_true, y_pred): 
    # Compute weights: "the contribution of each label is corrected by the inverse of its volume"
    Ncl = y_pred.shape[-1]
    w = np.zeros((Ncl,))
    for l in range(0,Ncl): w[l] = np.sum( np.asarray(y_true[:,:,:,:,l]==1,np.int8) )
    w = 1/(w**2+0.00001)

    # Compute gen dice coef:
    numerator = y_true*y_pred
    numerator = w*K.sum(numerator,(0,1,2,3))
    numerator = K.sum(numerator)

    denominator = y_true+y_pred
    denominator = w*K.sum(denominator,(0,1,2,3))
    denominator = K.sum(denominator)

    gen_dice_coef = numerator/denominator

    return 1-2*gen_dice_coef
Run Code Online (Sandbox Code Playgroud)

但是一定有问题。我正在处理3D图像,必须将其细分为4类(1个背景类和3个对象类,我有一个不平衡的数据集)。首先奇怪的事情:当我的火车损失和准确性训练中提高(和收敛非常快),我确认损失/精度是恒定的低谷时期(见图片)。其次,在对测试数据进行预测时,仅预测了背景类:我得到了恒定的体积。

我使用了完全相同的数据和脚本,但存在分类交叉熵损失,并得到了合理的结果(对象类别已细分)。这意味着我的实现存在问题。知道会是什么吗?

另外,我认为具有通用的骰子丢失实现对keras社区很有用,因为它似乎已用于大多数最近的语义分割任务中(至少在医学图像社区中)。

PS:对我来说,如何定义权重似乎很奇怪;我得到大约10 ^ -10的值。还有其他人尝试实现吗?我也测试了我的功能,但没有权重,但是遇到了同样的问题。

Ben*_*Ben 6

我认为这里的问题是你的体重。想象一下,您正在尝试解决多类分割问题,但在每张图像中只存在几个类。这方面的一个玩具示例(以及导致我解决此问题的示例)是通过以下方式从 mnist 创建分段数据集。

x = 28x28 图像和 y = 28x28x11 其中如果每个像素低于 0.4 的归一化灰度值,则将其归类为背景,否则归类为数字,即 x 的原始类。因此,如果您看到排名第一的图片,则会将一堆像素归类为一个,以及背景。

现在在这个数据集中,图像中只会有两个类。这意味着,在你丢掉骰子之后,将有 9 个权重 1./(0. + eps) = large ,因此对于每张图像,我们都会强烈惩罚所有 9 个不存在的类。在这种情况下,网络想要找到的一个明显强大的局部最小值是将所有内容预测为背景类。

我们确实希望惩罚任何不在图像中但不那么强烈的错误预测类。所以我们只需要修改权重。我是这样做的:

def gen_dice(y_true, y_pred, eps=1e-6):
    """both tensors are [b, h, w, classes] and y_pred is in logit form"""

    # [b, h, w, classes]
    pred_tensor = tf.nn.softmax(y_pred)
    y_true_shape = tf.shape(y_true)

    # [b, h*w, classes]
    y_true = tf.reshape(y_true, [-1, y_true_shape[1]*y_true_shape[2], y_true_shape[3]])
    y_pred = tf.reshape(pred_tensor, [-1, y_true_shape[1]*y_true_shape[2], y_true_shape[3]])

    # [b, classes]
    # count how many of each class are present in 
    # each image, if there are zero, then assign
    # them a fixed weight of eps
    counts = tf.reduce_sum(y_true, axis=1)
    weights = 1. / (counts ** 2)
    weights = tf.where(tf.math.is_finite(weights), weights, eps)

    multed = tf.reduce_sum(y_true * y_pred, axis=1)
    summed = tf.reduce_sum(y_true + y_pred, axis=1)

    # [b]
    numerators = tf.reduce_sum(weights*multed, axis=-1)
    denom = tf.reduce_sum(weights*summed, axis=-1)
    dices = 1. - 2. * numerators / denom
    dices = tf.where(tf.math.is_finite(dices), dices, tf.zeros_like(dices))
    return tf.reduce_mean(dices)
Run Code Online (Sandbox Code Playgroud)