【问题标题】:Why does my model predict the same label?为什么我的模型预测相同的标签?
【发布时间】:2020-01-17 12:31:09
【问题描述】:

我正在训练一个小型网络,训练似乎很顺利,val loss 减少了,我达到了 80 左右的验证准确度,一旦没有更多改进它实际上停止训练(耐心 = 10)。它训练了 40 个 epoch。但是,它一直只为每个测试图像预测一个类别!我尝试随机初始化卷积层,添加正则化器,从 Adam 切换到 SGD,添加剪辑值,添加 dropout。我也切换到了 softmax(我只有两个标签,但我看到了一些关于使用带有 2 个神经元的 softmax 和 Dense 层的建议)。其中一些或其中之一有助于过度拟合,但对预测问题没有任何作用。 数据是平衡的,虽然它是一个小数据集,所以如果它也为评估集预测相同的标签,那么它达到 80% 是没有意义的。

我的模型有什么问题,我该如何解决?欢迎任何cmets。

#Import some packages to use
import cv2
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from keras.preprocessing.image import ImageDataGenerator
import os
from keras.regularizers import l2
from keras.callbacks import EarlyStopping, ModelCheckpoint, ReduceLROnPlateau
from keras.layers.core import Dense, Dropout, Flatten
from keras.layers.convolutional import Conv2D, MaxPooling2D
from keras.initializers import RandomNormal

os.environ["CUDA_VISIBLE_DEVICES"]="0"

epochs = 200
callbacks = []
#schedule = None
decay = 0.0

earlyStopping = EarlyStopping(monitor='val_loss', patience=10, verbose=0, mode='min')
mcp_save = ModelCheckpoint('.mdl_wts.hdf5', save_best_only=True, monitor='val_loss', mode='min')
reduce_lr_loss = ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=3, verbose=1, epsilon=1e-5, mode='min')

train_dir = '/home/d/Desktop/s/data/train'
eval_dir = '/home/d/Desktop/s/data/eval'
test_dir = '/home/d/Desktop/s/data/test'



# create a data generator
train_datagen = ImageDataGenerator(rescale=1./255,   #Scale the image between 0 and 1
                                    rotation_range=40,
                                    width_shift_range=0.2,
                                    height_shift_range=0.2,
                                    shear_range=0.2,
                                    zoom_range=0.2,
                                    horizontal_flip=True,)

val_datagen = ImageDataGenerator(rescale=1./255)  #We do not augment validation data. we only perform rescale

test_datagen = ImageDataGenerator(rescale=1./255)  #We do not augment validation data. we only perform rescale

# load and iterate training dataset
train_generator = train_datagen.flow_from_directory(train_dir,  target_size=(224,224),class_mode='categorical', batch_size=16, shuffle='True', seed=42)
# load and iterate validation dataset
val_generator = val_datagen.flow_from_directory(eval_dir,  target_size=(224,224),class_mode='categorical', batch_size=16, shuffle='True', seed=42)
# load and iterate test dataset
test_generator = test_datagen.flow_from_directory(test_dir,  target_size=(224,224), class_mode=None, batch_size=1, shuffle='False', seed=42)
#We will use a batch size of 32. Note: batch size should be a factor of 2.***4,8,16,32,64...***
#batch_size = 4



#from keras import layers
from keras import models
from keras import optimizers
#from keras.layers import Dropout
#from keras.preprocessing.image import ImageDataGenerator
from keras.preprocessing.image import img_to_array, load_img

model = models.Sequential()
model.add(Conv2D(64, (3, 3), activation='relu', name='block1_conv1', kernel_initializer=RandomNormal(
        mean=0.0, stddev=0.05), bias_initializer=RandomNormal(mean=0.0, stddev=0.05), input_shape=(224, 224, 3)))
model.add(Conv2D(64, (3, 3), activation='relu', name='block1_conv2', kernel_initializer=RandomNormal(
        mean=0.0, stddev=0.05), bias_initializer=RandomNormal(mean=0.0, stddev=0.05)))
model.add(MaxPooling2D((2, 2)))
model.add(Dropout(0.2))
model.add(Conv2D(128, (3, 3), activation='relu', name='block2_conv1', kernel_initializer=RandomNormal(
        mean=0.0, stddev=0.05), bias_initializer=RandomNormal(mean=0.0, stddev=0.05)))
