为何训练最后一轮val_accuracy与fit后eval acc结果不一致?
训练验证准确率与后续评估准确率不一致的问题
Epoch 19/20 1/1 [==============================] - 0s 473ms/step - loss: 1.4018 - accuracy: 0.8750 - val_loss: 1.8656 - val_accuracy: 0.8900 Epoch 20/20 1/1 [==============================] - 0s 444ms/step - loss: 0.5904 - accuracy: 0.8750 - val_loss: 2.1255 - val_accuracy: 0.8700 get_dataset: validation Found 1000 files belonging to 2 classes. Using 100 files for validation. 4/4 [==============================] - 1s 81ms/step eval acc: 0.81
问题
为何训练最后一轮的val_accuracy(0.87)与fit完成后的eval acc(0.81)结果不一致?我尝试让fit过程中每一轮的验证以及后续额外验证使用同一数据集。
更新记录
2022-07-19 更新1
- 显然两次准确率计算并未实际使用相同数据,如何调试实际使用的数据?
- 2022-07-20 更新3:已追踪数据到TensorFlow内部,确认Model.evaluate(fit过程中)和Model.predict中的x.filenames是相同的,但后续在quick_execute中的
__inference_test_function_248219和__inference_predict_function_231438在Python外执行,参数为dtype=resource的张量,无法查看内容。
- 2022-07-20 更新3:已追踪数据到TensorFlow内部,确认Model.evaluate(fit过程中)和Model.predict中的x.filenames是相同的,但后续在quick_execute中的
- 为简化示例特意移除了类别平衡代码,知晓这会降低准确率实用性,但目前暂不关注此点。
get_dataset('validation')仅在fit开始时调用一次,并非每一轮都调用。- 已设置
max_queue_size=0, use_multiprocessing=False, workers=0,但准确率仍不一致。
2022-07-19 更新2
使用已弃用的ImageDataGenerator也能复现该现象,相关代码如下:
from tensorflow.keras.applications.resnet50 import preprocess_input from keras_preprocessing.image import ImageDataGenerator datagen = ImageDataGenerator( preprocessing_function=preprocess_input, validation_split=0.1, ) def get_dataset(subset): print('get_dataset:', subset) return datagen.flow_from_directory( 'data-nodup-1000', class_mode='binary', target_size=(224, 224), shuffle=True, seed=1, subset=subset, )
同时将真实标签的获取代码改为:
true_class = val_dataset.labels
2022-07-21 更新4
将验证数据的shuffle设置为shuffle=(subset == 'training')可使两个验证准确率一致,但这并非可行方案——因为flow_from_directory不做分层,此时验证集仅包含类别1。
核心代码
import tensorflow as tf from sklearn.metrics import accuracy_score from tensorflow.keras.models import Model from tensorflow.keras.layers import Dense, Flatten from tensorflow.keras.preprocessing import image_dataset_from_directory inputs = tf.keras.Input(shape=(224, 224, 3)) base_model = tf.keras.applications.ResNet50(weights='imagenet', include_top=False) base_output = base_model(inputs) base_model.trainable = False out = Flatten(name='flat')(base_output) out = Dense(1, activation='sigmoid')(out) model = Model(inputs=inputs, outputs=out) model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy']) def get_dataset(subset): print('get_dataset:', subset) return image_dataset_from_directory( 'data-nodup-1000', labels="inferred", label_mode='binary', color_mode="rgb", image_size=(224, 224), shuffle=True, seed=1, validation_split=0.1, subset=subset, crop_to_aspect_ratio=False, ) model.fit( get_dataset('training'), steps_per_epoch=1, epochs=20, validation_data=get_dataset('validation'), max_queue_size=0, use_multiprocessing=False, workers=0, ) val_dataset = get_dataset('validation') true_class = tf.concat([y for x, y in val_dataset], axis=0) pred = model.predict(val_dataset) pred_class = pred >= .5 print('eval acc:', accuracy_score(true_class, pred_class))
环境说明
- 使用最新版本库,如tensorflow 2.9.1和sklearn 1.1.1(通过pip-compile -U安装)。
- 文件夹
data-nodup-1000包含两个子文件夹:一个有113个类别0文件,另一个有887个类别1文件。
内容的提问来源于stack exchange,提问作者Robert Pollak
相关产品推荐
相关产品推荐

