我有大小为(100,100,4)的三阶张量,我试图用自编码器进行压缩和重建。我使用了一个基于物理原理的损失函数。数学上它是
L = – overlap(y_true,y_pred) + |1 – norm(y_pred)^2|
在代码中,它是这样的:
def physical_loss(y_true,y_pred,norm=None): return - tf.tensordot(y_true,y_pred,axes=([0,1,2],[0,1,2])) + tf.math.abs(1 - tf.tensordot(y_pred,y_pred,axes=([0,1,2],[0,1,2])))
第一个项是输入和输出张量之间的重叠(我希望最大化),第二个项防止输出张量爆炸,并且最理想的情况是保持其范数为1(张量与自身的重叠=范数)。因此,理想情况下,损失会降到-1(即y_pred = y_true且norm(y_pred)=1)
我的模型是一个卷积自编码器,它将大小缩减到(50,50,4),(25,25,4),然后再次构建回来。
cnn.compile(loss=physical_loss,optimizer="adam")cnn.fit(x_train, x_train, epochs=num_epochs, batch_size=16,validation_data=(x_train,x_train))
现在,当我训练我的卷积神经网络时,损失在16个epoch内逐渐下降,最终达到val_loss = -0.67。但是当我手动检查结果(即直接计算x_train和cnn.predict(x_train)之间的损失),平均我得到的是0.75(正数!)。
所以我的问题是Keras/TensorFlow在评估时输出了什么?
这可能与另一个问题有关,但当我使用cnn.evaluate(x_train,x_train,batch_size=X)时,结果会根据我选择的batch size X在+0.88到-0.88之间变化(选择None时,即32时,结果为-0.67)。我知道Keras是以批次处理所有事情的,但这不应该改变整体输出,对吗?这也使得解释输出/训练成功变得困难。
回答:
我想我已经弄明白了,问题在于Keras/TensorFlow期望的是一个已经向量化的函数。上述定义适用于三阶张量,但Keras/TensorFlow总是处理批次,即不是实际的列表而是四阶张量(第一维是“列表”)。
我发现向量化这个损失函数并不简单,但我找到了一个使用tf.map_fn(function,listed_tensor,dtype)的有效解决方案,其中listed_tensor是提到的四阶批次。
def single_eval(duo): return - tf.tensordot(duo[0],duo[1],axes=([0,1,2],[0,1,2])) + tf.keras.backend.abs(1 - tf.tensordot(duo[1],duo[1],axes=([0,1,2],[0,1,2])))def physical_loss(y_true,y_pred,norm=None): return tf.map_fn(single_eval,(y_true,y_pred),dtype=(tf.float32))
指定dtype是必要的,以便能够使用元组作为参数(即使用两个参数)。