即使使用相同的数据,Keras 训练和验证指标值也不同(逻辑回归)

Posted

技术标签:

【中文标题】即使使用相同的数据,Keras 训练和验证指标值也不同(逻辑回归)【英文标题】:Keras train and validation metric values are different even when using same data (Logistic regression) 【发布时间】:2019-04-14 17:26:36 【问题描述】:

我一直试图更好地理解keras 模型fit() 循环中的训练/验证序列。因此,我尝试了一个简单的训练循环,尝试使用由单个特征组成的输入数据拟合一个简单的逻辑回归模型。

我为训练验证提供相同的数据。在这些条件下,通过将批量大小指定为与总数据大小相同,人们会期望获得完全相同的损失和准确度。但这种情况并非如此。

这是我的代码:

用两个类生成一些两个随机数据:

N = 100
x = np.concatenate([np.random.randn(N//2, 1), np.random.randn(N//2, 1)+2])
y = np.concatenate([np.zeros(N//2), np.ones(N//2)])

并绘制两类数据分布(一个特征x):

data = pd.DataFrame('x': x.ravel(), 'y': y)
sns.violinplot(x='x', y='y', inner='point', data=data, orient='h')
pyplot.tight_layout(0)
pyplot.show()

构建并拟合 keras 模型:

model = tf.keras.Sequential([tf.keras.layers.Dense(1, activation='sigmoid', input_dim=1)])
model.compile(optimizer=tf.keras.optimizers.SGD(2), loss='binary_crossentropy', metrics=['accuracy'])
model.fit(x, y, epochs=10, validation_data=(x, y), batch_size=N)

请注意,我已经为训练和validation_data 指定了数据x 和目标y。此外,batch_size 与总大小 batch_size=N 相同。

训练结果为:

100/100 [==============================] - 1s 5ms/step - loss: 1.4500 - acc: 0.2300 - val_loss: 0.5439 - val_acc: 0.7200
Epoch 2/10
100/100 [==============================] - 0s 18us/step - loss: 0.5439 - acc: 0.7200 - val_loss: 0.4408 - val_acc: 0.8000
Epoch 3/10
100/100 [==============================] - 0s 16us/step - loss: 0.4408 - acc: 0.8000 - val_loss: 0.3922 - val_acc: 0.8300
Epoch 4/10
100/100 [==============================] - 0s 16us/step - loss: 0.3922 - acc: 0.8300 - val_loss: 0.3659 - val_acc: 0.8400
Epoch 5/10
100/100 [==============================] - 0s 17us/step - loss: 0.3659 - acc: 0.8400 - val_loss: 0.3483 - val_acc: 0.8500
Epoch 6/10
100/100 [==============================] - 0s 16us/step - loss: 0.3483 - acc: 0.8500 - val_loss: 0.3356 - val_acc: 0.8600
Epoch 7/10
100/100 [==============================] - 0s 17us/step - loss: 0.3356 - acc: 0.8600 - val_loss: 0.3260 - val_acc: 0.8600
Epoch 8/10
100/100 [==============================] - 0s 18us/step - loss: 0.3260 - acc: 0.8600 - val_loss: 0.3186 - val_acc: 0.8600
Epoch 9/10
100/100 [==============================] - 0s 18us/step - loss: 0.3186 - acc: 0.8600 - val_loss: 0.3127 - val_acc: 0.8700
Epoch 10/10
100/100 [==============================] - 0s 23us/step - loss: 0.3127 - acc: 0.8700 - val_loss: 0.3079 - val_acc: 0.8800

结果表明val_lossloss在每个epoch结束时不一样,accval_acc 并不完全相同。但是,基于此设置,人们会期望它们是相同的。

我一直在浏览 keras 中的代码,尤其是这部分: https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/engine/training.py#L1364

到目前为止,我只能说差异是由于通过计算图进行的一些不同的计算。

有人知道为什么会有这样的差异吗?

【问题讨论】:

【参考方案1】:

因此,在仔细查看结果之后,训练步骤中的 lossacc 值是在当前批次用于更新模型之前计算出来的。

因此,在每个 epoch 单个批次的情况下,在输入批次时评估训练 accloss,然后根据提供的优化器更新模型参数。训练步骤完成后,我们通过输入验证数据来计算损失和准确率,现在使用新的更新模型对其进行评估。

这从训练结果输出中可以明显看出,其中 epoch 1 中的验证准确度和损失等于 epoch 2 中的训练准确度和损失等...

使用 tensorflow 的快速检查确认在更新变量之前获取了值:

import tensorflow as tf
import numpy as np
np.random.seed(1)

x = tf.placeholder(dtype=tf.float32, shape=(None, 1), name="x")
y = tf.placeholder(dtype=tf.float32, shape=(None), name="y")

W = tf.get_variable(name="W", shape=(1, 1), dtype=tf.float32, initializer=tf.constant_initializer(0))
b = tf.get_variable(name="b", shape=1, dtype=tf.float32, initializer=tf.constant_initializer(0))
z = tf.matmul(x, W) + b

error = tf.square(z - y)
obj = tf.reduce_mean(error, name="obj")

opt = tf.train.MomentumOptimizer(learning_rate=0.025, momentum=0.9)
grads = opt.compute_gradients(obj)
train_step = opt.apply_gradients(grads)

N = 100
x_np = np.random.randn(N).reshape(-1, 1)
y_np = 2*x_np + 3 + np.random.randn(N)

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    for i in range(2):
        res = sess.run([obj, W, b, train_step], feed_dict=x: x_np, y: y_np)
        print('MSE: , W: , b: '.format(res[0], res[1][0, 0], res[2][0]))

输出:

MSE: 14.721437454223633, W: 0.0, b: 0.0
MSE: 13.372591018676758, W: 0.08826743811368942, b: 0.1636980175971985

由于参数Wb 被初始化为0,那么很明显,即使会话以梯度更新请求运行,获取的值仍然为0...

【讨论】:

以上是关于即使使用相同的数据,Keras 训练和验证指标值也不同(逻辑回归)的主要内容,如果未能解决你的问题,请参考以下文章

Keras预测给出的误差与评估不同,损失与指标不同

Keras cifar10 示例验证和测试损失低于训练损失

sklearn:应用相同的缩放来训练和预测管道

如何在交叉验证中获得 Keras scikit-learn 包装器的训练和验证损失?

Keras猫狗大战五:采用全部数据集训练,精度提高到90%

使用 Keras 进行深度学习 - 训练时没有学习率