简体   繁体   English

Keras VGG16微调

[英]Keras VGG16 fine tuning

There is an example of VGG16 fine-tuning on keras blog , but I can't reproduce it. keras博客上有一个VGG16微调的例子,但我无法重现它。

More precisely, here is code used to init VGG16 without top layer and to freeze all blocks except the topmost: 更确切地说,这里是用于在没有顶层的情况下初始化VGG16并冻结除最顶层之外的所有块的代码:

WEIGHTS_PATH_NO_TOP = 'https://github.com/fchollet/deep-learning-models/releases/download/v0.1/vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5'
weights_path = get_file('vgg16_weights.h5', WEIGHTS_PATH_NO_TOP)

model = Sequential()
model.add(InputLayer(input_shape=(150, 150, 3)))
model.add(Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(MaxPooling2D((2, 2), strides=(2, 2)))

model.add(Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(MaxPooling2D((2, 2), strides=(2, 2)))

model.add(Conv2D(256, (3, 3), activation='relu', padding='same'))
model.add(Conv2D(256, (3, 3), activation='relu', padding='same'))
model.add(Conv2D(256, (3, 3), activation='relu', padding='same'))
model.add(MaxPooling2D((2, 2), strides=(2, 2)))

model.add(Conv2D(512, (3, 3), activation='relu', padding='same'))
model.add(Conv2D(512, (3, 3), activation='relu', padding='same'))
model.add(Conv2D(512, (3, 3), activation='relu', padding='same'))
model.add(MaxPooling2D((2, 2), strides=(2, 2)))

model.add(Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv1'))
model.add(Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv2'))
model.add(Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv3'))
model.add(MaxPooling2D((2, 2), strides=(2, 2), name='block5_maxpool'))

model.load_weights(weights_path)

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

for layer in model.layers[-4:]:
    layer.trainable = True
    print("Layer '%s' is trainable" % layer.name)  

Next, creating a top model with single hidden layer: 接下来,创建具有单个隐藏层的顶级模型:

top_model = Sequential()
top_model.add(Flatten(input_shape=model.output_shape[1:]))
top_model.add(Dense(256, activation='relu'))
top_model.add(Dropout(0.5))
top_model.add(Dense(1, activation='sigmoid'))
top_model.load_weights('top_model.h5')

Note that it was previously trained on bottleneck features like it is described in the blog post. 请注意,它之前已经过博客文章中描述的瓶颈功能培训。 Next, add this top model to the base model and compile: 接下来,将此顶级模型添加到基础模型并编译:

model.add(top_model)
model.compile(loss='binary_crossentropy',
              optimizer=SGD(lr=1e-4, momentum=0.9),
              metrics=['accuracy'])

And eventually, fit on cats/dogs data: 最终,适合猫/狗数据:

batch_size = 16

train_datagen = ImageDataGenerator(rescale=1./255,
                                   shear_range=0.2,
                                   zoom_range=0.2,
                                   horizontal_flip=True)

test_datagen = ImageDataGenerator(rescale=1./255)

train_gen = train_datagen.flow_from_directory(
    TRAIN_DIR,
    target_size=(150, 150),
    batch_size=batch_size,
    class_mode='binary')

valid_gen = test_datagen.flow_from_directory(
    VALID_DIR,
    target_size=(150, 150),
    batch_size=batch_size,
    class_mode='binary')

model.fit_generator(
    train_gen,
    steps_per_epoch=nb_train_samples // batch_size,
    epochs=nb_epoch,
    validation_data=valid_gen,
    validation_steps=nb_valid_samples // batch_size)

But here is an error I am getting when trying to fit: 但是这是我在尝试适应时遇到的错误:

ValueError: Error when checking model target: expected block5_maxpool to have 4 > dimensions, but got array with shape (16, 1) ValueError:检查模型目标时出错:预期block5_maxpool有4>维,但得到的数组有形状(16,1)

Therefore, it seems that something is wrong with the last pooling layer in base model. 因此,基本模型中的最后一个池层似乎出现了问题。 Or probably I've done something wrong trying to connect base model with the top one. 或者我可能在尝试将基本模型与顶层模型连接时做错了。

Does anybody have similar issue? 有没有人有类似的问题? Or maybe there is a better way to build such "concatenated" models? 或者也许有更好的方法来构建这样的“连锁”模型? I am using keras==2.0.0 with theano backend. 我正在使用keras==2.0.0theano后端。

Note : I was using examples from gist and applications.VGG16 utility, but has issues trying to concatenate models, I am not too familiar with keras functional API. 注意 :我使用的是gist和applications.VGG16实用程序中的示例,但是在尝试连接模型时keras问题,我对keras功能API不太熟悉。 So this solution I provide here is the most "successful" one, ie it fails only on fitting stage. 所以我在这里提供的解决方案是最“成功”的解决方案,即它仅在适合阶段失败。


Update #1 更新#1

Ok, here is a small explanation about what I am trying to do. 好的,这里有一个关于我想要做什么的小解释。 First of all, I am generating bottleneck features from VGG16 as follows: 首先,我正在从VGG16产生以下瓶颈功能:

def save_bottleneck_features():
    datagen = ImageDataGenerator(rescale=1./255)
    model = applications.VGG16(include_top=False, weights='imagenet')

    generator = datagen.flow_from_directory(
        TRAIN_DIR,
        target_size=(150, 150),
        batch_size=batch_size,
        class_mode=None,
        shuffle=False)    
    print("Predicting train samples..")
    bottleneck_features_train = model.predict_generator(generator, nb_train_samples)
    np.save(open('bottleneck_features_train.npy', 'w'), bottleneck_features_train)

    generator = datagen.flow_from_directory(
        VALID_DIR,
        target_size=(150, 150),
        batch_size=batch_size,
        class_mode=None,
        shuffle=False)
    print("Predicting valid samples..")
    bottleneck_features_valid = model.predict_generator(generator, nb_valid_samples)
    np.save(open('bottleneck_features_valid.npy', 'w'), bottleneck_features_valid)

Then, I create a top model and train it on these features as follows: 然后,我创建一个顶级模型并在这些功能上进行训练,如下所示:

def train_top_model():
    train_data = np.load(open('bottleneck_features_train.npy'))
    train_labels = np.array([0]*(nb_train_samples / 2) + 
                            [1]*(nb_train_samples / 2))
    valid_data = np.load(open('bottleneck_features_valid.npy'))
    valid_labels = np.array([0]*(nb_valid_samples / 2) +
                            [1]*(nb_valid_samples / 2))
    model = Sequential()
    model.add(Flatten(input_shape=train_data.shape[1:]))  
    model.add(Dense(256, activation='relu'))
    model.add(Dropout(0.5))
    model.add(Dense(1, activation='sigmoid'))
    model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['accuracy'])
    model.fit(train_data, train_labels,
              nb_epoch=nb_epoch,
              batch_size=batch_size,
              validation_data=(valid_data, valid_labels),
              verbose=1)
    model.save_weights('top_model.h5')   

So basically, there are two trained models, base_model with ImageNet weights and top_model with weights generated from bottleneck features. 所以基本上有两种训练的模型, base_model与ImageNet重量和top_model从瓶颈的功能生成的权重。 And I wonder how to concatenate them? 我想知道如何连接它们? Is it possible or I am doing something wrong? 是可能还是我做错了什么? Because as I can see, the response from @thomas-pinetz supposes that the top model is not trained separately and right away appended to the model . 因为我可以看到,来自@ thomas-pinetz的回应假设顶级模型没有单独训练并立即附加到模型上 Not sure if I am clear, here is a quote from the blog: 不确定我是否清楚,这是博客的引用:

In order to perform fine-tuning, all layers should start with properly trained weights: for instance you should not slap a randomly initialized fully-connected network on top of a pre-trained convolutional base. 为了进行微调,所有层都应该从训练有素的权重开始:例如,你不应该在预先训练好的卷积基础上打一个随机初始化的全连接网络。 This is because the large gradient updates triggered by the randomly initialized weights would wreck the learned weights in the convolutional base. 这是因为由随机初始化的权重触发的大梯度更新将破坏卷积基础中的学习权重。 In our case this is why we first train the top-level classifier, and only then start fine-tuning convolutional weights alongside it. 在我们的例子中,这就是为什么我们首先训练顶级分类器,然后才开始微调卷积权重。

I think that the weights described by the vgg net do not fit your model and the error stems from this. 我认为vgg网络描述的权重不适合你的模型,错误源于此。 Anyways there is a way better way to do this using the network itself as described in ( https://keras.io/applications/#vgg16 ). 无论如何,使用网络本身有一种更好的方法来实现这一点,如( https://keras.io/applications/#vgg16 )中所述。

You can just use: 你可以使用:

base_model = keras.applications.vgg16.VGG16(include_top=False, weights='imagenet', input_tensor=None, input_shape=None)

to instantiate a vgg net that is pre-trained. 实例化预训练的vgg网。 Then you can freeze the layers and use the model class to instantiate your own model like this: 然后你可以冻结图层并使用模型类来实例化你自己的模型,如下所示:

x = base_model.output
x = Flatten()(x)
x = Dense(your_classes, activation='softmax')(x) #minor edit
new_model = Model(input=base_model.input, output=x)

To combine the bottom and the top network you can use the following code snippet. 要结合底部和顶部网络,您可以使用以下代码段。 The following functions are used (Input Layer ( https://keras.io/getting-started/functional-api-guide/ ) / load_model ( https://keras.io/getting-started/faq/#how-can-i-save-a-keras-model ) and the functional API of keras): 下面的函数被使用(输入层( https://keras.io/getting-started/functional-api-guide/ )/ load_model( https://keras.io/getting-started/faq/#how-can- i-save-a-keras-model )和keras的功能API:

final_input = Input(shape=(3, 224, 224))
base_model = vgg...
top_model = load_model(weights_file)

x = base_model(final_input)
result = top_model(x)
final_model = Model(input=final_input, output=result)

I think you can concatenate both by doing something like this: 我认为您可以通过执行以下操作来连接两者:

#load vgg model
vgg_model = applications.VGG16(weights='imagenet', include_top=False, input_shape=(150, 150, 3))
print('Model loaded.')

#initialise top model
top_model = Sequential()
top_model.add(Flatten(input_shape=vgg_model.output_shape[1:]))
top_model.add(Dense(256, activation='relu'))
top_model.add(Dropout(0.5))
top_model.add(Dense(1, activation='sigmoid'))


top_model.load_weights(top_model_weights_path)

# add the model on top of the convolutional base

model = Model(input= vgg_model.input, output= top_model(vgg_model.output))

This solution refers to the example Fine-tuning the top layers of aa pre-trained network . 该解决方案参考示例微调预训练网络的顶层 Full code can be found here . 完整代码可以在这里找到。

Ok, I guess Thomas and Gowtham posted correct (and more concise answers), but I wanted to share the code, which I was able to run successfully: 好吧,我想托马斯和Gowtham发布了正确的(更简洁的答案),但我想分享代码,我能够成功运行:

def train_finetuned_model(lr=1e-5, verbose=True):
    file_path = get_file('vgg16.h5', VGG16_WEIGHTS_PATH, cache_subdir='models')
    if verbose:
        print('Building VGG16 (no-top) model to generate bottleneck features.')

    vgg16_notop = build_vgg_16()
    vgg16_notop.load_weights(file_path)
    for _ in range(6):
        vgg16_notop.pop()
    vgg16_notop.compile(optimizer=RMSprop(lr=lr), loss='categorical_crossentropy', metrics=['accuracy'])    

    if verbose:
        print('Bottleneck features generation.')

    train_batches = get_batches('train', shuffle=False, class_mode=None, batch_size=BATCH_SIZE)
    train_labels = np.array([0]*1000 + [1]*1000)
    train_bottleneck = vgg16_notop.predict_generator(train_batches, steps=2000 // BATCH_SIZE)
    valid_batches = get_batches('valid', shuffle=False, class_mode=None, batch_size=BATCH_SIZE)
    valid_labels = np.array([0]*400 + [1]*400)
    valid_bottleneck = vgg16_notop.predict_generator(valid_batches, steps=800 // BATCH_SIZE)

    if verbose:
        print('Training top model on bottleneck features.')

    top_model = Sequential()
    top_model.add(Flatten(input_shape=train_bottleneck.shape[1:]))
    top_model.add(Dense(4096, activation='relu'))
    top_model.add(Dropout(0.5))
    top_model.add(Dense(4096, activation='relu'))
    top_model.add(Dropout(0.5))
    top_model.add(Dense(2, activation='softmax'))
    top_model.compile(optimizer=RMSprop(lr=lr), loss='categorical_crossentropy', metrics=['accuracy'])
    top_model.fit(train_bottleneck, to_categorical(train_labels),
                  batch_size=32, epochs=10,
                  validation_data=(valid_bottleneck, to_categorical(valid_labels)))

    if verbose:
        print('Concatenate new VGG16 (without top layer) with pretrained top model.')

    vgg16_fine = build_vgg_16()
    vgg16_fine.load_weights(file_path)
    for _ in range(6):
        vgg16_fine.pop()
    vgg16_fine.add(Flatten(name='top_flatten'))    
    vgg16_fine.add(Dense(4096, activation='relu'))
    vgg16_fine.add(Dropout(0.5))
    vgg16_fine.add(Dense(4096, activation='relu'))
    vgg16_fine.add(Dropout(0.5))
    vgg16_fine.add(Dense(2, activation='softmax'))
    vgg16_fine.compile(optimizer=RMSprop(lr=lr), loss='categorical_crossentropy', metrics=['accuracy'])

    if verbose:
        print('Loading pre-trained weights into concatenated model')

    for i, layer in enumerate(reversed(top_model.layers), 1):
        pretrained_weights = layer.get_weights()
        vgg16_fine.layers[-i].set_weights(pretrained_weights)

    for layer in vgg16_fine.layers[:26]:
        layer.trainable = False

    if verbose:
        print('Layers training status:')
        for layer in vgg16_fine.layers:
            print('[%6s] %s' % ('' if layer.trainable else 'FROZEN', layer.name))        

    vgg16_fine.compile(optimizer=RMSprop(lr=1e-6), loss='binary_crossentropy', metrics=['accuracy'])

    if verbose:
        print('Train concatenated model on dogs/cats dataset sample.')

    train_datagen = ImageDataGenerator(rescale=1./255,
                                       shear_range=0.2,
                                       zoom_range=0.2,
                                       horizontal_flip=True)
    test_datagen = ImageDataGenerator(rescale=1./255)
    train_batches = get_batches('train', gen=train_datagen, class_mode='categorical', batch_size=BATCH_SIZE)
    valid_batches = get_batches('valid', gen=test_datagen, class_mode='categorical', batch_size=BATCH_SIZE)
    vgg16_fine.fit_generator(train_batches, epochs=100,
                             steps_per_epoch=2000 // BATCH_SIZE,
                             validation_data=valid_batches,
                             validation_steps=800 // BATCH_SIZE)
    return vgg16_fine 

It is kind of too verbose and makes all things manually (ie copies weights from pre-trained layers to concatenated model), but it works, more or less. 它有点过于冗长并且可以手动完成所有操作(即将重量从预先训练的层复制到连接模型),但它或多或少都有效。

Though this code I've posted has a problem with low accuracy (around 70%), but that is a different story. 虽然我发布的这个代码有一个低精度的问题(大约70%),但这是一个不同的故事。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM