微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

BinaryCrossentropy 中 from_logits 的意外行为?

如何解决BinaryCrossentropy 中 from_logits 的意外行为?

我正在玩一个天真的 U-net,我将它作为玩具数据集部署在 MNIST 上。 我发现 from_logits 参数在 tf.keras.losses.BinaryCrossentropy 中的工作方式有一种奇怪的行为。

据我了解,如果在任何神经网络的最后一层使用 activation='sigmoid',那么在 tf.keras.losses.BinaryCrossentropy 中必须使用 from_logits=False。如果改为 activation=None,则需要 from_logits=True。尽管 from_logits=True 看起来更稳定(例如 Why does sigmoid & crossentropy of Keras/tensorflow have low precision?),但它们中的任何一个都应该在实践中起作用。 以下示例中的情况并非如此

所以,我的 unet 如下(完整代码在本文末尾):

def unet(input,init_depth,activation):
    # do stuff that defines layers
    # last layer is a 1x1 convolution
    output = tf.keras.layers.Conv2D(1,(1,1),activation=activation)(prevIoUs_layer) # shape = (28x28x1)
    return tf.keras.Model(input,output)

现在我定义了两个模型,一个在最后一层激活:

input = Layers.Input((28,28,1))
model_withProbs = unet(input,4,activation='sigmoid')
model_withProbs.compile(loss=tf.keras.losses.BinaryCrossentropy(from_logits=False),optimizer=tf.keras.optimizers.Adam()) #from_logits=False since the sigmoid is already present

一个没有

model_withLogits = unet(input,activation=None)
model_withLogits.compile(loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),optimizer=tf.keras.optimizers.Adam()) #from_logits=True since there is no activation

如果我是对的,他们应该有完全相同的行为。

相反,model_withLogits 的预测具有高达 2500 左右的像素值(这是错误的),而对于 model_withProbs,我得到的值介于 0 和 1 之间(这是正确的)。你可以看看我得到的数字here

我考虑过稳定性问题(from_logits=True 更稳定),但这个问题甚至在训练之前就出现了(see here)。此外,问题正是当我通过 from_logits=True(即 model_withLogits)时,所以我认为稳定性无关紧要。

有人知道为什么会这样吗?我在这里遗漏了什么基本知识吗?

Post Scriptum:代码

重新利用 MNIST 进行细分。

我加载 MNIST:

(x_train,labels_train),(x_test,labels_test) = tf.keras.datasets.mnist.load_data()

我通过将所有非零值 x_train 设置为 1 来重新利用 MNIST 进行分段任务:

x_train = x_train/255 #normalisation 
x_test = x_test/255 
Y_train = np.zeros(x_train.shape)  #create segmentation map
Y_train[x_train>0] = 1   #Y_train is zero everywhere but where the digit is drawn

完整的 unet 网络

def unet(input,activation):

  conv1 = Layers.Conv2D(init_depth,(2,2),activation='relu',padding='same')(input)
  pool1 = Layers.MaxPool2D((2,2))(conv1)
  drop1 = Layers.Dropout(0.2)(pool1)

  conv2 = Layers.Conv2D(init_depth*2,padding='same')(drop1)
  pool2 = Layers.MaxPool2D((2,2))(conv2)
  drop2 = Layers.Dropout(0.2)(pool2)

  conv3 = Layers.Conv2D(init_depth*4,padding='same')(drop2)
  #pool3 = Layers.MaxPool2D((2,2))(conv3)
  #drop3 = Layers.Dropout(0.2)(conv3)

  #upsampling
  up1 = Layers.Conv2DTranspose(init_depth*2,strides=(2,2))(conv3)
  up1 = Layers.concatenate([conv2,up1])
  conv4 = Layers.Conv2D(init_depth*2,padding='same')(up1)

  up2 = Layers.Conv2DTranspose(init_depth,padding='same')(conv4)
  up2 = Layers.concatenate([conv1,up2])
  conv5 = Layers.Conv2D(init_depth,padding='same' )(up2)

  last = Layers.Conv2D(1,activation=activation)(conv5)


  return tf.keras.Model(inputs=input,outputs=last)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。