我正在玩一个天真的 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
看起来更稳定(例如,为什么 Keras/tensorflow 的 sigmoid 和交叉熵精度低?)。在以下示例中不是这种情况。
所以,我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,4,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 之间(这是正确的)。你可以看看我在这里得到的数字
我考虑过稳定性问题(from_logits=True
更稳定),但这个问题甚至在训练之前就出现了(见这里)。此外,问题正是当我通过from_logits=True
(即 for model_withLogits
)时,所以我认为稳定性无关紧要。
有人知道为什么会这样吗?我在这里缺少任何基本的东西吗?
后脚本:代码
重新利用 MNIST 进行分割。
我加载 MNIST:
(x_train, labels_train), (x_test, labels_test) = tf.keras.datasets.mnist.load_data()
我通过将所有非零值设置为一个来将 MNIST 重新用于分割任务x_train
:
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, init_depth,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,(2,2),activation='relu',padding='same')(drop1)
pool2 = Layers.MaxPool2D((2,2))(conv2)
drop2 = Layers.Dropout(0.2)(pool2)
conv3 = Layers.Conv2D(init_depth*4, (2,2), activation='relu',padding='same')(drop2)
#pool3 = Layers.MaxPool2D((2,2))(conv3)
#drop3 = Layers.Dropout(0.2)(conv3)
#upsampling
up1 = Layers.Conv2DTranspose(init_depth*2, (2,2), strides=(2,2))(conv3)
up1 = Layers.concatenate([conv2,up1])
conv4 = Layers.Conv2D(init_depth*2, (2,2), padding='same')(up1)
up2 = Layers.Conv2DTranspose(init_depth,(2,2), strides=(2,2), padding='same')(conv4)
up2 = Layers.concatenate([conv1,up2])
conv5 = Layers.Conv2D(init_depth, (2,2), padding='same' )(up2)
last = Layers.Conv2D(1,(1,1), activation=activation)(conv5)
return tf.keras.Model(inputs=input,outputs=last)