用于二元分类的 Resnet50 预测所有图像属于同一类

问题描述 投票:0回答:1

我正在使用 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

"""Trying out the Keras Generators"""


def create_generators(valid_perc, test_perc, shuffle=False,preprocess_func=None):
    
    # Split data into train and temp sets (70% train, 30% temp)
    train_data, temp_data = train_test_split(df_train, test_size=valid_perc, random_state=42)
    # Split temp data into validation and test sets (15% validation, 15% test)
    valid_data, test_data = train_test_split(temp_data, test_size=test_perc, random_state=42)
    


    # rescale changes pixels from 1-255 integers to 0-1 floats suitable for neural nets
    rescale = 1./255
    if preprocess_func is not None:
        # no need to rescale if using Keras in-built ResNet50 preprocess_func
        rescale = None

    train_datagen = ImageDataGenerator(
        rescale = rescale, 
        preprocessing_function=preprocess_func
    )

    # Keras has this two-part process of defining generators. 
    # First the generic properties above, then the actual generators with filenames and all.
    train_generator = train_datagen.flow_from_dataframe(
        dataframe=train_data,
        directory=train_dir,
        x_col="filename", # the name of column containing image filename in dataframe
        y_col="classes", # the y-col in dataframe
        batch_size=batch_size, 
        shuffle=shuffle,
        class_mode="binary", # categorical if multiple. then y_col can be list or tuple also 
        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 个类别。

#the total number of images we have:
train_size = len(train_generator.filenames)
#train_steps is how many steps per epoch Keras runs the genrator. One step is batch_size*images
train_steps = len(train_generator)
#use 2* number of images to get more augmentations in. some do, some dont. up to you
#same for the validation set
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":
        #the full pre-trained model is fine-tuned in this case
        for layer in base_model.layers:
            layer.trainable = True
    else:
        #if not all should be trainable, first set them all as non-trainable (fixed)
        for layer in base_model.layers:
            layer.trainable = False
        #and finally set the last N layers as trainable
        #idea is to re-use higher level features and fine-tune the finer details
        for layer in base_model.layers[-trainable_layer_count:]:
            layer.trainable = True
    print("base model has {} layers".format(len(base_model.layers)))
    #here on it is the fully custom classification on top of pre-trained layers above
    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)
    #doing binary prediction, so just 1 neuron is enough
    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'])

enter image description here

enter image description here

在我对验证数据进行预测之后:

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

# Assuming you have the ground truth labels in df_valid["DND_label"] and the predicted labels in df_valid["prediction"]
threshold = 0.5
df_valid['probability'] = predictions
df_valid['category'] = np.where(df_valid['probability'] > threshold, 1,0)
# Calculate confusion matrix
conf_matrix = confusion_matrix(df_valid["DND_label"], df_valid["category"])

# Print confusion matrix
print("Confusion Matrix:")
print(conf_matrix)

这是我得到的混淆矩阵:

混淆矩阵: [[30 0] [37 0]]

我尝试做一些改变,但仍然得到相同的结果,任何帮助!

问题是由于数据不平衡还是其他原因造成的?

谢谢您的帮助

python binary classification resnet
1个回答
0
投票

您是否尝试过对最终的 Dense 层使用 softmax 激活?

尝试更换:

final_output = Dense(1, activation='sigmoid', name='final_output')(x)

与:

final_output = Dense(2, activation='softmax', name='final_output')(x)

然后你只需要过滤概率最高的类别作为你的最终预测。

© www.soinside.com 2019 - 2024. All rights reserved.