深度学习:训练集往往是好的,而验证集是不好的

时间:2018-12-10 10:33:37

标签: python tensorflow machine-learning keras deep-learning

我正面临着一个问题,我很难理解为什么我会有这种行为。

我正在尝试使用预先训练的resnet 50(keras)模型进行二进制图像分类,我还构建了一个简单的cnn。我有大约8k大小为200x200的平衡RGB图像,并将该集合分为三个子集(训练70%,验证15%,测试15%)。

我构建了一个生成器,用于根据keras.utils.Sequence向模型提供数据。

我的问题是我的模型倾向于在训练集上学习,但在验证集上,我在预训练的resnet50和简单的cnn上的结果不佳。 我尝试了几种方法来解决此问题,但根本没有改善。

  • 有无训练集(旋转)数据增强
  • 图像在[0,1]之间归一化
  • 有和没有正则化器
  • 学习率的变化

这是获得的结果的示例:

Epoch 1/200
716/716 [==============================] - 320s 447ms/step - loss: 8.6096 - acc: 0.4728 - val_loss: 8.6140 - val_acc: 0.5335

Epoch 00001: val_loss improved from inf to 8.61396, saving model to ../models_saved/resnet_adam_best.h5
Epoch 2/200
716/716 [==============================] - 287s 401ms/step - loss: 8.1217 - acc: 0.5906 - val_loss: 10.9314 - val_acc: 0.4632

Epoch 00002: val_loss did not improve from 8.61396
Epoch 3/200
716/716 [==============================] - 249s 348ms/step - loss: 7.5357 - acc: 0.6695 - val_loss: 11.1432 - val_acc: 0.4657

Epoch 00003: val_loss did not improve from 8.61396
Epoch 4/200
716/716 [==============================] - 284s 397ms/step - loss: 7.5092 - acc: 0.6828 - val_loss: 10.0665 - val_acc: 0.5351

Epoch 00004: val_loss did not improve from 8.61396
Epoch 5/200
716/716 [==============================] - 261s 365ms/step - loss: 7.0679 - acc: 0.7102 - val_loss: 4.2205 - val_acc: 0.5351

Epoch 00005: val_loss improved from 8.61396 to 4.22050, saving model to ../models_saved/resnet_adam_best.h5
Epoch 6/200
716/716 [==============================] - 285s 398ms/step - loss: 6.9945 - acc: 0.7161 - val_loss: 10.2276 - val_acc: 0.5335
....

这是用于将数据加载到我的模型中的类。

 class DataGenerator(keras.utils.Sequence):

    def __init__(self, inputs,
                 labels, img_size,
                 input_shape,
                 batch_size, num_classes,
                 validation=False):

        self.inputs = inputs
        self.labels = labels
        self.img_size = img_size
        self.input_shape = input_shape
        self.batch_size = batch_size
        self.num_classes = num_classes
        self.validation = validation
        self.indexes = np.arange(len(self.inputs))
        self.inc = 0

    def __getitem__(self, index):
        """Generate one batch of data

        Parameters
        ----------
        index :the index from which batch will be taken

        Returns
        -------
        out : a tuple that contains (inputs and labels associated)
        """
        batch_inputs = np.zeros((self.batch_size, *self.input_shape))
        batch_labels = np.zeros((self.batch_size, self.num_classes))

        # Generate data
        for i in range(self.batch_size):
            # choose random index in features

            if self.validation:
                index = self.indexes[self.inc]
                self.inc += 1
                if self.inc == len(self.inputs):
                    self.inc = 0
            else:
                index = random.randint(0, len(self.inputs) - 1)

            batch_inputs[i] = self.rgb_processing(self.inputs[index])
            batch_labels[i] = to_categorical(self.labels[index], num_classes=self.num_classes)
        return batch_inputs, batch_labels

    def __len__(self):
        """Denotes the number of batches per epoch

        Returns
        -------
        out : number of batches per epochs

        """
        return int(np.floor(len(self.inputs) / self.batch_size))


    def rgb_processing(self, path):
        img = load_img(path)
        rgb = img.get_rgb_array()

        if not self.validation:
            if random.choice([True, False]):
                rgb = random_rotation(rgb)

        return rgb/np.max(rgb)


class Models:

    def __init__(self, input_shape, classes):
        self.input_shape = input_shape
        self.classes = classes
        pass

    def simpleCNN(self, optimizer):
        model = Sequential()
        model.add(Conv2D(32, kernel_size=(3, 3),
                         activation='relu',
                         input_shape=self.input_shape))
        model.add(Conv2D(64, (3, 3), activation='relu'))
        model.add(MaxPooling2D(pool_size=(2, 2)))
        model.add(Dropout(0.25))
        model.add(Flatten())
        model.add(Dense(128, activation='relu'))
        model.add(Dropout(0.5))
        model.add(Dense(len(self.classes), activation='softmax'))

        model.compile(loss=keras.losses.binary_crossentropy,
                      optimizer=optimizer,
                      metrics=['accuracy'])

        return model

    def resnet50(self, optimizer):
        model = keras.applications.resnet50.ResNet50(include_top=False,
                                                     input_shape=self.input_shape,
                                                     weights='imagenet')
        model.summary()
        model.layers.pop()
        model.summary()

        for layer in model.layers:
            layer.trainable = False
        output = Flatten()(model.output)
        #I also tried to add dropout layers here with batch normalization but it does not change results   
        output = Dense(len(self.classes), activation='softmax')(output)

        finetuned_model = Model(inputs=model.input,
                                outputs=output)

        finetuned_model.compile(optimizer=optimizer,
                                loss=keras.losses.binary_crossentropy,
                                metrics=['accuracy'])

        return finetuned_model

这是这些函数的调用方式:

train_batches = DataGenerator(inputs=train.X.values,
                              labels=train.y.values,
                              img_size=img_size,
                              input_shape=input_shape,
                              batch_size=batch_size,
                              num_classes=len(CLASSES))

validate_batches = DataGenerator(inputs=validate.X.values,
                                 labels=validate.y.values,
                                 img_size=img_size,
                                 input_shape=input_shape,
                                 batch_size=batch_size,
                                 num_classes=len(CLASSES),
                                 validation=True)

if model_name == "cnn":
    model = models.simpleCNN(optimizer=Adam(lr=0.0001))
elif model_name == "resnet":
    model = models.resnet50(optimizer=Adam(lr=0.0001))

early_stopping = EarlyStopping(patience=15)
checkpointer = ModelCheckpoint(output_name + '_best.h5', verbose=1, save_best_only=True)

        history = model.fit_generator(train_batches, steps_per_epoch=num_train_steps, epochs=epochs,
                                  callbacks=[early_stopping, checkpointer], validation_data=validate_batches,
                                  validation_steps=num_valid_steps)

1 个答案:

答案 0 :(得分:0)

我终于找到了导致这种过度拟合的主要因素。由于我使用了预先训练的模型。我将图层设置为不可训练。因此,我试图将它们设置为可训练的,并且似乎可以解决问题。

       for layer in model.layers:
        layer.trainable = False

我的假设是我的图像与用于训练模型的数据相距太远。

我还在resnet模型的末尾添加了一些辍学和批处理规范化功能。