tensorlayer / TensorLayer

Deep Learning and Reinforcement Learning Library for Scientists and Engineers
http://tensorlayerx.com
Other
7.31k stars 1.61k forks source link

Question about the implementation of the 'Jaccard' Dice coefficient #1134

Closed dljve closed 2 years ago

dljve commented 2 years ago

New Issue Checklist

Issue Description

I am wondering about the implementation of the 'Jaccard' Dice coefficient in the tensorlayer.cost module.

Does the loss_type jaccard refer here to the Jaccard index. Because the Jaccard index should be calculated as (X∩Y)/(X∪Y). In the code below it uses the same numerator of the Sorensen Dice, but it squares both the output and the target in the denominator:

l = tf.reduce_sum(output * output, axis=axis)
r = tf.reduce_sum(target * target, axis=axis)

Shouldn't the denomiator for the Jaccard Index be:

tf.reduce_sum(output, axis=axis) + tf.reduce_sum(target, axis=axis) - tf.reduce_sum(output * target, axis=axis) or |X|+|Y|-X∩Y.

And the nominator should not have the factor 2.

I am confused what metric the option jaccard is actually computing here. It gives me a different output from the regular Jaccard Index. Is there another name this metric is known by?

Reproducible Code

def dice_coe(output, target, loss_type='jaccard', axis=(1, 2, 3), smooth=1e-5):
    """Soft dice (Sørensen or Jaccard) coefficient for comparing the similarity
    of two batch of data, usually be used for binary image segmentation
    i.e. labels are binary. The coefficient between 0 to 1, 1 means totally match.

    Parameters
    -----------
    output : Tensor
        A distribution with shape: [batch_size, ....], (any dimensions).
    target : Tensor
        The target distribution, format the same with `output`.
    loss_type : str
        ``jaccard`` or ``sorensen``, default is ``jaccard``.
    axis : tuple of int
        All dimensions are reduced, default ``[1,2,3]``.
    smooth : float
        This small value will be added to the numerator and denominator.
            - If both output and target are empty, it makes sure dice is 1.
            - If either output or target are empty (all pixels are background), dice = ```smooth/(small_value + smooth)``, then if smooth is very small, dice close to 0 (even the image values lower than the threshold), so in this case, higher smooth can have a higher dice.

    Examples
    ---------
    >>> import tensorlayer as tl
    >>> outputs = tl.act.pixel_wise_softmax(outputs)
    >>> dice_loss = 1 - tl.cost.dice_coe(outputs, y_)

    References
    -----------
    - `Wiki-Dice <https://en.wikipedia.org/wiki/Sørensen–Dice_coefficient>`__

    """
    inse = tf.reduce_sum(output * target, axis=axis)
    if loss_type == 'jaccard':
        l = tf.reduce_sum(output * output, axis=axis)
        r = tf.reduce_sum(target * target, axis=axis)
    elif loss_type == 'sorensen':
        l = tf.reduce_sum(output, axis=axis)
        r = tf.reduce_sum(target, axis=axis)
    else:
        raise Exception("Unknow loss_type")
    # old axis=[0,1,2,3]
    # dice = 2 * (inse) / (l + r)
    # epsilon = 1e-5
    # dice = tf.clip_by_value(dice, 0, 1.0-epsilon) # if all empty, dice = 1
    # new haodong
    dice = (2. * inse + smooth) / (l + r + smooth)
    ##
    dice = tf.reduce_mean(dice, name='dice_coe')
Laicheng0830 commented 2 years ago

The equation of Dice coefficient is 2∣X⋂Y∣/(∣X∣+∣Y∣).The general denominator is (∣X∣+∣Y∣), or it can be(∣X∣^2+∣Y∣^2). Here jaccard represents the way (∣X∣^2+∣Y∣^2).

dljve commented 2 years ago

Okay, thanks for the clarification. I was confused by the naming then, I understand that this form doesn't necessarily refer to the jaccard index.

Edit: I see that your second formulation is also the one that is used in the V-Net paper.