小数据集迁移学习的过度拟合

数据挖掘 深度学习 美国有线电视新闻网 训练 过拟合 迁移学习
2022-03-07 08:17:21

我正在使用迁移学习来执行图像分类。

使用的基本模型:Resnet50使用ImageNet数据集 class_1class_2每个类都有 1000 个样本(小数据集)。并且数据集与数据集不相似ImageNet这里使用的数量FC layers是 3 和[1024, 512, 256]. 我使用了drop out0.5 来减少过拟合。

当我用 100 个 epoch 训练模型时,我可以清楚地看到模型过度拟合training accuracy0.9985 和testing accuracy0.875。

使用的 FC 层数是否过多导致了这个过拟合问题?如何使模型更通用?

使用的代码如下所示:

from keras.applications.resnet50 import ResNet50, preprocess_input
from keras.preprocessing.image import ImageDataGenerator
from keras.layers import Dense, Activation, Flatten, Dropout
from keras.models import Sequential, Model 
from keras.optimizers import SGD, Adam
from keras.callbacks import TensorBoard
import keras
import matplotlib.pyplot as plt

HEIGHT = 300
WIDTH = 300
TRAIN_DIR = "/home/ubuntu/dataset/training_set/"
TEST_DIR = "/home/ubuntu/dataset/test_set/"
BATCH_SIZE = 8
class_list = ["class_1", "class_2"]
FC_LAYERS = [1024, 512, 256]
dropout = 0.5
NUM_EPOCHS = 100
BATCH_SIZE = 8

def build_finetune_model(base_model, dropout, fc_layers, num_classes):
    for layer in base_model.layers:
        layer.trainable = False

    x = base_model.output
    x = Flatten()(x)
    for fc in fc_layers:
        print(fc)
        x = Dense(fc, activation='relu')(x)
        x = Dropout(dropout)(x)
    preditions = Dense(num_classes, activation='softmax')(x)
    finetune_model = Model(inputs = base_model.input, outputs = preditions)
    return finetune_model

base_model = ResNet50(weights = 'imagenet',
                       include_top = False,
                       input_shape = (HEIGHT, WIDTH, 3))

train_datagen = ImageDataGenerator(preprocessing_function = preprocess_input,
                                   rotation_range = 90,
                                   horizontal_flip = True,
                                   vertical_flip = False)

test_datagen = ImageDataGenerator(preprocessing_function = preprocess_input,
                                  rotation_range = 90,
                                  horizontal_flip = True,
                                  vertical_flip = False)

train_generator = train_datagen.flow_from_directory(TRAIN_DIR,
                                                    target_size = (HEIGHT, WIDTH),
                                                    batch_size = BATCH_SIZE)

test_generator = test_datagen.flow_from_directory(TEST_DIR,
                                                  target_size = (HEIGHT, WIDTH),
                                                  batch_size = BATCH_SIZE)

finetune_model = build_finetune_model(base_model,
                                      dropout = dropout,
                                      fc_layers = FC_LAYERS,
                                      num_classes = len(class_list))

adam = Adam(lr = 0.00001)
finetune_model.compile(adam, loss="categorical_crossentropy", metrics=["accuracy"])

filepath = "./checkpoints" + "RestNet50" + "_model_weights.h5"
checkpoint = keras.callbacks.ModelCheckpoint(filepath, monitor = ["acc"], verbose= 1, mode = "max")
cb=TensorBoard(log_dir=("/home/ubuntu/"))
callbacks_list = [checkpoint, cb]

print(train_generator.class_indices)

history = finetune_model.fit_generator(generator = train_generator, epochs = NUM_EPOCHS, steps_per_epoch = 100, 
                                       shuffle = True, callbacks=callbacks_list, validation_data = test_generator)

更新 :

  1. 训练后模型生成的权重文件为 2.7 GB。考虑到模型的复杂性,这是否正常?

  2. 我将如何选择steps_per_epoch值?有什么标准吗?

3个回答

