如何了解模型损失的输出和骰子系数

时间:2020-07-07 15:17:57

标签: python tensorflow keras

我正在使用它:

Python version: 3.7.7 (default, May  6 2020, 11:45:54) [MSC v.1916 64 bit (AMD64)]
TensorFlow version: 2.1.0
Eager execution: True

使用此U-Net模型:

inputs = Input(shape=img_shape)

    conv1 = Conv2D(64, (5, 5), activation='relu', padding='same', data_format="channels_last", name='conv1_1')(inputs)
    conv1 = Conv2D(64, (5, 5), activation='relu', padding='same', data_format="channels_last", name='conv1_2')(conv1)
    pool1 = MaxPooling2D(pool_size=(2, 2), data_format="channels_last", name='pool1')(conv1)
    conv2 = Conv2D(96, (3, 3), activation='relu', padding='same', data_format="channels_last", name='conv2_1')(pool1)
    conv2 = Conv2D(96, (3, 3), activation='relu', padding='same', data_format="channels_last", name='conv2_2')(conv2)
    pool2 = MaxPooling2D(pool_size=(2, 2), data_format="channels_last", name='pool2')(conv2)

    conv3 = Conv2D(128, (3, 3), activation='relu', padding='same', data_format="channels_last", name='conv3_1')(pool2)
    conv3 = Conv2D(128, (3, 3), activation='relu', padding='same', data_format="channels_last", name='conv3_2')(conv3)
    pool3 = MaxPooling2D(pool_size=(2, 2), data_format="channels_last", name='pool3')(conv3)

    conv4 = Conv2D(256, (3, 3), activation='relu', padding='same', data_format="channels_last", name='conv4_1')(pool3)
    conv4 = Conv2D(256, (4, 4), activation='relu', padding='same', data_format="channels_last", name='conv4_2')(conv4)
    pool4 = MaxPooling2D(pool_size=(2, 2), data_format="channels_last", name='pool4')(conv4)

    conv5 = Conv2D(512, (3, 3), activation='relu', padding='same', data_format="channels_last", name='conv5_1')(pool4)
    conv5 = Conv2D(512, (3, 3), activation='relu', padding='same', data_format="channels_last", name='conv5_2')(conv5)

    up_conv5 = UpSampling2D(size=(2, 2), data_format="channels_last", name='up_conv5')(conv5)
    ch, cw = get_crop_shape(conv4, up_conv5)
    crop_conv4 = Cropping2D(cropping=(ch, cw), data_format="channels_last", name='crop_conv4')(conv4)
    up6 = concatenate([up_conv5, crop_conv4])
    conv6 = Conv2D(256, (3, 3), activation='relu', padding='same', data_format="channels_last", name='conv6_1')(up6)
    conv6 = Conv2D(256, (3, 3), activation='relu', padding='same', data_format="channels_last", name='conv6_2')(conv6)

    up_conv6 = UpSampling2D(size=(2, 2), data_format="channels_last", name='up_conv6')(conv6)
    ch, cw = get_crop_shape(conv3, up_conv6)
    crop_conv3 = Cropping2D(cropping=(ch, cw), data_format="channels_last", name='crop_conv3')(conv3)
    up7 = concatenate([up_conv6, crop_conv3])
    conv7 = Conv2D(128, (3, 3), activation='relu', padding='same', data_format="channels_last", name='conv7_1')(up7)
    conv7 = Conv2D(128, (3, 3), activation='relu', padding='same', data_format="channels_last", name='conv7_2')(conv7)

    up_conv7 = UpSampling2D(size=(2, 2), data_format="channels_last", name='up_conv7')(conv7)
    ch, cw = get_crop_shape(conv2, up_conv7)
    crop_conv2 = Cropping2D(cropping=(ch, cw), data_format="channels_last", name='crop_conv2')(conv2)
    up8 = concatenate([up_conv7, crop_conv2])
    conv8 = Conv2D(96, (3, 3), activation='relu', padding='same', data_format="channels_last", name='conv8_1')(up8)
    conv8 = Conv2D(96, (3, 3), activation='relu', padding='same', data_format="channels_last", name='conv8_2')(conv8)

    up_conv8 = UpSampling2D(size=(2, 2), data_format="channels_last", name='up_conv8')(conv8)
    ch, cw = get_crop_shape(conv1, up_conv8)
    crop_conv1 = Cropping2D(cropping=(ch, cw), data_format="channels_last", name='crop_conv1')(conv1)
    up9 = concatenate([up_conv8, crop_conv1])
    conv9 = Conv2D(64, (3, 3), activation='relu', padding='same', data_format="channels_last", name='conv9_1')(up9)
    conv9 = Conv2D(64, (3, 3), activation='relu', padding='same', data_format="channels_last", name='conv9_2')(conv9)

    ch, cw = get_crop_shape(inputs, conv9)
    conv9 = ZeroPadding2D(padding=(ch, cw), data_format="channels_last", name='conv9_3')(conv9)
    conv10 = Conv2D(1, (1, 1), activation='sigmoid', data_format="channels_last", name='conv10_1')(conv9)
    model = Model(inputs=inputs, outputs=conv10)

