You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Keras中model.fit与model.evaluate训练集准确率不一致问题

问题:Keras训练日志准确率显示1.0,但evaluate测试训练集准确率偏低

我在探索单个神经元的拟合极限,针对二元分类任务验证它能否完美拟合上升沿(x<10时y=0,x≥10时y=1)。但测试时发现:模型通过model.fit训练时日志里的accuracy达到1.0,可使用model.evaluate在训练集上测试时,准确率却约为0.9940000176429749;就算取消EarlyStopping直接查看不同epoch的结果,二者数值也经常存在差异。

复现代码

import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import keras
import random
from keras import layers
from keras.callbacks import EarlyStopping
from keras.optimizers import Adam


def random_seed(seed_num=1):
    np.random.seed(seed_num)
    tf.random.set_seed(seed_num)
    random.seed(seed_num)


class CustomEarlyStopping(keras.callbacks.Callback):
    def __init__(self, threshold):
        super().__init__()
        self.threshold = threshold

    def on_epoch_end(self, epoch, logs=None):
        accuracy = logs["accuracy"]
        loss = logs['loss']
        if accuracy >= self.threshold:
            self.model.stop_training = True

# data init
x = np.arange(-20, 30, 0.1)
y = np.zeros_like(x)
df = pd.DataFrame({'x': x, 'y': y})
df.y = df.x.map(lambda x: 0 if x<10 else 1)
X_train = df.drop(columns='y')
y_train = df.y

# model
random_seed()
model = keras.Sequential([
    layers.Input(shape=X_train.shape[-1]),
    layers.Normalization(),
    layers.Dense(1, activation='relu'),
    layers.Dense(1, activation='sigmoid'),
])
model.compile(
    optimizer=Adam(learning_rate=0.1),
    loss='binary_crossentropy',
    metrics=['accuracy'],
)
history = model.fit(
    X_train, y_train, 
    batch_size=128,
    epochs=100,
    callbacks=[
        CustomEarlyStopping(1.0)
    ]
)
history_df = pd.DataFrame(history.history)

last_accuracy = history_df.accuracy.tolist()[-1]
predict_accuracy = model.evaluate(X_train, y_train)[-1]

print('last_accuracy', last_accuracy)           # 1.0
print('predict_accuracy', predict_accuracy)     # 0.9940000176429749

问题原因

核心问题出在**layers.Normalization层的行为差异**:

  • 训练阶段:未提前对Normalization层做适配(adapt),该层会在每个batch训练时使用当前batch的均值和方差做归一化,同时累积更新全局移动均值和方差。
  • 评估阶段:模型切换到推理模式,Normalization层会使用训练过程中累积的全局移动均值和方差做归一化,而非当前batch的统计值。

这就导致同一个样本在训练和评估时的归一化结果不一致,最终预测输出出现偏差,进而造成训练日志的准确率和evaluate的准确率不匹配。

另外,训练日志里的accuracy是批次准确率的平均值,当大部分batch准确率为100%,少数batch接近100%时,平均值可能被四舍五入显示为1.0,但全量评估时会暴露少量预测错误的样本。

解决方案

最直接的修复方式是提前让Normalization层适配训练集的全局统计量,确保训练和评估时使用相同的归一化参数:

# 在模型compile前添加这行代码
model.layers[1].adapt(X_train)

修改后的完整代码:

import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import keras
import random
from keras import layers
from keras.callbacks import EarlyStopping
from keras.optimizers import Adam


def random_seed(seed_num=1):
    np.random.seed(seed_num)
    tf.random.set_seed(seed_num)
    random.seed(seed_num)


class CustomEarlyStopping(keras.callbacks.Callback):
    def __init__(self, threshold):
        super().__init__()
        self.threshold = threshold

    def on_epoch_end(self, epoch, logs=None):
        accuracy = logs["accuracy"]
        loss = logs['loss']
        if accuracy >= self.threshold:
            self.model.stop_training = True

# data init
x = np.arange(-20, 30, 0.1)
y = np.zeros_like(x)
df = pd.DataFrame({'x': x, 'y': y})
df.y = df.x.map(lambda x: 0 if x<10 else 1)
X_train = df.drop(columns='y')
y_train = df.y

# model
random_seed()
model = keras.Sequential([
    layers.Input(shape=X_train.shape[-1]),
    layers.Normalization(),
    layers.Dense(1, activation='relu'),
    layers.Dense(1, activation='sigmoid'),
])
# 关键:适配训练集,让Normalization层使用全局均值和方差
model.layers[1].adapt(X_train)
model.compile(
    optimizer=Adam(learning_rate=0.1),
    loss='binary_crossentropy',
    metrics=['accuracy'],
)
history = model.fit(
    X_train, y_train, 
    batch_size=128,
    epochs=100,
    callbacks=[
        CustomEarlyStopping(1.0)
    ]
)
history_df = pd.DataFrame(history.history)

last_accuracy = history_df.accuracy.tolist()[-1]
predict_accuracy = model.evaluate(X_train, y_train)[-1]

print('last_accuracy', last_accuracy)           
print('predict_accuracy', predict_accuracy)     

额外说明

你的模型结构用了两层Dense(relu+sigmoid),其实单个Dense层加sigmoid就可以完美拟合这个线性可分的上升沿任务,不过这不是导致准确率差异的原因。

内容的提问来源于stack exchange,提问作者SuiFengPiaoYang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 01:35:03