【问题标题】:Keras train and validation metric values are different even when using same data (Logistic regression)即使使用相同的数据,Keras 训练和验证指标值也不同(逻辑回归)
【发布时间】: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

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

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

【问题讨论】:

    标签: python tensorflow machine-learning keras deep-learning


    【解决方案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...

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-02-25
      • 1970-01-01
      • 2017-11-18
      • 2015-01-07
      • 1970-01-01
      • 2015-02-13
      • 2016-01-31
      • 2020-08-12
      相关资源
      最近更新 更多