并具有以下功能:

def dice_coef(y_true, y_pred):
    y_true_f = K.flatten(y_true)
    y_pred_f = K.flatten(y_pred)
    intersection = K.sum(y_true_f * y_pred_f)
    return (2.0 * intersection + 1.0) / (K.sum(y_true_f) + K.sum(y_pred_f) + 1.0)

def dice_coef_loss(y_true, y_pred):
    return 1-dice_coef(y_true, y_pred)

要编译模型,请执行以下操作:

model.compile(tf.keras.optimizers.Adam(lr=(1e-4) * 2), loss=dice_coef_loss, metrics=[dice_coef])

我在训练时得到以下输出:

Epoch 1/2
5/5 [==============================] - 8s 2s/sample - loss: 1.0000 - dice_coef: 4.5962e-05 - val_loss: 0.9929 - val_dice_coef: 0.0071
Epoch 2/2
5/5 [==============================] - 5s 977ms/sample - loss: 0.9703 - dice_coef: 0.0297 - val_loss: 0.9939 - val_dice_coef: 0.0061
Train on 5 samples, validate on 5 samples

我认为这个想法是使损失接近零,但我不理解我得到的1.000(也许我可以获得的最差损失值)。但是我不明白dice_coef的值。

dice_coef值是什么意思?

1 个答案:

答案 0 :(得分:1)

骰子损失是一种损失函数,可以防止普通交叉熵损失中的某些局限性。

交叉熵的局限性:

使用交叉熵损失时,标签的统计分布在训练准确性中起着重要作用。 标签分布越不平衡,训练就越困难。尽管加权交叉熵损失可以减轻难度,但改进并不显着,也无法解决交叉熵损失的内在问题。在交叉熵损失中,损失被计算为每个像素损失的平均值,而每个像素损失则被离散地计算,而无需知道其相邻像素是否为边界。交叉熵损失仅从微观意义上考虑损失,而不是全局考虑,这不足以进行图像水平预测。

骰子丢失

Dice Coef功能可以描述为:

Dice Loss

这显然是您的功能dice_coef(y_true, y_pred)正在计算的内容。关于Sørensen–Dice coefficient

的更多信息

在以上p_ig_i中的等式中,分别是成对的对应预测值和地面真实值的像素值。在边界检测方案中,它们的值为0或1,表示像素是否为边界(值为1)或不是边界(值为0)。分母是预测和基本事实的总边界像素之和,分子是正确预测的边界像素的总和,因为总和仅在pi和gi时增加匹配(均为值1)。 Better Illustration Dice Loss

分母考虑全局范围内边界像素的总数,而分子考虑局部范围内两组像素之间的重叠。 因此,骰子丢失会考虑本地和全局的丢失信息,这对于确保准确性至关重要。

关于您的训练,由于您的损失价值在训练中正在降低,因此您不必担心太多,尝试增加时期并在模型中分析网络。< / p>

骰子损失仅为1 - dice coef。这是您的函数正在计算的。

Dice Loss