
在使用keras进行模型训练时,我们可能会观察到model.fit在每个epoch结束时打印的accuracy(训练精度)与训练结束后使用model.evaluate在相同训练集上计算得到的精度存在差异。例如,fit报告的精度可能达到1.0,而evaluate的结果却略低于1.0。这种差异尤其在自定义回调函数中依赖logs['accuracy']进行逻辑判断(如提前停止)时,可能导致意外的行为。
造成这种差异的根本原因在于model.fit和model.evaluate计算指标的方式不同:
model.fit中的训练精度(accuracy):在每个epoch内,模型会分批次(batch)处理数据并更新权重。model.fit报告的accuracy是该epoch内所有批次精度的平均值。重要的是,每个批次的精度是在该批次数据被处理之前(或在权重更新之后但尚未处理下一个批次之前)计算的。这意味着,对于一个epoch内的不同批次,模型的权重可能在不断变化,因此计算出的精度是基于动态变化的模型状态。当一个epoch结束时,报告的accuracy是整个epoch中,模型在处理各个批次时所达到的平均性能。
model.evaluate中的精度:model.evaluate函数在调用时,会使用模型当前的最终权重来对整个数据集进行一次性(或分批次)评估。它不会在评估过程中更新权重。因此,model.evaluate的结果代表了模型在固定权重下的整体性能。
当batch_size较小,或者模型在训练初期权重变化较大时,model.fit报告的平均精度与model.evaluate在最终权重下计算的精度之间就可能出现显著差异。
解决这一问题的关键在于,让model.fit在每个epoch结束时,使用当前epoch的最终权重,在一个固定数据集上计算指标。这可以通过fit方法的validation_data参数来实现。即使我们希望在训练集上进行评估以比较,也可以将训练集本身作为validation_data。
当validation_data被提供时,Keras会在每个epoch结束时,使用该epoch的最终模型权重对验证数据进行一次评估,并报告val_loss和val_accuracy等指标。这些val_accuracy值将与model.evaluate在相同数据集上得到的结果更加一致,因为它反映的是模型在固定权重下的表现。
对于自定义的提前停止回调,也应该监控val_accuracy而不是accuracy。
以下是修正后的代码示例,展示了如何通过引入validation_data并调整自定义回调来解决精度不一致问题:
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):
# 监控 'val_accuracy' 而不是 'accuracy'
accuracy = logs.get("val_accuracy")
if accuracy is not None and accuracy >= self.threshold:
print(f"\n达到验证精度阈值 {self.threshold},停止训练。")
self.model.stop_training = True
# 1. 数据准备
x = np.arange(-20, 30, 0.1)
y = np.zeros_like(x)
df = pd.DataFrame({'x': x, 'y': y})
# 创建一个简单的二分类问题:x < 10 为 0,否则为 1
df.y = df.x.map(lambda x_val: 0 if x_val < 10 else 1)
X_train = df.drop(columns='y')
y_train = df.y
# 2. 模型构建
random_seed() # 设置随机种子
model = keras.Sequential([
layers.Input(shape=X_train.shape[-1]),
layers.Normalization(), # 数据归一化层
layers.Dense(1, activation='relu'), # 第一个全连接层
layers.Dense(1, activation='sigmoid'), # 输出层,用于二分类
])
# 3. 模型编译
model.compile(
optimizer=Adam(learning_rate=0.1), # 使用Adam优化器
loss='binary_crossentropy', # 二元交叉熵损失函数
metrics=['accuracy'], # 监控精度
)
# 4. 模型训练
history = model.fit(
X_train, y_train,
validation_data=(X_train, y_train), # 关键:将训练集也作为验证集
batch_size=128,
epochs=300, # 增加epochs以确保模型充分训练
callbacks=[
CustomEarlyStopping(1.0) # 使用自定义提前停止回调
]
)
history_df = pd.DataFrame(history.history)
# 5. 结果验证
# 获取history中记录的最后一个训练精度(注意这里仍然是训练精度)
last_accuracy_fit = history_df.accuracy.tolist()[-1]
# 获取history中记录的最后一个验证精度
last_accuracy_val = history_df.val_accuracy.tolist()[-1]
# 使用model.evaluate在训练集上进行评估
predict_accuracy = model.evaluate(X_train, y_train, verbose=0)[-1] # verbose=0 不打印进度条
print(f'Fit报告的最后一个训练精度 (accuracy): {last_accuracy_fit:.6f}')
print(f'Fit报告的最后一个验证精度 (val_accuracy): {last_accuracy_val:.6f}')
print(f'model.evaluate评估的精度: {predict_accuracy:.6f}')
# 预期输出:
# Fit报告的最后一个训练精度 (accuracy): 1.000000
# Fit报告的最后一个验证精度 (val_accuracy): 1.000000
# model.evaluate评估的精度: 1.000000通过这些修改,model.fit报告的val_accuracy与model.evaluate的结果将保持高度一致,因为它们都是在相同的固定数据集上,使用相同的模型最终权重进行计算的。
通过理解Keras内部指标计算的机制并正确配置model.fit的参数,我们可以更准确地监控模型训练过程,并确保训练结果的可靠性。
以上就是Keras模型训练与评估精度不一致问题解析与解决方案的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号