为什么我的模型始终保持精确的0.5 AUC?

时间:2019-05-03 11:20:30

标签: python keras scikit-learn auc

我目前正在做一个项目,需要预测一组图像中的眼部疾病。我正在使用Keras内置应用程序。在VGG16和VGG19上我获得了不错的结果,但是在Xception架构上,我一直获得每个周期正好为0.5的AUC。

我尝试了不同的优化程序和学习率,但是没有任何效果。通过从RMSProp优化器切换到Adam优化器,我解决了VGG19的相同问题,但是我无法在Xception上使用它。

   def buildModel():
    from keras.models import Model
    from keras.layers import Dense, Flatten
    from keras.optimizers import adam

    input_model = applications.xception.Xception(
        include_top=False,
        weights='imagenet',
        input_tensor=None,
        input_shape=input_sizes["xception"],
        pooling=None,
        classes=2)

    base_model = input_model

    x = base_model.output
    x = Flatten()(x)
    predictions = Dense(2, activation='softmax')(x)

    model = Model(inputs=base_model.input, outputs=predictions)
    for layer in base_model.layers:
        layer.trainable = False

    model.compile(optimizer=adam(lr=0.01), loss='binary_crossentropy', metrics=['accuracy'])

    return model


class Histories(keras.callbacks.Callback):

    def __init__(self, val_data):
        super(Histories, self).__init__()
        self.x_batch = []
        self.y_batch = []
        for i in range(len(val_data)):
            x, y = val_data.__getitem__(i)
            self.x_batch.extend(x)
            self.y_batch.extend(np.ndarray.astype(y, int))
        self.aucs = []
        self.specificity = []
        self.sensitivity = []
        self.losses = []
        return

    def on_train_begin(self, logs={}):
        initFile("results/xception_results_adam_3.txt")
        return

    def on_train_end(self, logs={}):
        return

    def on_epoch_begin(self, epoch, logs={}):
        return

    def on_epoch_end(self, epoch, logs={}):
        self.losses.append(logs.get('loss'))
        y_pred = self.model.predict(np.asarray(self.x_batch))
        con_mat = confusion_matrix(np.asarray(self.y_batch).argmax(axis=-1), y_pred.argmax(axis=-1))
        tn, fp, fn, tp = con_mat.ravel()
        sens = tp/(tp+fn)
        spec = tn/(tn+fp)
        auc_score = roc_auc_score(np.asarray(self.y_batch).argmax(axis=-1), y_pred.argmax(axis=-1))
        print("Specificity: %f Sensitivity: %f AUC: %f"%(spec, sens, auc_score))
        print(con_mat)
        self.sensitivity.append(sens)
        self.specificity.append(spec)
        self.aucs.append(auc_score)
        writeToFile("results/xception_results_adam_3.txt", epoch, auc_score, spec, sens, self.losses[epoch])
        return


# What follows is data from the Jupyter Notebook that I actually use to evaluate
#%% Initialize data
trainDirectory =      'RetinaMasks/train'
valDirectory =        'RetinaMasks/val'
testDirectory =       'RetinaMasks/test'

train_datagen = ImageDataGenerator(rescale=1. / 255)
test_datagen = ImageDataGenerator(rescale=1. / 255)

train_generator = train_datagen.flow_from_directory(
    trainDirectory,
    target_size=(299, 299),
    batch_size=16,
    class_mode='categorical')

validation_generator = test_datagen.flow_from_directory(
    valDirectory,
    target_size=(299, 299),
    batch_size=16,
    class_mode='categorical')

test_generator = test_datagen.flow_from_directory(
    testDirectory,
    target_size=(299, 299),
    batch_size=16,
    class_mode='categorical')

#%% Create model
model = buildModel("xception")

#%% Initialize metrics
from keras.callbacks import EarlyStopping
from MetricsCallback import Histories
import keras

metrics = Histories(validation_generator)
es = EarlyStopping(monitor='val_loss', 
                   min_delta=0, 
                   patience=20,
                   verbose=0, 
                   mode='auto', 
                   baseline=None, 
                   restore_best_weights=False)

mcp = keras.callbacks.ModelCheckpoint("saved_models/xception.adam.lr0.1_{epoch:02d}.hdf5", 
                                      monitor='val_loss', 
                                      verbose=0, 
                                      save_best_only=False, 
                                      save_weights_only=False, 
                                      mode='auto',
                                      period=1)

#%% Train model
from StaticDataAugmenter import superDirectorySize

history = model.fit_generator(
    train_generator,
    steps_per_epoch=superDirectorySize(trainDirectory) // 16,
    epochs=100,
    validation_data=validation_generator,
    validation_steps=superDirectorySize(valDirectory) // 16,
    callbacks=[metrics, es, mcp],
    workers=8,
    shuffle=False
)

老实说,我不知道是什么原因导致了这种行为,或者如何防止这种行为。预先谢谢您,我为冗长的代码片段表示歉意:)

4 个答案:

答案 0 :(得分:3)

您的学习率太大。 尝试降低学习率。

我也遇到过类似的情况,这种情况发生在转学过程中。如果采用二进制分类,则在多个时期内将AUC扩展为0.5,意味着您的卷积神经网络不会学习任何东西。

使用0.0001,0.00001,0.000001的学习率。

我非常有信心,如果您降低学习速度,您的问题将得到解决:)。

答案 1 :(得分:0)

AUC为0.5表示您的网络随机猜测输出,这意味着它没有学到任何东西。例如here,已经对此进行了讨论。

如Timbus Calin所建议的那样,您可以对学习率进行“线性搜索”(从0.000001开始),然后将学习率提高10。

