我目前正在尝试了解如何重用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部分讨论了这个概念。如果有人能够解释它会很高兴。
答案 0 :(得分:7)
你一定要尝试Transfer Learning(链接是第一个Google结果"转移学习Keras",有很多关于这个主题的教程)。本质上,TL是对具有新分类层的一些大数据集(即,最常见的Imagenet)预先训练的网络的微调。背后的想法是,您希望保留在网络较低级别学习的所有优秀功能(因为您的图像很可能也具有这些功能),并且只需在这些功能之上学习新的分类器。这往往效果很好,特别是如果您的小数据集不允许从头开始对网络进行全面培训(它也比完整培训快得多)
请注意,有几种方法可以做TL(我鼓励您研究这个主题以找到最适合您的方法)。在我的应用程序中,我只是使用从Imagenet公共检查点获取的权重来启动网络,删除最后一层并从那里训练所有内容(学习率低,或者你会搞乱低级功能)你真的想保留)。这种方法允许数据增加。
另一种方法是使用瓶颈。在此上下文中,瓶颈(也称为嵌入在其他上下文中)是网络中某个深度级别的某个输入样本的内部表示。换句话说,你可以看到N级的瓶颈,因为网络的输出在N层之后停止了。为什么这有用?因为您可以使用预先训练好的网络预先计算所有样本的瓶颈,然后仅模拟网络最后层的培训,而无需实际重新计算网络的所有(昂贵)部分直到瓶颈点。
假设您有一个具有以下结构的网络:
in -> A -> B -> C -> D -> E -> out
其中in
和out
是输入和输出图层,另一个是网络中可能包含的任何类型的图层。
我们还说你发现在Imagenet上预先培训过的网络检查点。 Imagenet有1000个课程,你不需要这些课程。因此,您将丢弃网络的最后一层(分类器)。但是,其他图层包含您要保留的功能。让E
成为我们示例中的分类器层。
从数据集中提取样本,将其提供给in
并收集匹配的瓶颈值作为图层D
的输出。对数据集中的所有样本执行此操作一次。瓶颈的集合是您用来训练新的clssifier的新数据集。
您构建具有以下结构的虚拟网络:
bottleneck_in -> E' -> out
您现在可以像往常一样训练此网络,但不是从数据集中提取样本,而是从瓶颈数据集中提供匹配的瓶颈。请注意,这样做可以将所有图层的计算从A
保存到D
,,但这样您就无法在培训期间应用任何数据扩充(当然,您仍然可以做到构建瓶颈,但你需要存储大量数据。)
最后,要构建最终的分类器,您的网络架构将是
in -> A -> B -> C -> D -> E' -> out
权重A
到D
取自公共检查点,权重E'
来自您的培训。
答案 1 :(得分:2)
非常短的版本:
我几乎可以肯定Keras至少包含一个代码示例