Epoch 由单个批次组成,但 model.fit 和 model.evaluate 在 Keras 上给出不同的结果。Model.fit 不正确

数据挖掘 喀拉斯
2022-02-10 12:47:59

不幸的是,我之前的问题没有得到令人满意的回答(如下所示,如果一个时期由一个批次组成,观察到相同的奇怪行为),这个问题让我发疯,所以请原谅用新的、更有针对性的问题重新发布,很简单的代码。这是我以前版本的基本相同问题的链接:

似乎 Keras 的 .fit 和 .evaluate 方法给出了不同的训练准确度(但验证准确度相同)。损失也一样。?

(我试图评论该主题的建议答案,但允许的空间太短而无法发布新代码)。

请注意,在下面的新示例中,一个 epoch 有一个批次(2 个示例),并且代码针对单个 epoch 进行了训练,因此 1 个 epoch = 1 个批次。因此,什么 model.fit 打印应该匹配什么 model.evaluate 打印。(这是由 Andrej Karpathy 建议从拟合单个批次开始的)

import numpy as np
np.random.seed(42)
import keras
from keras import layers
from keras.regularizers import l2
import tensorflow as tf
tf.random.set_seed(42)
import keras.backend as K

#def mse_custom(y_true, y_pred): #Tried defining the loss, got same results           
#    custom = K.mean(K.square((y_pred - y_true)))    
#    return custom

#Below I use 20 examples because the issue becomes much clearer than with a few examples
x_train = np.array([0.10526316, 0. ,0.26315789,0.73684211,0.94736842,1., \
                    0.52631579,0.84210526,0.21052632, 0.63157895,0.42105263,0.31578947,\
                    0.36842105,0.78947368,0.15789474,0.68421053, 0.47368421,0.05263158,\
                    0.57894737,0.89473684]).reshape(20,1,1,1)
y_train = np.array([0.31438135, 0. ,0.48843161,0.83670357,0.86771850,1., \
                    0.70921682,0.85732503,0.48475008,0.79387635,0.66044467,0.46540228,\
                    0.57612215,0.97176724,0.43534982,0.83881293,0.61425264,0.30733072,\
                    0.68508229,0.87707974]).reshape(20,1)
input_shape = (None,None,1)  #(1,1,1) gives the same results

seedbase = 1
model = keras.Sequential(
    [
        keras.Input(shape=input_shape),
        layers.Dense(100, activation="sigmoid", use_bias=True,
                     kernel_initializer=keras.initializers.RandomNormal(mean=0., stddev=1./np.sqrt(1),seed=seedbase+0), #/np.sqrt(30)
                     bias_initializer=keras.initializers.RandomNormal(mean=0., stddev=1.,seed=seedbase+1),
                     kernel_regularizer=l2(0),
                     bias_regularizer=None),
        layers.Dense(1, activation="sigmoid", use_bias=True,
                     kernel_initializer=keras.initializers.RandomNormal(mean=0., stddev=1./np.sqrt(1),seed=seedbase+12), #/np.sqrt(10)
                     bias_initializer=keras.initializers.RandomNormal(mean=0., stddev=1.,seed=seedbase+13), 
                     kernel_regularizer=l2(0),
                     bias_regularizer=None),
    ]
)

model.summary()

"""
## Train the model
"""
n_val = 2
n_tr =20-n_val
val_split = n_val/20
batch_size = 2
epochs = 1
opt = keras.optimizers.SGD(learning_rate=0.1)
#opt='adam'
#opt = keras.optimizers.RMSprop(0.00099)
model.compile(loss='mean_squared_error', optimizer=opt)
ann=model.fit(x_train, y_train, batch_size=batch_size, epochs=epochs, validation_split=val_split, verbose = 1)
"""
## Evaluate the trained model
"""
print("\n")
score = model.evaluate(x_train[:n_tr], y_train[:n_tr], batch_size=1, verbose=0)
print("Training loss:", score)
score = model.evaluate(x_train[-n_val:], y_train[-n_val:], batch_size=1, verbose=0)
print("Validation loss:", score)

如果您将 epochs 增加到 100,model.fit 训练损失将变为 0(仅匹配两个示例中的一个,但与另一个示例相距甚远,因此显然不正确,这是错误的线索)训练停止。model.evaluate 训练损失稳定在正确的值。而validation model.fit loss与validation model.evaluate loss非常接近。

此外,如果我增加单个批次中的示例数量,则不匹配会变得更小。

我知道我一定做错了什么,但是什么?您的帮助将不胜感激。

1个回答

您的输入/标签的形状设置不正确,我猜这会导致损失计算中发生一些奇怪的事情。

您正在设置input_shape=(None, None, 1),但您的 x_train 的输入形状为 (20,1,1,1)。首先,这些应该具有相同的维度,输入形状中的每个 None 都表示一个可变的批次维度。简单的方法是让input_shape=(None, 1)和 x_train 的输入形状为 (20,1),但或者让input_shape=(None, None, None, 1)和 x_train 的输入形状为 (20, 1, 1, 1) 也可以。但是,您还必须确保 y_train 与 x_train 具有相同的批量维度。所以在简单的情况下,这将是 (20, 1),但如果 x_train 的形状为 (20, 1, 1, 1),那么 y_train 的形状也应该为 (20, 1, 1, 1)。

进行这些更改后,我在训练期间得到的 val_loss 与在评估中计算的相同。训练损失(相对于 val_loss)总是不同的,因为训练损失是在训练期间计算的(在权重更新之前)。