python - resnet50 迁移学习期间的大规模过拟合
问题描述
这是我第一次尝试用 CNN 做一些事情,所以我可能在做一些非常愚蠢的事情——但不知道我错在哪里......
该模型似乎学习得很好,但验证准确性并没有提高(甚至在第一个 epoch 之后),并且验证损失实际上随着时间的推移而增加。看起来我并没有过度拟合(在 1 个 epoch 之后?) - 我们必须以其他方式离开。
我正在训练一个 CNN 网络——我有大约 100k 种植物(1000 个类别)的图像,并且想要微调 ResNet50 以创建一个多类分类器。图像有各种尺寸,我像这样加载它们:
from keras.preprocessing import image
def path_to_tensor(img_path):
# loads RGB image as PIL.Image.Image type
img = image.load_img(img_path, target_size=(IMG_HEIGHT, IMG_HEIGHT))
# convert PIL.Image.Image type to 3D tensor with shape (IMG_HEIGHT, IMG_HEIGHT, 3)
x = image.img_to_array(img)
# convert 3D tensor to 4D tensor with shape (1, IMG_HEIGHT, IMG_HEIGHT, 3) and return 4D tensor
return np.expand_dims(x, axis=0)
def paths_to_tensor(img_paths):
list_of_tensors = [path_to_tensor(img_path) for img_path in img_paths] #can use tqdm(img_paths) for data
return np.vstack(list_of_tensors)enter code here
数据库很大(不适合内存)并且必须创建我自己的生成器来提供从磁盘读取和扩充的功能。(我知道 Keras 有 .flow_from_directory() - 但我的数据不是这样构造的 - 它只是 100k 图像与 100k 元数据文件混合的转储)。我可能应该创建一个脚本来更好地构建它们,而不是创建我自己的生成器,但问题可能出在其他地方。
下面的生成器版本暂时不做任何扩充 - 只是重新缩放:
def generate_batches_from_train_folder(images_to_read, labels, batchsize = BATCH_SIZE):
#Generator that returns batches of images ('xs') and labels ('ys') from the train folder
#:param string filepath: Full filepath of files to read - this needs to be a list of image files
#:param np.array: list of all labels for the images_to_read - those need to be one-hot-encoded
#:param int batchsize: Size of the batches that should be generated.
#:return: (ndarray, ndarray) (xs, ys): Yields a tuple which contains a full batch of images and labels.
dimensions = (BATCH_SIZE, IMG_HEIGHT, IMG_HEIGHT, 3)
train_datagen = ImageDataGenerator(
rescale=1./255,
#rotation_range=20,
#zoom_range=0.2,
#fill_mode='nearest',
#horizontal_flip=True
)
# needs to be on a infinite loop for the generator to work
while 1:
filesize = len(images_to_read)
# count how many entries we have read
n_entries = 0
# as long as we haven't read all entries from the file: keep reading
while n_entries < (filesize - batchsize):
# start the next batch at index 0
# create numpy arrays of input data (features)
# - this is already shaped as a tensor (output of the support function paths_to_tensor)
xs = paths_to_tensor(images_to_read[n_entries : n_entries + batchsize])
# and label info. Contains 1000 labels in my case for each possible plant species
ys = labels[n_entries : n_entries + batchsize]
# we have read one more batch from this file
n_entries += batchsize
#perform online augmentation on the xs and ys
augmented_generator = train_datagen.flow(xs, ys, batch_size = batchsize)
yield next(augmented_generator)
这就是我定义模型的方式:
def get_model():
# define the model
base_net = ResNet50(input_shape=DIMENSIONS, weights='imagenet', include_top=False)
# Freeze the layers which you don't want to train. Here I am freezing all of them
for layer in base_net.layers:
layer.trainable = False
x = base_net.output
#for resnet50
x = Flatten()(x)
x = Dense(512, activation="relu")(x)
x = Dropout(0.5)(x)
x = Dense(1000, activation='softmax', name='predictions')(x)
model = Model(inputs=base_net.input, outputs=x)
# compile the model
model.compile(
loss='categorical_crossentropy',
optimizer=optimizers.Adam(1e-3),
metrics=['acc'])
return model
因此,我有 1,562,088 个可训练参数用于大约 70k 图像
然后我使用 5 折交叉验证,但该模型不适用于任何折叠,所以我不会在此处包含完整代码,相关位是这样的:
trial_fold = temp_model.fit_generator(
train_generator,
steps_per_epoch = len(X_train_path) // BATCH_SIZE,
epochs = 50,
verbose = 1,
validation_data = (xs_v,ys_v),#valid_generator,
#validation_steps= len(X_valid_path) // BATCH_SIZE,
callbacks = callbacks,
shuffle=True)
我做了各种各样的事情——确保我的生成器确实在工作,尝试通过减小全连接层的大小来玩网络的最后几层,尝试增强——没有任何帮助......
我不认为网络中的参数数量太多——我知道其他人也做了几乎相同的事情并且准确度接近 0.5,但我的模型似乎疯狂地过度拟合。任何关于如何解决这个问题的想法将不胜感激!
更新1:
我决定停止重新发明东西并按文件排序以使用 .flow_from_directory() 过程。为了确保我导入了正确的格式(由下面的 Ioannis Nasios 评论触发) - 我确保来自 keras 的 resnet50 应用程序的 preprocessing_unit()。
我还决定检查模型是否真的产生了有用的东西——我为我的数据集计算了瓶颈特征,然后使用随机森林来预测类。它确实有效,我的准确度约为 0.4
所以,我想我的图像输入格式肯定有问题。作为下一步,我将微调模型(使用新的顶层),看看问题是否仍然存在......
更新 2:
我认为问题出在图像预处理上。我最终没有进行微调,只是提取了瓶颈层并训练了 linear_SVC() - 得到了大约 60% 的训练和大约 45% 的测试数据集的准确度。
解决方案
您需要在 ImageDataGenerator 中使用 preprocessing_function 参数。
train_datagen = ImageDataGenerator(preprocessing_function=keras.applications.resnet50.preprocess_input)
这将确保您的图像按照您正在使用的预训练网络的预期进行预处理。
推荐阅读
- c# - 是否可以跳过一行
- bounding-box - 如何使用 Revit API 从 Revit Element 获取边界框,然后调用该边界框的中心
- react-native - 反应原生错误
- math - 10,25,50,100,250,500,1000 如何找到这个序列方程来绘制在图中
- python - 带有 Pushover 的 Python:没有名为 pushover 的模块
- alexa - Alexa - 一旦音频开始,卡片标题和描述就会消失
- javascript - 如何从json数据中过滤子菜单并绑定到angular2中的html
- maven - 当父 POM 未定义该属性时插件配置中的默认属性值
- javascript - 为什么 '\\\[' 等于 '\\[' ?反斜杠如何在字符串中工作?
- android - 在不存储用户生物特征的情况下使用 Touch ID 或 Face ID