在 keras 中微调预训练模型

Posted

技术标签:

【中文标题】在 keras 中微调预训练模型【英文标题】:Fine tuning pretrained model in keras 【发布时间】:2017-06-05 11:10:17 【问题描述】:

我想在 keras 中使用预训练的 imagenet VGG16 模型,并在顶部添加我自己的小型 convnet。我只对特征感兴趣,而不是预测

from keras.preprocessing.image import ImageDataGenerator, array_to_img, img_to_array, load_img
from keras.applications.vgg16 import VGG16
from keras.preprocessing import image
from keras.applications.vgg16 import preprocess_input
import numpy as np
import os
from keras.models import Model
from keras.models import Sequential
from keras.layers import Convolution2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense

从目录加载图片(目录包含 4 张图片)

IF = '/home/ubu/files/png/'
files = os.listdir(IF)

imgs = [img_to_array(load_img(IF + p, target_size=[224,224])) for p in files]
im = np.array(imgs)

加载基础模型,预处理输入并获取特征

base_model = VGG16(weights='imagenet', include_top=False)

x = preprocess_input(aa)
features = base_model.predict(x)

这行得通,我在预训练的 VGG 上获得了我的图像的特征。

我现在想微调模型并添加一些卷积层。 我阅读了https://blog.keras.io/building-powerful-image-classification-models-using-very-little-data.html 和https://keras.io/applications/,但无法将它们完全结合在一起。

在顶部添加我的模型:

x = base_model.output
x = Convolution2D(32, 3, 3)(x)
x = Activation('relu')(x)
x = MaxPooling2D(pool_size=(2, 2))(x)
x = Convolution2D(32, 3, 3)(x)
x = Activation('relu')(x)
feat = MaxPooling2D(pool_size=(2, 2))(x)

构建完整的模型

model_complete = Model(input=base_model.input, output=feat)

停止学习基础层

for layer in base_model.layers:
layer.trainable = False

新型号

model_complete.compile(optimizer='rmsprop', 
          loss='binary_crossentropy')

现在适合新模型,模型是 4 张图像,[1,0,1,0] 是类标签。 但这显然是错误的:

model_complete.fit_generator((x, [1,0,1,0]), samples_per_epoch=100, nb_epoch=2)

ValueError: output of generator should be a tuple (x, y, sample_weight) or (x, y). Found: None

这是怎么做到的?

如果我只想替换最后一个卷积块(VGG16 中的 conv block5)而不是添加一些东西,我该怎么做?

我将如何只训练瓶颈特征?

特征输出 features 具有形状 (4, 512, 7, 7)。有四个图像,但其他维度是什么?我如何将其减少为 (1,x) 数组?

【问题讨论】:

【参考方案1】:

使用model.fit(X, y) 对您的数据集进行训练,如下所述:https://keras.io/models/model/#fit

此外,您应该添加一个 Flatten 层和一个输出形状为 1 的密集层,以获得正确的结果形状。

【讨论】:

【参考方案2】:

拟合模型

您的生成器代码的问题在于 fit_generator 方法需要一个生成器函数来生成您未提供的拟合数据。 您可以按照已链接到的教程中的方式定义生成器,也可以自己创建数据和标签并自己拟合模型:

model_complete.fit(images, labels, batch_size=100, nb_epoch=2)

其中图像是您生成的训练图像,标签是相应的标签。

删除最后一层

假设你有一个模型变量和下面描述的“pop”方法,你可以通过model = pop(model) 删除最后一层。

仅训练特定层 正如您在代码中所做的那样,您可以这样做:

for layer in base_model.layers:
    layer.trainable = False

然后您可以通过将trainable 属性更改为True 来“解冻”和分层。

改变尺寸

要将输出更改为一维数组,您可以使用Flatten layer


pop方法

def pop(model):
    '''Removes a layer instance on top of the layer stack.
    This code is thanks to @joelthchao https://github.com/fchollet/keras/issues/2371#issuecomment-211734276
    '''
    if not model.outputs:
        raise Exception('Sequential model cannot be popped: model is empty.')
    else:
        model.layers.pop()
        if not model.layers:
            model.outputs = []
            model.inbound_nodes = []
            model.outbound_nodes = []
        else:
            model.layers[-1].outbound_nodes = []
            model.outputs = [model.layers[-1].output]
        model.built = False

    return model

【讨论】:

Flatten 层和 reshape(-1) 一样吗? 我没有这样使用它,但据我所知 - 结果应该是一样的。

以上是关于在 keras 中微调预训练模型的主要内容,如果未能解决你的问题,请参考以下文章

Keras - 分类器未从预训练模型的转移值中学习

《自然语言处理实战入门》深度学习 ---- 预训练模型的使用(ALBERT 进行多标签文本分类与微调 fine tune)

《自然语言处理实战入门》深度学习 ---- 预训练模型的使用(ALBERT 进行多标签文本分类与CPU 下的微调 fine tune)

手把手写深度学习(13):如何利用官方预训练模型做微调/迁移学习?(以Resnet50提取图像特征为例)

使用 Simple Transformers 微调预训练的语言模型

持续预训练与使用 MLM 微调语言模型