zl程序教程

您现在的位置是:首页 >  其他

当前栏目

深度学习笔记:使用预训练模型之在线特征提取+数据增强

训练笔记学习数据 模型 深度 在线 增强
2023-09-14 09:15:01 时间

目录

0. 前言

2. 预特征提取 w/o 数据增强, ImageDataGenerator(rescale)

3. 在线特征提取+数据增强+'conv_base.trainable=False'+ ImageDataGenerator(rescale)

4. 在线特征提取+数据增强 + conv_base.trainable = True, ImageDataGenerator(rescale)

5. 预特征提取 w/o 数据增强, ImageDataGenerator(preprocess_input)

6. 在线特征提取+数据增强 + conv_base.trainable = False + ImageDataGenerator(preprocess_input)

7. 在线特征提取+数据增强 + conv_base.trainable = True + ImageDataGenerator(preprocess_input)

8. 总结


0. 前言

本系列介绍如何搭建一个卷积神经网络用于图像分类的深度学习问题,尤其是再训练数据集比较小的场合。通常来说,深度学习需要大量的数据进行训练,尤其是像在图像处理这种通常数据维度非常高的场合。但是当你没有一个足够大的数据集进行训练的时候应该怎么办呢?

解决训练数据集太小的方法通常有两种(两者可以结合使用):

(1) 使用数据增强策略

(2) 使用预训练模型

在F.chollet《Deep Learning with Python》#5.3.1中提到利用预训练模型进行特征提取然后再训练分类器时有两种做法:

方法一:用预训练模型的卷积基对数据集进行处理生成原数据集的特征,我称之为预特征提取。然后,基于预提取的特征训练最后的密集连接层分类器。关于这个方法的实验参见: 深度学习笔记:利用预训练模型之特征提取训练小数据集上的图像分类器 深度学习笔记:利用预训练模型之特征提取训练小数据集上的图像分类器_chenxy_bwave的专栏-CSDN博客

方法二:在卷积基的基础上进行扩展,追加最终的密集连接分类器。然后在冻结卷积基的系数的条件下基于数据增强技术对整个模型进行训练。由于卷积基被冻结,其系数没有更新,所以卷积基的作用也仅仅是用于特征提取,但是它是在线(on-the-fly)进行的,所以我称之为在线特征提取,以区别于上面的预特征提取。

方法一不能与数据增强技术结合使用,这个在另一篇博文中已经解释过了。

前面几篇我们分别进行了如下实验:

(1) 不用数据增强条件下在一个小数据集上训练一个小的卷积神经网络

(2) 用数据增强技术的条件下在一个小数据集上训练一个小的卷积神经网络

(3) 基于预训练模型的预特征提取(without data augmentation),然后再训练最后的密集连接分类器(即上述得方法一)

基于预训练模型的预特征提取的方法,分类准确度比(1),(2)有很大的提高。但是无法利用数据增强。数据增强是否会带来更大的好处呢?答案可能是:应该可以。但是F.chollet《Deep Learning with Python》#5.3.1中所举的例子却可能是一个乌龙。详细的讨论可以参考[ref1]和[ref2]。

本文做一些对比试验来看看到底是什么情况:

第2章:预特征提取 w/o 数据增强, ImageDataGenerator(rescale)

第3章:在线特征提取+数据增强 + 'conv_base.trainable=False'+ ImageDataGenerator(rescale)

第4章:在线特征提取+数据增强 + 'conv_base.trainable=True', ImageDataGenerator(rescale)

第5章:预特征提取 w/o 数据增强, ImageDataGenerator(preprocess_input)

第6章:在线特征提取+数据增强 + conv_base.trainable = False + ImageDataGenerator(preprocess_input)

第7章:在线特征提取+数据增强 + conv_base.trainable = True + ImageDataGenerator(preprocess_input)

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras import utils
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
print(tf.__version__)

2. 预特征提取 w/o 数据增强, ImageDataGenerator(rescale)

        这个是重复之前的实现,用于对比。与“https://blog.csdn.net/chenxy_bwave/article/details/122287236” 的差异仅仅在于,改用test_datagen用作验证。因为在后续对比实验中,要针对train_datagen采用数据增强处理,而(据说)验证集是不需要采用数据增强的(是不需要还是不能做呢?)。为了保证大家在尽量相同的条件下进行对比试验,就统一用test_datagen用作验证集了。最后就对比验证集上的accuracy。

# Model loading
conv_base = keras.applications.vgg16.VGG16(
    weights="imagenet",
    include_top=False,
    input_shape=(180, 180, 3))

# Data generators
import os
from tensorflow.keras.preprocessing.image import ImageDataGenerator

