ResNet50二分类模型全部预测为同一类的问题排查求助
使用ResNet50做二分类时模型全预测为0类的问题排查
我正在使用ResNet50模型进行二分类任务,混淆矩阵显示模型将所有图像都预测为0类,我的代码是否存在错误?
import os import pandas as pd from keras.applications.resnet50 import preprocess_input from sklearn.model_selection import train_test_split from keras.preprocessing.image import ImageDataGenerator import matplotlib.pyplot as plt import PIL from sklearn.preprocessing import LabelEncoder import numpy as np from keras.models import Sequential, Model from tqdm.auto import tqdm tqdm.pandas() train_dir = "/content/drive/MyDrive/DefectDetection/splitDefectData" file_list = os.listdir(train_dir) DEFECTUEUX = "Defectueux" # 有缺陷类 NONDEFECTUEUX = "NonDefectueux" # 无缺陷类 TRAIN_TOTAL = len(file_list) labels = [] df_train = pd.DataFrame() idx = 0 img_sizes = [] for filename in file_list: if "NonDefectueux" in filename: labels.append(NONDEFECTUEUX) else: labels.append(DEFECTUEUX) img = PIL.Image.open(f"{train_dir}/{filename}") img.close() idx += 1 df_train["filename"] = file_list df_train["classes"] = labels label_encoder = LabelEncoder() df_train["DND_label"] = label_encoder.fit_transform(df_train["classes"]) # {Defectueux:0, NonDefectueux:1} batch_size = 10 img_size = 224 epochs = 10 """测试Keras生成器""" def create_generators(valid_perc, test_perc, shuffle=False,preprocess_func=None): # 将数据分为训练集和临时集(70%训练,30%临时) train_data, temp_data = train_test_split(df_train, test_size=valid_perc, random_state=42) # 将临时集分为验证集和测试集(15%验证,15%测试) valid_data, test_data = train_test_split(temp_data, test_size=test_perc, random_state=42) # 归一化将像素从0-255整数转换为0-1浮点数,适合神经网络输入 rescale = 1./255 if preprocess_func is not None: # 如果使用Keras内置的ResNet50预处理函数,则无需归一化 rescale = None train_datagen = ImageDataGenerator( rescale = rescale, preprocessing_function=preprocess_func ) # Keras生成器分为两步定义: # 首先定义通用属性,然后基于文件名等创建实际生成器 train_generator = train_datagen.flow_from_dataframe( dataframe=train_data, directory=train_dir, x_col="filename", # 数据框中包含图像文件名的列名 y_col="classes", # 数据框中的标签列 batch_size=batch_size, shuffle=shuffle, class_mode="binary", # 多分类时用categorical,此时y_col可以是列表或元组 target_size=(img_size,img_size), seed=42 ) valid_generator = train_datagen.flow_from_dataframe( dataframe=valid_data, directory=train_dir, x_col="filename", y_col="classes", batch_size=batch_size, shuffle=shuffle, class_mode="binary", target_size=(img_size,img_size), seed=42 ) test_generator = train_datagen.flow_from_dataframe( dataframe=test_data, directory=train_dir, x_col="filename", y_col="classes", batch_size=batch_size, shuffle=shuffle, class_mode="binary", target_size=(img_size,img_size), seed=42 ) return train_generator, valid_generator, test_generator, train_datagen train_generator, valid_generator, test_generator, train_datagen = create_generators(0.3,0.5,shuffle = True)
运行上述代码后输出:
已验证314个图像文件名,分属2个类别。
已验证67个图像文件名,分属2个类别。
已验证68个图像文件名,分属2个类别。
接着执行模型定义与编译代码:
# 获取图像总数 train_size = len(train_generator.filenames) # train_steps是Keras每个 epoch 运行生成器的步数,一步对应batch_size张图像 train_steps = len(train_generator) # 用2倍图像数可以获得更多增强样本,可选操作 # 验证集同理 valid_size = len(valid_generator.filenames) valid_steps = len(valid_generator) from keras.regularizers import l2 from keras.preprocessing.image import ImageDataGenerator from keras.models import Sequential, load_model from keras.layers import ( Dropout, Dense, Input, GlobalAveragePooling2D) from keras.applications.resnet50 import ResNet50 def create_model(trainable_layer_count): input_tensor = Input(shape=(img_size, img_size, 3)) base_model = ResNet50(include_top=False,weights='imagenet',input_tensor=input_tensor) if trainable_layer_count == "all": # 全预训练模型将被微调 for layer in base_model.layers: layer.trainable = True else: # 如果不是所有层都可训练,先将所有层设为不可训练(固定) for layer in base_model.layers: layer.trainable = False # 最后将最后N层设为可训练 # 思路是复用高层特征,微调细节 for layer in base_model.layers[-trainable_layer_count:]: layer.trainable = True print("基础模型共有{}层".format(len(base_model.layers))) # 在预训练层之上构建自定义分类头 x = GlobalAveragePooling2D()(base_model.output) x = Dropout(0.5)(x) x = Dense(64, activation='relu', kernel_regularizer=l2(5e-4))(x) x = Dropout(0.5)(x) # 二分类只需要1个神经元 final_output = Dense(1, activation='sigmoid', name='final_output')(x) model = Model(input_tensor, final_output) return model model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
注:此处原问题附带两张训练过程的损失与准确率曲线图片
在验证数据上执行预测的代码:
valid_generator.reset() df_valid = pd.DataFrame() np.set_printoptions(suppress=True) diffs = [] predictions = [] classes = [] DND_labels = [] for filename in tqdm(valid_generator.filenames): img = PIL.Image.open(f'{train_dir}/{filename}') resized = img.resize((img_size, img_size)) np_img = np.array(resized) if "NonDefectueux" in filename: reference = 1 classes.append(NONDEFECTUEUX) else: reference = 0 classes.append(DEFECTUEUX) DND_labels.append(reference) score_predict = model.predict(preprocess_input(np_img[np.newaxis])) diffs.append(abs(reference-score_predict[0][0])) predictions.append(score_predict) df_valid["filename"] = valid_generator.filenames df_valid["classes"] = classes df_valid["DND_label"] = DND_labels df_valid["diff"] = diffs df_valid["prediction"] = predictions from sklearn.metrics import confusion_matrix # 假设真实标签在df_valid["DND_label"],预测标签在df_valid["prediction"] threshold = 0.5 df_valid['probability'] = predictions df_valid['category'] = np.where(df_valid['probability'] > threshold, 1,0) # 计算混淆矩阵 conf_matrix = confusion_matrix(df_valid["DND_label"], df_valid["category"]) # 打印混淆矩阵 print("Confusion Matrix:") print(conf_matrix)
得到的混淆矩阵结果:
Confusion Matrix:
[[30 0]
[37 0]]
我尝试了一些修改,但仍然得到相同的结果,请求帮助!
该问题是由不平衡数据(unbalanced data)导致的还是其他原因?
感谢您的帮助
内容的提问来源于stack exchange,提问作者Hanane
相关产品推荐
相关产品推荐