model.add(Conv2D(128, (3, 3), activation='relu', name='block2_conv2',kernel_initializer=RandomNormal(
        mean=0.0, stddev=0.05), bias_initializer=RandomNormal(mean=0.0, stddev=0.05)))
model.add(MaxPooling2D((2, 2), name='block2_pool'))
model.add(Dropout(0.2))
model.add(Conv2D(256, (3, 3), activation='relu', name='block3_conv1', kernel_initializer=RandomNormal(
        mean=0.0, stddev=0.05), bias_initializer=RandomNormal(mean=0.0, stddev=0.05)))
model.add(Conv2D(256, (3, 3), activation='relu', name='block3_conv2', kernel_initializer=RandomNormal(
        mean=0.0, stddev=0.05), bias_initializer=RandomNormal(mean=0.0, stddev=0.05)))
model.add(Conv2D(256, (3, 3), activation='relu', name='block3_conv3', kernel_initializer=RandomNormal(
        mean=0.0, stddev=0.05), bias_initializer=RandomNormal(mean=0.0, stddev=0.05)))
model.add(MaxPooling2D((2, 2), name='block3_pool'))
model.add(Dropout(0.2))
#model.add(layers.Conv2D(512, (3, 3), activation='relu', name='block4_conv1'))
#model.add(layers.Conv2D(512, (3, 3), activation='relu', name='block4_conv2'))
#model.add(layers.Conv2D(512, (3, 3), activation='relu', name='block4_conv3'))
#model.add(layers.MaxPooling2D((2, 2), name='block4_pool'))
model.add(Flatten())
model.add(Dense(256, kernel_regularizer=l2(0.01), bias_regularizer=l2(0.01), activation='relu', kernel_initializer='he_uniform'))
model.add(Dropout(0.5))
model.add(Dense(2, kernel_regularizer=l2(0.01), bias_regularizer=l2(0.01), activation='softmax'))

#Lets see our model
model.summary()

#We'll use the RMSprop optimizer with a learning rate of 0.0001
#We'll use binary_crossentropy loss because its a binary classification
#model.compile(loss='binary_crossentropy', optimizer=optimizers.SGD(lr=1e-5, momentum=0.9), metrics=['acc'])
model.compile(loss='categorical_crossentropy',
                   #optimizer=optimizers.Adadelta(lr=1.0, rho=0.95, epsilon=1e-08, decay=decay),
                    optimizer=optimizers.SGD(lr= 0.0001, clipvalue = 0.5, decay=1e-6, momentum=0.9, nesterov=True),
              metrics=['accuracy'])



#The training part
#We train for 64 epochs with about 100 steps per epoch
history = model.fit_generator(train_generator,
                              steps_per_epoch=train_generator.n // train_generator.batch_size,
                              epochs=epochs,
                              validation_data=val_generator,
                              validation_steps=val_generator.n // val_generator.batch_size,
                              callbacks=[earlyStopping, mcp_save]) #, reduce_lr_loss])


#Save the model
model.save_weights('/home/d/Desktop/s/categorical_weights.h5')
model.save('/home/d/Desktop/s/categorical_model_keras.h5')

#lets plot the train and val curve
#get the details form the history object
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']

epochs = range(1, len(acc) + 1)

#Train and validation accuracy
plt.plot(epochs, acc, 'b', label='Training accuracy')
plt.plot(epochs, val_acc, 'r', label='Validation accuracy')
plt.title('Training and Validation accuracy')
plt.legend()

plt.figure()
#Train and validation loss
plt.plot(epochs, loss, 'b', label='Training loss')
plt.plot(epochs, val_loss, 'r', label='Validation loss')
plt.title('Training and Validation loss')
plt.legend()

plt.show()

