如何在 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
其中in
和out
是输入和输出层,另一个是您可能在网络中拥有的任何类型的层。
假设您在某处发现了在 Imagenet 上预训练的网络检查点。 Imagenet 有 1000 个类,你都不需要。因此,您将丢弃网络的最后一层(分类器)。但是,其他图层包含您要保留的要素。让E
成为我们示例中的分类器层。
从您的数据集中抽取样本,将它们提供给in
并收集匹配的瓶颈值作为层D
的输出。您对数据集中的所有样本执行一次。瓶颈集合是您将用于训练新分类器的新数据集。
您构建了一个具有以下结构的虚拟网络:
bottleneck_in -> E' -> out
您现在可以像往常一样训练这个网络,但不是从数据集中提供样本,而是从瓶颈数据集中提供匹配的瓶颈。请注意,这样做会保存从 A
到 D
的所有层的计算,但是这样你就不能在训练期间应用任何数据增强(当然你仍然可以这样做来构建瓶颈,但您将有大量数据要存储)。
最后,为了构建你的最终分类器,你的网络架构将是
in -> A -> B -> C -> D -> E' -> out
权重 A
到 D
取自公共检查点,权重 E'
来自您的训练。
【讨论】:
【参考方案2】:非常短的版本:
-
加载 Vgg
扔掉输出层和倒数第二层
在最后放置一个新的、随机初始化的输出层
微调您的数据
我几乎可以肯定 Keras 至少包含一个代码示例
【讨论】:
以上是关于如何在 Keras 中重用 VGG19 进行图像分类?的主要内容,如果未能解决你的问题,请参考以下文章
使用 keras 预训练 vgg16 的感知损失,输出图像颜色不正确