batch_size = 32
train_dir = os.path.join('F:\DL\cats_vs_dogs_small', 'train')
test_dir = os.path.join('F:\DL\cats_vs_dogs_small', 'test')
train_datagen = ImageDataGenerator(rescale=1./255)
test_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory(
    directory=train_dir,
    target_size=(180, 180),
    color_mode="rgb",
    batch_size=batch_size,
    class_mode="binary",
    subset='training',
    shuffle=True,
    seed=42
)
test_generator = test_datagen.flow_from_directory(
    directory=test_dir,
    target_size=(180, 180),
    color_mode="rgb",
    batch_size=batch_size,
    class_mode='binary',
    shuffle=False,
    seed=42
)

# Feature Extraction
import numpy as np

def get_features_and_labels(dataGenerator):
    all_features = []
    all_labels = []
    k = 0
    for images, labels in dataGenerator:
        features = conv_base.predict(images)
        all_features.append(features)
        all_labels.append(labels)
        k += 1
        if dataGenerator.batch_size * (k+1) > dataGenerator.n:
            break
    print('Totally, {0}-batches with batch_size={1}'.format(k,dataGenerator.batch_size))
    return np.concatenate(all_features), np.concatenate(all_labels)

train_features, train_labels =  get_features_and_labels(train_generator)
val_features, val_labels =  get_features_and_labels(test_generator)
#test_features, test_labels =  get_features_and_labels(test_generator)
print(train_features.shape,train_labels.shape)

from tensorflow.keras import optimizers
inputs = keras.Input(shape=(5, 5, 512)) # This shape has to be the same as the output shape of the convbase
x = layers.Flatten()(inputs)
x = layers.Dense(256,activation='relu')(x)
#x = layers.Dropout(0.5)(x) # Having slight difference
outputs = layers.Dense(1, activation="sigmoid")(x)
model = keras.Model(inputs, outputs)
model.compile(loss="binary_crossentropy",
              optimizer=optimizers.RMSprop(learning_rate=2e-5),
              metrics=["accuracy"])

callbacks = [
    keras.callbacks.ModelCheckpoint(
      filepath="feature_extraction.keras",
      save_best_only=True,
      monitor="val_loss")
]
history = model.fit(
    train_features, train_labels,
    epochs=32,
    validation_data=(val_features, val_labels),
    callbacks=callbacks)

# Plotting loss and accuracy curve
import matplotlib.pyplot as plt
accuracy = history.history["accuracy"]
val_accuracy = history.history["val_accuracy"]
loss = history.history["loss"]
val_loss = history.history["val_loss"]
epochs = range(1, len(accuracy) + 1)
plt.plot(epochs, accuracy, "bo", label="Training accuracy")
plt.plot(epochs, val_accuracy, "b", label="Validation accuracy")
plt.title("Training and validation accuracy")
plt.legend()
plt.figure()
plt.plot(epochs, loss, "bo", label="Training loss")
plt.plot(epochs, val_loss, "b", label="Validation loss")
plt.title("Training and validation loss")
plt.legend()
plt.show()

 

  

3. 在线特征提取+数据增强+'conv_base.trainable=False'+ ImageDataGenerator(rescale)

        这个就F.chollet书中的原始方案。F.chollet声称这个方案能够获得96%的accuracy,然而,很多人都报告并不能再现。在[ref2]中有人给出一个解释,那就是可能是因为keras的早一点的版本(在F.chollet写这本书的时后)的一个bug导致了F.chollet的乌龙:那是的keras版本中,虽然conv_base.trainable设为False,然而并没有真正的冻结!

        以下结果也表明,虽然由于在线特征提取耗费了预特征提取的几乎20倍的时间,然而accuracy只要极小的改善(1~2%?)。所以,数据增强的魔力在这里失效了?

conv_base  = keras.applications.vgg16.VGG16(
    weights="imagenet",
    include_top=False,
    input_shape=(180, 180, 3))
conv_base.trainable = False

# Data augmentation makes the dropout layer unnecessary
from tensorflow.keras import models
from tensorflow.keras import layers
model = models.Sequential()
model.add(conv_base)
model.add(layers.Flatten())
model.add(layers.Dense(256,activation='relu'))
model.add(layers.Dense(1, activation="sigmoid"))

model.summary()

model.compile(loss="binary_crossentropy",
              #optimizer="rmsprop",
              optimizer=optimizers.RMSprop(learning_rate=2e-5),
              metrics=["accuracy"])

# Data generators
import os
from tensorflow.keras.preprocessing.image import ImageDataGenerator

batch_size = 32
train_dir = os.path.join('F:\DL\cats_vs_dogs_small', 'train')
test_dir = os.path.join('F:\DL\cats_vs_dogs_small', 'test')
train_datagen = ImageDataGenerator(
    rescale=1./255,
    rotation_range=40,
    width_shift_range=0.2,
    height_shift_range=0.2,
    shear_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True,
    fill_mode='nearest')