model.evaluate_generator(generator=val_generator, steps=val_generator.n // val_generator.batch_size)

STEP_SIZE_TEST=test_generator.n//test_generator.batch_size
test_generator.reset()
pred=model.predict_generator(test_generator,
steps=STEP_SIZE_TEST,
verbose=1)

predicted_class_indices=np.argmax(pred,axis=1)

labels = (train_generator.class_indices)
np.save('/home/d/Desktop/s/classes', labels)

labels = dict((v,k) for k,v in labels.items())
predictions = [labels[k] for k in predicted_class_indices]

filenames=test_generator.filenames
results=pd.DataFrame({"Filename":filenames,
                      "Predictions":predictions})
results.to_csv("categorical_results.csv",index=False)

【问题讨论】:

    标签: keras neural-network deep-learning conv-neural-network training-data


    【解决方案1】:

    可能导致这种行为的问题之一是数据集不平衡。你的模型发现,如果每次都预测优势类,就会得到很好的结果。

    有很多方法可以解决不平衡数据集。这是一个很好的tutorial。 最简单但功能强大的解决方案之一是,如果错误地预测了较小的类别,则对您的损失施加更高的惩罚。这可以通过在fitfit_generator函数中设置参数class_weight在keras中实现。

    可以是示例字典:

    class_weight = {0: 0.75, 1: 0.25}  # does not necessarily add to up 1.
    history = model.fit_generator(train_generator,
                                  steps_per_epoch=train_generator.n // train_generator.batch_size,
                                  epochs=epochs,
                                  class_weight= class_weight,  # this is the important part
                                  validation_data=val_generator,
                                  validation_steps=val_generator.n // val_generator.batch_size,
                                  callbacks=[earlyStopping, mcp_save]) #, reduce_lr_loss])
    

    【讨论】:

    • 感谢您的提示,我正在尝试这个,但我只是仔细检查了一下,我的数据集非常平衡(实际上几乎所有集合的 50% 50% - 基本上完全平衡)所以它一定是什么否则,尴尬的是,对于验证集,如果网络只输出一个类并且数据集是平衡的,我如何获得 %80 的准确度......
    • 在这种情况下,验证集没有以分层方式进行拆分。这意味着训练和验证之间的分离使其不平衡。这将影响您的训练,因为您的回调取决于 val_loss。你能确保你的验证文件夹中的类是平衡的吗?
    • 是的,我仔细检查过,val 集也是平衡的:对于训练,我有 32354 / 31681 对于 val,我每个班级有 9092 / 9860 个样本
    • 我认为数据集的准备方式是,他们对每个视频片段采样大致相同数量的帧,因此即使我在视频级别拆分数据集,它们都非常平衡
    【解决方案2】:

    除了 Coderji 的回答之外,使用stratified k-fold cross-validation 来对抗类不平衡也可能被证明是有利的,k = 5 是常见的做法。这基本上将您的数据集拆分为k 拆分,就像常规交叉验证一样,但也会对这些拆分进行分层。在类不平衡的情况下,这些拆分中的每一个都包含过采样/欠采样的类,以补偿它们在数据集中出现的较低/较高的发生率。

    到目前为止Keras does not have it's own way 使用分层的k-fold 交叉验证。相反,建议使用sklearn's StratifiedKFoldThis article 详细概述了如何在 Keras 中实现这一点, 其要点是:

    from sklearn.model_selection import StratifiedKFold# Instantiate the cross validator
    skf = StratifiedKFold(n_splits=kfold_splits, shuffle=True)# Loop through the indices the split() method returns
    for index, (train_indices, val_indices) in enumerate(skf.split(X, y)):
        print "Training on fold " + str(index+1) + "/10..."    # Generate batches from indices
        xtrain, xval = X[train_indices], X[val_indices]
        ytrain, yval = y[train_indices], y[val_indices]    # Clear model, and create it
        model = None
        model = create_model()
    
        # Debug message I guess
        # print "Training new iteration on " + str(xtrain.shape[0]) + " training samples, " + str(xval.shape[0]) + " validation samples, this may be a while..."
    
        history = train_model(model, xtrain, ytrain, xval, yval)
        accuracy_history = history.history['acc']
        val_accuracy_history = history.history['val_acc']
        print "Last training accuracy: " + str(accuracy_history[-1]) + ", last validation accuracy: " + str(val_accuracy_history[-1])
    
    • create_model() 返回一个编译好的 Keras 模型
    • train_model() 返回最后一个 history object 的最后一个 model.fit() 操作

    【讨论】:

    • 感谢您的提示,但是我不能直接使用它来解决我的问题,因为我需要在视频级别拆分,所以我自己做。
    猜你喜欢
    • 2016-01-30
    • 1970-01-01
    • 2020-07-22
    • 1970-01-01
    • 2021-04-15
    • 2018-06-11
    • 1970-01-01
    • 2020-09-12
    • 2020-06-26
    相关资源
    最近更新 更多