我建议您直接从随机搜索开始,在该搜索中,您不仅要尝试优化学习率,而且还要尝试优化其他超参数,例如批次大小。在此paper中了解有关随机搜索的更多信息。

答案 2 :(得分:0)

您没有正确计算AUC,目前您有:

auc_score = roc_auc_score(np.asarray(self.y_batch).argmax(axis=-1), y_pred.argmax(axis=-1))
从模型产生的(概率)分数计算出AUC。模型输出的argmax不提供分数,但提供类标签。正确的函数调用是:

auc_score = roc_auc_score(np.asarray(self.y_batch).argmax(axis=-1), y_pred[:, 1])

请注意,计算ROC所需的分数是肯定类别的概率,这是softmax输出的第二个元素。这就是为什么仅将预测的第二列用于进行AUC的原因。

答案 3 :(得分:0)

那呢?

   def buildModel():
    from keras.models import Model
    from keras.layers import Dense, Flatten
    from keras.optimizers import adam

    input_model = applications.xception.Xception(
        include_top=False,
        weights='imagenet',
        input_tensor=None,
        input_shape=input_sizes["xception"],
        pooling='avg',  # 1
        classes=2)

    base_model = input_model

    x = base_model.output
    # x = Flatten()(x)  # 2
    predictions = Dense(2, activation='softmax')(x)

    model = Model(inputs=base_model.input, outputs=predictions)
    for layer in base_model.layers:
        layer.trainable = False

    model.compile(optimizer=adam(lr=0.01), 
                  loss='categorical_crossentropy',  # 3
                  metrics=['accuracy'])

    return model


class Histories(keras.callbacks.Callback):

    def __init__(self, val_data):
        super(Histories, self).__init__()
        self.x_batch = []
        self.y_batch = []
        for i in range(len(val_data)):
            x, y = val_data.__getitem__(i)
            self.x_batch.extend(x)
            self.y_batch.extend(np.ndarray.astype(y, int))
        self.aucs = []
        self.specificity = []
        self.sensitivity = []
        self.losses = []
        return

    def on_train_begin(self, logs={}):
        initFile("results/xception_results_adam_3.txt")
        return

    def on_train_end(self, logs={}):
        return

    def on_epoch_begin(self, epoch, logs={}):
        return

    def on_epoch_end(self, epoch, logs={}):
        self.losses.append(logs.get('loss'))
        y_pred = self.model.predict(np.asarray(self.x_batch))
        con_mat = confusion_matrix(np.asarray(self.y_batch).argmax(axis=-1), y_pred.argmax(axis=-1))
        tn, fp, fn, tp = con_mat.ravel()
        sens = tp/(tp+fn)
        spec = tn/(tn+fp)
        auc_score = roc_auc_score(np.asarray(self.y_batch).argmax(axis=-1), y_pred.argmax(axis=-1))
        print("Specificity: %f Sensitivity: %f AUC: %f"%(spec, sens, auc_score))
        print(con_mat)
        self.sensitivity.append(sens)
        self.specificity.append(spec)
        self.aucs.append(auc_score)
        writeToFile("results/xception_results_adam_3.txt", epoch, auc_score, spec, sens, self.losses[epoch])
        return


# What follows is data from the Jupyter Notebook that I actually use to evaluate
#%% Initialize data
trainDirectory =      'RetinaMasks/train'
valDirectory =        'RetinaMasks/val'
testDirectory =       'RetinaMasks/test'

train_datagen = ImageDataGenerator(rescale=1. / 255)
test_datagen = ImageDataGenerator(rescale=1. / 255)

train_generator = train_datagen.flow_from_directory(
    trainDirectory,
    target_size=(299, 299),
    batch_size=16,
    class_mode='categorical')

validation_generator = test_datagen.flow_from_directory(
    valDirectory,
    target_size=(299, 299),
    batch_size=16,
    class_mode='categorical')

test_generator = test_datagen.flow_from_directory(
    testDirectory,
    target_size=(299, 299),
    batch_size=16,
    class_mode='categorical')

#%% Create model
model = buildModel("xception")

#%% Initialize metrics
from keras.callbacks import EarlyStopping
from MetricsCallback import Histories
import keras

metrics = Histories(validation_generator)
es = EarlyStopping(monitor='val_loss', 
                   min_delta=0, 
                   patience=20,
                   verbose=0, 
                   mode='auto', 
                   baseline=None, 
                   restore_best_weights=False)

mcp = keras.callbacks.ModelCheckpoint("saved_models/xception.adam.lr0.1_{epoch:02d}.hdf5", 
                                      monitor='val_loss', 
                                      verbose=0, 
                                      save_best_only=False, 
                                      save_weights_only=False, 
                                      mode='auto',
                                      period=1)


#%% Load saved model
from keras.models import load_model
# model = load_model("saved_models/vgg16.10.hdf5")  # 4

#%% Train model
from StaticDataAugmenter import superDirectorySize

history = model.fit_generator(
    train_generator,
    steps_per_epoch=superDirectorySize(trainDirectory) // 16,
    epochs=100,
    validation_data=validation_generator,
    validation_steps=superDirectorySize(valDirectory) // 16,
    callbacks=[metrics, es, mcp],
    workers=8,
    shuffle=False
)

对于1和2,我认为在ReLU之后不使用池化层就使用FC层是没有意义的,请不要尝试使用它,否则可能无济于事。

对于3,为什么当生成器使用class_mode='categorical'时使用BCE?

对于4,如上所述,这意味着您正在加载VGG模型并对其进行训练,而不是使用buildModel()中的Xception。