如何在 Keras 中重用 VGG19 进行图像分类?

Posted

技术标签:

【中文标题】如何在 Keras 中重用 VGG19 进行图像分类?【英文标题】:How to reuse VGG19 for image classification in Keras? 【发布时间】:2018-02-19 09:41:09 【问题描述】:

我目前正在尝试了解如何重用 VGG19(或其他架构)以改进我的小型图像分类模型。我将图像(在本例中为绘画)分为 3 类(比如说,15、16 和 17 世纪的绘画)。我有一个相当小的数据集,每类 1800 个训练示例,验证集中每类 250 个。

我有以下实现:

from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras import backend as K
from keras.callbacks import ModelCheckpoint
from keras.regularizers import l2, l1
from keras.models import load_model

# set proper image ordering for TensorFlow
K.set_image_dim_ordering('th')

batch_size = 32

# this is the augmentation configuration we will use for training
train_datagen = ImageDataGenerator(
        rescale=1./255,
        shear_range=0.2,
        zoom_range=0.2,
        horizontal_flip=True)

# this is the augmentation configuration we will use for testing:
# only rescaling
test_datagen = ImageDataGenerator(rescale=1./255)

# this is a generator that will read pictures found in
# subfolers of 'data/train', and indefinitely generate
# batches of augmented image data
train_generator = train_datagen.flow_from_directory(
        'C://keras//train_set_paintings//',  # this is the target directory
        target_size=(150, 150),  # all images will be resized to 150x150
        batch_size=batch_size,
        class_mode='categorical')

# this is a similar generator, for validation data
validation_generator = test_datagen.flow_from_directory(
        'C://keras//validation_set_paintings//',
        target_size=(150, 150),
        batch_size=batch_size,
        class_mode='categorical')

model = Sequential()

model.add(Conv2D(16, (3, 3), input_shape=(3, 150, 150)))
model.add(Activation('relu'))  # also tried LeakyRelu, no improvments
model.add(MaxPooling2D(pool_size=(2, 3), data_format="channels_first"))

model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 3), data_format="channels_first"))

model.add(Flatten())
model.add(Dense(64, kernel_regularizer=l2(.01))) 
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(3))
model.add(Activation('softmax'))

model.compile(loss='categorical_crossentropy',
              optimizer='adam',  # also tried SGD, it doesn't perform as well as adam
              metrics=['accuracy'])

fBestModel = 'best_model_final_paintings.h5'
best_model = ModelCheckpoint(fBestModel, verbose=0, save_best_only=True)

hist = model.fit_generator(
    train_generator,
    steps_per_epoch=2000 // batch_size,
    epochs=100,
    validation_data=validation_generator,
    validation_steps=200 // batch_size,
    callbacks=[best_model],
    workers=8  # cpu generation is run in parallel to the gpu training
)

print("Maximum train accuracy:", max(hist.history["acc"]))
print("Maximum train accuracy on epoch:", hist.history["acc"].index(max(hist.history["acc"]))+1)

print("Maximum validation accuracy:", max(hist.history["val_acc"]))
print("Maximum validation accuracy on epoch:", hist.history["val_acc"].index(max(hist.history["val_acc"]))+1)

我设法在过度拟合方面保持平衡:

如果我让架构更深,如果我更严格地对其进行规范化,它要么会过度拟合,要么会像疯了一样跳来跳去,甚至一度达到 100%:

我也尝试过使用 BatchNormalization,但是模型几乎没有学习,它在训练集上的准确率没有超过 50%。尝试了有无辍学。

我正在寻找改进模型的其他方法,而不是过多地改变架构。我看到的选项之一是重用现有架构及其权重并将其插入我的模型中。但我找不到任何真实的例子来说明如何做到这一点。我主要关注这篇博文: https://blog.keras.io/building-powerful-image-classification-models-using-very-little-data.html

