我只用Dense
层写了一个普通的自动编码器。 下面是我的代码:
iLayer = Input ((784,))
layer1 = Dense(128, activation='relu' ) (iLayer)
layer2 = Dense(64, activation='relu') (layer1)
layer3 = Dense(28, activation ='relu') (layer2)
layer4 = Dense(64, activation='relu') (layer3)
layer5 = Dense(128, activation='relu' ) (layer4)
layer6 = Dense(784, activation='softmax' ) (layer5)
model = Model (iLayer, layer6)
model.compile(loss='binary_crossentropy', optimizer='adam')
(trainX, trainY), (testX, testY) = mnist.load_data()
print ("shape of the trainX", trainX.shape)
trainX = trainX.reshape(trainX.shape[0], trainX.shape[1]* trainX.shape[2])
print ("shape of the trainX", trainX.shape)
model.fit (trainX, trainX, epochs=5, batch_size=100)
问题:
1(softmax
提供概率分布。理解。这意味着,我将有一个 784 个值的向量,概率在 0 到 1 之间。例如 [ 0.02, 0.03..... 最多 784 个项目],将所有 784 个元素相加得到 1。
2(我不明白二进制交叉熵如何与这些值一起工作。二进制交叉熵是针对两个输出值的,对吧?
在自动编码器的上下文中,模型的输入和输出是相同的。因此,如果输入值在 [0,1] 范围内,则可以使用sigmoid
作为最后一层的激活函数。否则,您需要对最后一层使用适当的激活函数(例如linear
这是默认的(。
至于损失函数,它再次回到输入数据的值。如果输入数据仅在0 和 1 之间(而不是它们之间的值(,则binary_crossentropy
可以作为损失函数。否则,您需要使用其他损失函数,例如'mse'
(即均方误差(或'mae'
(即平均绝对误差(。请注意,在范围[0,1]
的输入值的情况下,您可以使用binary_crossentropy
,因为它通常使用(例如 Keras 自动编码器教程和本文(。但是,不要期望损失值变为零,因为当预测和标签都不为零或一(无论它们是否相等(时binary_crossentropy
不会返回零。这是Hugo Larochelle的视频,他解释了自动编码器中使用的损失函数(关于使用binary_crossentropy
输入范围[0,1]的部分从5:30开始(
具体来说,在您的示例中,您使用的是 MNIST 数据集。因此,默认情况下,MNIST 的值是 [0, 255] 范围内的整数。通常您需要先规范化它们:
trainX = trainX.astype('float32')
trainX /= 255.
现在,这些值将在 [0,1] 范围内。因此sigmoid
可以用作激活函数,binary_crossentropy
或mse
中的任何一个都可以用作损失函数。
为什么即使真实标签值(即真实值(在 [0,1] 范围内也可以使用binary_crossentropy
?
请注意,我们试图在训练中最小化损失函数。因此,如果我们使用的损失函数在预测等于真标签时达到其最小值(可能不一定等于零(,那么它是一个可以接受的选择。让我们验证一下二进制交叉熵的情况,其定义如下:
bce_loss = -y*log(p) - (1-y)*log(1-p)
其中y
是真实标签,p
是预测值。让我们将y
视为固定的,看看p
的哪个值最小化了这个函数:我们需要取关于p
的导数(我假设log
是自然对数函数,以便于计算简单(:
bce_loss_derivative = -y*(1/p) - (1-y)*(-1/(1-p)) = 0 =>
-y/p + (1-y)/(1-p) = 0 =>
-y*(1-p) + (1-y)*p = 0 =>
-y + y*p + p - y*p = 0 =>
p - y = 0 => y = p
如您所见,二进制交叉熵在y=p
时具有最小值,即当真实标签等于预测标签时,这正是我们正在寻找的。