为何CNN训练出现loss为nan、accuracy为0?附完整代码
这种情况我在扩展分类任务范围的时候也碰到过,结合你描述的「10分类(A-J)准确率正常,26分类就出现Loss=NaN、准确率极低」的现象,排除数据损坏/泄露、未归一化的问题后,大概率是以下几个原因,给你梳理排查方向和解决办法:
1. 标签取值范围不匹配损失函数要求
你用的是sparse_categorical_crossentropy损失函数,它要求标签必须是从0开始的连续整数(对应输出层神经元的索引)。比如26分类任务,标签应该是0-25,但如果你的代码从路径提取标签时,把A对应1、B对应2……Z对应26,那标签里就会出现26这个值——而输出层只有26个神经元(索引0-25),计算损失时会因为找不到对应索引导致数值溢出,最终变成NaN。
解决办法:
检查标签数组的取值范围,确保所有标签都在0到25之间。比如可以加一段代码验证:
print("标签最小值:", np.min(labels)) print("标签最大值:", np.max(labels))
如果最大值是26,就把所有标签减1,统一调整为0-25的范围。
2. 学习率过高导致梯度爆炸
10分类任务复杂度低,默认的Adam学习率(1e-3)可能刚好合适,但扩展到26分类后,任务难度提升,或者数据分布的差异变大,过高的学习率会让模型参数更新幅度过大,直接导致梯度爆炸,Loss瞬间变成NaN。
解决办法:
降低学习率,比如把Adam优化器的学习率调到1e-4甚至更小:
from tensorflow.keras.optimizers import Adam model.compile(optimizer=Adam(learning_rate=1e-4), loss='sparse_categorical_crossentropy', metrics=['accuracy'])
如果还是不行,可以继续尝试5e-5的学习率。
3. 梯度裁剪防止数值溢出
即使调整了学习率,有时候复杂模型的梯度还是会出现异常波动,这时候可以通过梯度裁剪限制梯度的范围,避免数值溢出导致NaN。
解决办法:
在Adam优化器里添加梯度裁剪参数,比如用clipnorm(限制梯度的L2范数):
model.compile(optimizer=Adam(learning_rate=1e-4, clipnorm=1.0), loss='sparse_categorical_crossentropy', metrics=['accuracy'])
或者用clipvalue限制梯度的最大绝对值:
model.compile(optimizer=Adam(learning_rate=1e-4, clipvalue=0.5), loss='sparse_categorical_crossentropy', metrics=['accuracy'])
4. 检查Dropout/BatchNormalization的参数合理性
虽然10分类时模型参数正常,但26分类任务下,过高的Dropout率可能导致模型无法有效学习特征,极端情况下也可能引发数值不稳定。比如如果你的Dropout率设成了0.7甚至更高,可以尝试降低到0.3-0.5之间。
另外,确认BatchNormalization的axis参数是否和输入维度匹配——你的输入是(75,75,1),最后一维是通道,所以axis=-1(默认值)是正确的,不用调整,但如果之前手动改了axis,可能会导致归一化异常。
快速排查步骤
建议按这个顺序快速定位问题:
- 先检查标签取值范围(最可能的原因,因为10分类正常,扩展后标签容易出问题)
- 然后降低学习率,加上梯度裁剪
- 最后调整Dropout率验证
内容的提问来源于stack exchange,提问作者JupyterBoi