它谈到了重用 VGG19 来提高准确性,但并没有真正解释它是如何完成的。还有其他我可以效仿的例子吗?我将如何使其适应我当前的实施?我找到了一个完整的模型架构,但无法在我的硬件上运行它,所以我正在寻找一种方法来重用已经训练过的带有权重的模型,然后让它适应我的问题。

另外,我不明白博客在 VGG 部分中谈到的“瓶颈特性”背后的概念。如果有人能解释一下会很高兴。

【问题讨论】:

【参考方案1】:

您绝对应该尝试Transfer Learning(链接是“迁移学习 Keras”的第一个 Google 搜索结果,有很多关于该主题的教程)。本质上,TL 是对网络的微调,该网络在一些具有新分类层的大型数据集(即最常见的 Imagenet)上进行了预训练。背后的想法是,您希望保留在较低级别的网络中学习到的所有好的特征(因为您的图像很可能也具有这些特征),并在这些特征之上学习一个新的分类器。这往往效果很好,特别是如果您有小数据集,不允许从头开始对网络进行全面训练(这也比全面训练快得多)

请注意,有多种方法可以进行 TL(我鼓励您研究该主题以找到最适合您的方法)。在我的应用程序中,我只是使用从 Imagenet 公共检查点获取的权重来初始化网络,删除最后一层并从那里训练所有内容(使用足够低的学习率,否则你会弄乱你的低级特征其实想保留)。这种方法允许数据增强。

另一种方法是使用瓶颈。在这种情况下,瓶颈,在其他情况下也称为嵌入,是您的一个输入样本在网络中某个深度级别的内部表示。换个说法,您可以看到 N 层的瓶颈,因为网络的输出在 N 层之后停止。为什么这很有用?因为您可以使用预训练网络预先计算所有样本的瓶颈,然后仅模拟网络最后一层的训练,而无需实际重新计算网络的所有(昂贵)部分直到瓶颈点。

一个简化的例子

假设您有一个具有以下结构的网络:

in -> A -> B -> C -> D -> E -> out

其中inout 是输入和输出层,另一个是您可能在网络中拥有的任何类型的层。 假设您在某处发现了在 Imagenet 上预训练的网络检查点。 Imagenet 有 1000 个类,你都不需要。因此,您将丢弃网络的最后一层(分类器)。但是,其他图层包含您要保留的要素。让E 成为我们示例中的分类器层。

从您的数据集中抽取样本,将它们提供给in 并收集匹配的瓶颈值作为层D 的输出。您对数据集中的所有样本执行一次。瓶颈集合是您将用于训练新分类器的新数据集。

您构建了一个具有以下结构的虚拟网络:

bottleneck_in -> E' -> out

您现在可以像往常一样训练这个网络,但不是从数据集中提供样本,而是从瓶颈数据集中提供匹配的瓶颈。请注意,这样做会保存从 AD 的所有层的计算,但是这样你就不能在训练期间应用任何数据增强(当然你仍然可以这样做来构建瓶颈,但您将有大量数据要存储)。

最后,为了构建你的最终分类器,你的网络架构将是

in -> A -> B -> C -> D -> E' -> out

权重 AD 取自公共检查点,权重 E' 来自您的训练。

【讨论】:

【参考方案2】:

非常短的版本:

    加载 Vgg 扔掉输出层和倒数第二层 在最后放置一个新的、随机初始化的输出层 微调您的数据

我几乎可以肯定 Keras 至少包含一个代码示例

【讨论】:

以上是关于如何在 Keras 中重用 VGG19 进行图像分类?的主要内容,如果未能解决你的问题,请参考以下文章

Keras CIFAR-10图像分类 VGG 篇

Keras CIFAR-10图像分类 VGG 篇

使用 keras 预训练 vgg16 的感知损失,输出图像颜色不正确

使用预训练 vgg19 tensorflow,Keras 在 CNN 自动编码器中定义自定义损失(感知损失)

使用VGG网络进行MNIST图像分类

使用VGG网络进行MNIST图像分类