CNN图像分类模型ROC曲线面积为NaN问题求助
排查CNN图像分类任务中ROC曲线面积(AUC)为NaN的问题
我基于CNN实现图像数据集分类任务时,遇到ROC曲线面积(AUC)为NaN的问题,相关代码如下:
#Package Initilize import numpy as np from sklearn import metrics import matplotlib.pyplot as plt import tensorflow as tf import keras from keras.preprocessing import image from keras.models import Sequential from keras.layers import Convolution2D from keras.layers import MaxPooling2D from keras.layers import Flatten from keras.layers import Dense from keras.layers import Dropout train_datagen = image.ImageDataGenerator( rescale=1/255, shear_range = 0.3, zoom_range = 0.3, horizontal_flip = True, ) validation_datagen = image.ImageDataGenerator( rescale = 1/255 ) target_size = (100,100,3) train = train_datagen.flow_from_directory( 'Dataset/Train', target_size = target_size[:-1], batch_size = 32, class_mode = 'categorical' ) validation = validation_datagen.flow_from_directory( 'Dataset/Validation', target_size = target_size[:-1], batch_size = 32, class_mode = 'categorical' ) test = validation_datagen.flow_from_directory( 'Dataset/Test', target_size = target_size[:-1], batch_size = 32, shuffle = False, class_mode = 'categorical' ) input_layer = keras.layers.Input(shape=target_size) #Model Define conv2d_1 = keras.layers.Conv2D(filters=64, kernel_size=(3,3), strides=1, padding='same', activation='relu', kernel_initializer='he_normal')(input_layer) batchnorm_1 = keras.layers.BatchNormalization()(conv2d_1) maxpool1=keras.layers.MaxPool2D(pool_size=(2,2))(batchnorm_1) conv2d_2 = keras.layers.Conv2D(filters=32, kernel_size=(3,3), strides=1, padding='same', activation='relu',kernel_initializer='he_normal')(maxpool1) batchnorm_2 = keras.layers.BatchNormalization()(conv2d_2) maxpool2=keras.layers.MaxPool2D(pool_size=(2,2))(batchnorm_2) flatten = keras.layers.Flatten()(maxpool2) dense_1 = keras.layers.Dense(256, activation='relu')(flatten) dense_2 = keras.layers.Dense(n_classes, activation='softmax')(dense_1) dense_3 = keras.layers.Dense(n_classes, activation='softmax')(dense_2) model = keras.models.Model(input_layer, dense_3) #Compile Define model.compile(optimizer=keras.optimizers.Adam(0.001), loss='categorical_crossentropy', metrics=['acc']) model.summary() #Fit the model history = model.fit_generator(generator=train, validation_data=validation, epochs=2) #ROC Curve Define x, y = validation.next() prediction = model.predict(x) predict_label1 = np.argmax(prediction, axis=-1) true_label1 = np.argmax(y, axis=-1) y = np.array(true_label1) scores = np.array(predict_label1) fpr, tpr, thresholds = metrics.roc_curve(y, scores, pos_label=9) roc_auc = metrics.auc(fpr, tpr) plt.figure() lw = 2 plt.plot(fpr, tpr, color='darkorange', lw=lw, label='ROC curve (area = %0.2f)' % roc_auc) plt.plot([0, 1], [0, 1], color='navy', lw=lw, linestyle='--') plt.xlim([0.0, 1.0]) plt.ylim([0.0, 1.05]) plt.xlabel('False Positive Rate') plt.ylabel('True Positive Rate') plt.title('Receiver operating characteristic (ROC)') plt.legend(loc="lower right") plt.show()
问题截图显示ROC曲线面积为NaN。
核心原因及解决办法
1. 仅使用单批次数据,样本分布极端
- 问题:通过
validation.next()只获取了一个批次的验证数据,该批次中可能完全没有pos_label=9的样本,或所有样本都是该类别,导致ROC计算无法生成有效的FPR/TPR点,最终AUC为NaN。 - 解决:使用完整的验证集数据,而不是单批次:
# 重置生成器到起始位置,避免数据重复 validation.reset() # 获取完整验证集的预测结果 predictions = model.predict(validation, verbose=1) # 直接获取验证集所有真实标签 true_labels = validation.classes
2. 传入离散类别标签而非概率分数
- 问题:用
np.argmax(prediction, axis=-1)得到的是离散类别索引,ROC曲线需要模型输出的**类别概率(置信度)**作为评分依据,离散标签无法反映模型对类别的置信程度,会引发计算异常。 - 解决:传入目标正类的预测概率值:
# 取pos_label=9对应的预测概率作为评分 scores = predictions[:, 9]
3. 模型结构冗余且未定义类别数
- 问题:代码中
n_classes未定义却直接使用;连续两层Dense(n_classes, activation='softmax')完全冗余,会干扰模型输出的合理性。 - 解决:
- 从训练生成器中获取类别数:
n_classes = train.num_classes - 移除冗余的
dense_3层,用dense_2作为输出层:dense_2 = keras.layers.Dense(n_classes, activation='softmax')(dense_1) model = keras.models.Model(input_layer, dense_2)
- 从训练生成器中获取类别数:
4. 训练轮次过少,模型未收敛
- 问题:仅训练2轮,模型尚未学到有效特征,输出的预测结果可能完全随机或极端,导致ROC计算异常。
- 解决:增加训练轮次,同时加入早停机制防止过拟合:
from keras.callbacks import EarlyStopping # 当验证集准确率连续3轮不提升时停止训练,恢复最优权重 early_stop = EarlyStopping(monitor='val_acc', patience=3, restore_best_weights=True) history = model.fit_generator(generator=train, validation_data=validation, epochs=20, callbacks=[early_stop])
5. 多分类任务的ROC处理方式错误
- 问题:如果是多分类任务,直接用二分类的
roc_curve方法会导致计算异常,需要采用One-vs-Rest等多分类ROC计算方式。 - 解决:可以循环计算每个类别的ROC,或使用
roc_auc_score的多分类模式:from sklearn.metrics import roc_auc_score # 采用One-vs-Rest模式计算多分类AUC roc_auc = roc_auc_score(true_labels, predictions, multi_class='ovr')
内容的提问来源于stack exchange,提问作者Imdadul Haque
相关产品推荐
相关产品推荐