test_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory(
    directory=train_dir,
    target_size=(180, 180),
    color_mode="rgb",
    batch_size=batch_size,
    class_mode="binary",
    subset='training',
    shuffle=True,
    seed=42
)
test_generator = test_datagen.flow_from_directory(
    directory=test_dir,
    target_size=(180, 180),
    color_mode="rgb",
    batch_size=batch_size,
    class_mode='binary',
    shuffle=False,
    seed=42
)
callbacks = [
    keras.callbacks.ModelCheckpoint(
        filepath="feature_extraction_with_data_augmentation.keras",
        save_best_only=True,
        monitor="val_loss")
]
history = model.fit(
    train_generator,
    epochs=50,
    validation_data=test_generator,
    callbacks=callbacks)

import matplotlib.pyplot as plt
accuracy = history.history["accuracy"]
val_accuracy = history.history["val_accuracy"]
loss = history.history["loss"]
val_loss = history.history["val_loss"]
epochs = range(1, len(accuracy) + 1)
plt.plot(epochs, accuracy, "bo", label="Training accuracy")
plt.plot(epochs, val_accuracy, "b", label="Validation accuracy")
plt.title("Training and validation accuracy")
plt.legend()
plt.figure()
plt.plot(epochs, loss, "bo", label="Training loss")
plt.plot(epochs, val_loss, "b", label="Validation loss")
plt.title("Training and validation loss")
plt.legend()
plt.show()

 

 

 

4. 在线特征提取+数据增强 + conv_base.trainable = True, ImageDataGenerator(rescale)

        在[ref2]中也有人说直接conv_base.trainable改为True就可以了,即允许卷积基的系数也参与训练。但是,这种方案当然没有什么可称道的。因为这样的话,采用预训练模型的好处就大打折扣了(这样的话,其实可能就等价于后面要介绍的微调模型的方式了)。

        以下代码相比第3章的代码仅仅是将conv_base.trainable改为True(代码就不重贴了).

        由于卷积基也参与了系数更新,因此运行速度相比第3章进一步的变慢了(大概是第3章的2.5倍)。

 

 

 

 

5. 预特征提取 w/o 数据增强, ImageDataGenerator(preprocess_input)

        在[ref2]中有人报告仅仅用ImageDataGenerator(preprocess_input)替换用ImageDataGenerator(rescale=1./255),不需要数据增强,也能得到大幅度的性能提升。

 

所以以下代码相比上一节的代码仅仅修改了两处,如下图所示。结果表明的确很快就在验证集上达到了95.7%的accuracy。那么,ImageDataGenerator(preprocess_input)究竟做了什么神奇的事情呢?

        Need further investigation.

 

 

6. 在线特征提取+数据增强 + conv_base.trainable = False + ImageDataGenerator(preprocess_input)

        在第3章代码的基础上,将ImageDataGenerator(rescale=1./255)改为ImageDataGenerator(preprocessing_function=preprocess_input).

 

 

 

 

7. 在线特征提取+数据增强 + conv_base.trainable = True + ImageDataGenerator(preprocess_input)

        在第6章代码的基础上,将conv_base.trainable由False改为True.

 

 

8. 总结

从以上实验对比结果来看,可以认为其实主要的性能提升是来自ImageDataGenerator(rescale=1./255)到ImageDataGenerator(preprocessing_function=preprocess_input)所带来,相比之下,数据增强和conv_base.trainable = True所带来的好处范围相形见绌。所以,接下来需要解释的问题是:

(1) ImageDataGenerator(preprocessing_function=preprocess_input)究竟做了什么特殊的处理,竟有如此神奇的魔力?

(2) 为什么“数据增强”和“conv_base.trainable = True”没有在preprocess_input的基础上进一步带来更明显的改进?

关于(2),在[ref2]中有人给出了一个可能的解释,那就是:preprocess_input已经给出了足够好的性能,没有给“数据增强”和“conv_base.trainable = True”留什么进一步改进的空间。“I suspect what is happening is that the pre-trained model is already so well suited to the task of classifying cats and dogs that there isn't really much room for improvement when the inputs are properly preprocessed, So what was really happening in the book was the author was training the top layer to work around 'deficiencies' introduced by not preprocessing the inputs.--by jonvanw”.也就是说的确F.Chollet摆了一个小小的乌龙,当然这不会使这本书有所失色^-^

[Reference]

[1] machine learning - feature extraction: freezing convolutional base vs. training on extracted features - Cross Validated

[2] https://github.com/fchollet/deep-learning-with-python-notebooks/issues/21