首先:

  • 我认为你应该减少FC层数和FC层的节点数,例如256或512的1个FC,或256和512的2个FC。试试这个。

  • 尝试将批量大小设置为 30,并将 epoch 数减少到接近 10 或 20。对于小型数据集来说,100 epoch 太多了。

其次,减少过拟合的方法不止一种:

1- 通过使用诸如翻转、缩放等增强技术来扩大您的数据集。

2- 使用像 dropout 这样的正则化技术(你已经这样做了),但你可以玩 dropout rate。尝试大于或小于 0.5。

3-在您的情况下,一种好的技术是提前停止。在任何时期,当您看到模型过度拟合时,请停止它。

4- 使用交叉验证来训练/测试你的模型。

还有很多。

随时问任何进一步的问题。

我为迁移学习实现了各种架构,并观察到包含 BatchNorm 层(例如 Inception、ResNet、MobileNet)的模型在评估(验证/测试)期间的性能比没有 BatchNorm 层的模型差很多(~30% 与 >95% 的测试准确度相比) (例如 VGG)在我的自定义数据集上。此外,保存瓶颈特征并将其用于分类时不会出现此问题。已经有一些关于这个主题的博客条目、论坛主题、问题和拉取请求,事实证明,BatchNorm 层在冻结时使用的不是新数据集的统计信息,而是原始数据集 (ImageNet) 的统计信息:

假设您正在构建一个计算机视觉模型,但您没有足够的数据,因此您决定使用 Keras 的预训练 CNN 之一并对其进行微调。不幸的是,这样做并不能保证 BN 层内的新数据集的均值和方差与原始数据集的均值和方差相似。请记住,目前,在训练期间,无论 BN 层是否冻结,您的网络都将始终使用小批量统计信息;同样在推理过程中,您将使用先前学习的冻结 BN 层的统计信息。因此,如果您微调顶层,它们的权重将调整为新数据集的均值/方差。然而,在推理过程中,他们将接收到不同比例的数据,因为将使用原始数据集的均值/方差。

引用自http://blog.datumbox.com/the-batch-normalization-layer-of-keras-is-broken/

对我来说解决问题的是冻结所有层,然后解冻所有 BatchNormalization 层,以使它们使用新数据集的统计信息而不是原始统计信息:

# build model
input_tensor = Input(shape=train_generator.image_shape)
base_model = inception_v3.InceptionV3(input_tensor=input_tensor,
                                      include_top=False,
                                      weights='imagenet',
                                      pooling='avg')
x = base_model.output

# freeze all layers in the base model
base_model.trainable = False

# un-freeze the BatchNorm layers
for layer in base_model.layers:
    if "BatchNormalization" in layer.__class__.__name__:
        layer.trainable = True

# add custom layers
x = Dense(1024, activation='relu')(x)
x = Dropout(0.5)(x)
x = Dense(train_generator.num_classes, activation='softmax')(x)

# define new model
model = Model(inputs=input_tensor, outputs=x)

set_learning_phase这也解释了使用冻结层训练模型并使用验证/测试集对其进行评估和保存瓶颈特征(使用 model.predict 内部后端标志设置为0)和在缓存的瓶颈特征上训练分类器之间的性能差异。

更多信息在这里:

拉取请求以更改此行为(不接受): https ://github.com/keras-team/keras/pull/9965

类似的线程: https ://stackoverflow.com/questions/50364706/massive-overfit-during-resnet50-transfer-learning

小心 Keras 批量标准化。你可以试试这段代码:

K.set_learning_phase(0)
input_tensor = Input(shape(img_size, img_size, 3))  
base_model = ResNet50(input_tensor=input_tensor, include_top=False, weights="imagenet", pooling="avg")
x = base_model.output
#Define your own top layers
K.set_learning_phase(1)
x = Dense()
...
x = Dense()
model = Model(input_tensor, x)
for layer  in base_model.layers:
    layer.trainable = False

或者您可以尝试解冻最后几个卷积层,这可能会有所帮助。但是,仍然要小心批量标准化。keras 的迁移学习有很多关于这个问题的讨论。