神经网络训练异常:Loss恒为0.0、Accuracy恒为1.0问题排查
训练神经网络时异常指标的问题排查
问题现象
尝试用10个epoch训练神经网络,初始训练时出现异常输出:
35/300 [==>...........................] - ETA: 1:09 - loss: 0.0000e+00 - accuracy: 1.0000 36/300 [==>...........................] - ETA: 1:09 - loss: 0.0000e+00 - accuracy: 1.0000 37/300 [==>...........................] - ETA: 1:08 - loss: 0.0000e+00 - accuracy: 1.0000
调整后训练指标出现无规律波动,甚至出现准确率极低的情况:
5/300 [..............................] - ETA: 1:12 - loss: 0.1564 - accuracy: 0.9750 6/300 [..............................] - ETA: 1:15 - loss: 0.1311 - accuracy: 0.8333 7/300 [..............................] - ETA: 1:13 - loss: 0.1124 - accuracy: 0.7143 8/300 [..............................] - ETA: 1:13 - loss: 0.0984 - accuracy: 0.6250 9/300 [..............................] - ETA: 1:12 - loss: 0.0874 - accuracy: 0.5556
后续出现:
51/300 [====>.........................] - ETA: 1:04 - loss: 0.0154 - accuracy: 0.0980
完整训练代码
batch_size = 32 img_height = 150 img_width = 150 dataset_url = "http://cnrpark.it/dataset/CNR-EXT-Patches-150x150.zip" print(dataset_url) data_dir = tf.keras.utils.get_file(origin=dataset_url, fname='CNR-EXT-Patches-150x150', untar=True) train_ds = tf.keras.utils.image_dataset_from_directory( data_dir, validation_split=0.2, subset="training", seed=123, image_size=(img_height, img_width), batch_size=batch_size) num_classes = 1 val_ds = tf.keras.utils.image_dataset_from_directory( data_dir, validation_split=0.2, subset="validation", seed=123, image_size=(img_height, img_width), batch_size=batch_size) class_names = train_ds.class_names print(class_names) for image_batch, labels_batch in train_ds: print(image_batch.shape) print(labels_batch.shape) break normalization_layer = tf.keras.layers.Rescaling(1./255) normalized_ds = train_ds.map(lambda x, y: (normalization_layer(x), y)) image_batch, labels_batch = next(iter(normalized_ds)) first_image = image_batch[0] print(np.min(first_image), np.max(first_image)) model = tf.keras.Sequential([ tf.keras.layers.Rescaling(1./255), tf.keras.layers.Conv2D(32, 3, activation='relu'), tf.keras.layers.MaxPooling2D(), tf.keras.layers.Conv2D(32, 3, activation='relu'), tf.keras.layers.MaxPooling2D(), tf.keras.layers.Conv2D(32, 3, activation='relu'), tf.keras.layers.MaxPooling2D(), tf.keras.layers.Flatten(), tf.keras.layers.Dense(1, activation='sigmoid'), tf.keras.layers.Dense(num_classes) ]) AUTOTUNE = tf.data.AUTOTUNE train_ds = train_ds.cache().prefetch(buffer_size=AUTOTUNE) val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE) model.compile( optimizer='adam', loss=tf.keras.losses.BinaryCrossentropy(), metrics=['accuracy']) model.fit( train_ds, validation_data=val_ds, epochs=10 )
问题根源分析
1. 输出层结构错误
模型最后连续叠加了两个全连接层:
tf.keras.layers.Dense(1, activation='sigmoid'), tf.keras.layers.Dense(num_classes)
- 第一个
Dense(1, activation='sigmoid')已经输出了二分类所需的0-1概率值,完全匹配BinaryCrossentropy损失函数的要求。 - 第二个
Dense(num_classes)(num_classes=1)是多余的,且没有设置激活函数,会将sigmoid输出的0-1值映射为任意实数,导致损失计算逻辑完全混乱,直接引发loss为0、准确率异常波动等问题。
2. 类别数设置矛盾
CNR-EXT-Patches数据集是二分类任务(停车场空位/非空位),image_dataset_from_directory会根据数据集文件夹数量自动识别类别(class_names应该是两个类别),但你手动将num_classes设为1,虽然二分类用单节点输出是合理的,但结合多余的输出层,进一步加剧了训练逻辑的错误。
修复方案
- 移除多余的输出层:删除模型中的
tf.keras.layers.Dense(num_classes),保留tf.keras.layers.Dense(1, activation='sigmoid')作为唯一输出层。 - 可选优化:使用logits输出:如果想避免sigmoid激活可能带来的饱和问题,可以将输出层改为
tf.keras.layers.Dense(1),并在编译时设置:model.compile( optimizer='adam', loss=tf.keras.losses.BinaryCrossentropy(from_logits=True), metrics=['accuracy']) - 确认类别数:无需手动设置
num_classes=1,可以通过num_classes = len(class_names)自动获取真实类别数,二分类场景下不影响单节点输出的逻辑。
内容的提问来源于stack exchange,提问作者Jan Tuđan
相关产品推荐
相关产品推荐

