【问题标题】:Implementing Multiclass Dice Loss Function实现多类骰子损失函数
【发布时间】:2021-03-15 10:48:45
【问题描述】:

我正在使用 UNet 进行多类分割。我对模型的输入是HxWxC,我的输出是,

outputs = layers.Conv2D(n_classes, (1, 1), activation='sigmoid')(decoder0)

使用SparseCategoricalCrossentropy 我可以很好地训练网络。现在我也想尝试骰子系数作为损失函数。实现如下,

def dice_loss(y_true, y_pred, smooth=1e-6):
    y_true = tf.cast(y_true, tf.float32)
    y_pred = tf.math.sigmoid(y_pred)

    numerator = 2 * tf.reduce_sum(y_true * y_pred) + smooth
    denominator = tf.reduce_sum(y_true + y_pred) + smooth

    return 1 - numerator / denominator

但是,我实际上是在增加损失而不是减少损失。我检查了多个来源,但我发现的所有材料都使用 diceloss 进行二进制分类而不是多类。所以我的问题是执行是否有问题。

【问题讨论】:

  • @DavidS 谢谢你确实解决了问题

标签: python tensorflow tensorflow2.0 semantic-segmentation


【解决方案1】:

问题在于您的骰子损失并没有解决您拥有的类别数量,而是假设二进制大小写,因此它可能解释了您的损失增加。

您应该实现考虑所有类的广义骰子损失并返回所有类的值。

类似于以下内容:

def dice_coef_9cat(y_true, y_pred, smooth=1e-7):
    '''
    Dice coefficient for 10 categories. Ignores background pixel label 0
    Pass to model as metric during compile statement
    '''
    y_true_f = K.flatten(K.one_hot(K.cast(y_true, 'int32'), num_classes=10)[...,1:])
    y_pred_f = K.flatten(y_pred[...,1:])
    intersect = K.sum(y_true_f * y_pred_f, axis=-1)
    denom = K.sum(y_true_f + y_pred_f, axis=-1)
    return K.mean((2. * intersect / (denom + smooth)))

def dice_coef_9cat_loss(y_true, y_pred):
    '''
    Dice loss to minimize. Pass to model as loss during compile statement
    '''
    return 1 - dice_coef_9cat(y_true, y_pred)

这个sn-p取自https://github.com/keras-team/keras/issues/9395#issuecomment-370971561

这是针对 9 个类别的,而您应该根据自己拥有的类别数量进行调整。

【讨论】:

  • 这确实有效,但它的训练效果总是比SparseCategoricalCrossentropy 差。我认为问题是y_pred 未转换为热转换损失按预期计算如何导致ValueError: No gradients provided for any variable: 错误
【解决方案2】:

如果你在做多类分割,应该使用'softmax'激活函数。

我建议使用 one-hot 编码的真实值掩码。这需要在损失计算代码之外完成。

广义骰子损失等在以下链接中实现:

https://github.com/NifTK/NiftyNet/blob/dev/niftynet/layer/loss_segmentation.py

【讨论】:

  • “这需要在损失计算代码之外进行”是否有理由在损失函数之外进行 one-hot 编码?
  • 其实你可以在损失函数内部做one-hot encoding,但是如果你有多个损失函数,那么你需要在每个损失函数中添加one-hot encoding。这使得代码更长。
【解决方案3】:

不知道为什么,但最后一层有“sigmoid”作为激活函数。 对于多类分割,它必须是“softmax”而不是“sigmoid”。

此外,您正在考虑的损失是 SparseCategoricalCrossentropy 以及多通道输出。如果最后一层只有 1 个通道(在进行多类分割时),那么使用 SparseCategoricalCrossentropy 是有意义的,但是当您有多个通道作为输出时,要考虑的损失是“CategoricalCrossentropy”。

由于激活和输出通道不匹配(如上所述),您的损失正在增加。

改变

outputs = layers.Conv2D(n_classes, (1, 1), activation='sigmoid')(decoder0)

到

outputs = layers.Conv2D(n_classes, (1, 1), activation='softmax')(decoder0)

【讨论】:

    猜你喜欢
    • 2018-08-07
    • 2019-09-30
    • 1970-01-01
    • 1970-01-01
    • 2019-12-25
    • 2021-03-08
    • 2022-07-25
    • 2018-09-21
    • 2021-01-24
    相关资源
    最近更新 更多