• 【火炉炼AI】深度学习007-Keras微调进一步提升性能


    【火炉炼AI】深度学习007-Keras微调进一步提升性能

    (本文所使用的Python库和版本号: Python 3.6, Numpy 1.14, scikit-learn 0.19, matplotlib 2.2, Keras 2.1.6, Tensorflow 1.9.0)

    本文使用微调(Fine-tune)技术来提升模型的性能,是前面的两篇文章(编号为005和006)的延续。前面我们通过迁移学习(文章006)将这个猫狗大战二分类问题的预测准确率提升到了90%左右,但是还能不能进一步提升了?

    前面我们没有对VGG16的卷积层进行参数的优化,那么我们这里就可以来优化这部分的参数,当然,优化是很细微的调整,所以称为Fine-tune。

    微调也不是对VGG16的所有结构参数都进行调整,而是仅仅调整后面几个卷积层的参数,因为有很多学者发现,对不同的数据集,VGG16提取的特征在底层基本一样,主要区别在于高层,即卷积层的后面几层,所以只需要对这几层进行修改即可。如下我们要调整的层数为:

    可以看出,主要调整Conv block 5,前面的4个block不需调整。

    我这篇博文主要参考了:keras系列︱图像多分类训练与利用bottleneck features进行微调(三),这篇博文也是参考的Building powerful image classification models using very little data,但我发现这两篇博文有很多地方的代码跑不起来,主要原因可能是Keras或Tensorflow升级造成的,所以我做了一些必要的修改。


    1. 准备数据集

    这一部分和前面文章(编号005)一模一样,故而省略。


    2. 构建模型

    在模型的构建上,相当于是取VGG16模型的“身子”,再加上自己定义的模型作为“头”,将这两部分加在一起,组成一个完整的模型,“身子”模型仍然采用VGG16在ImageNet上得到的weights,而“头”模型则采用我们上一篇文章中训练得到的weights,然后用这个模型来训练和预测,将“身子”的一部分weights和“头”的全部weights都进行调整。

    这里有几个注意点:

    1,自己定义的分类器模型作为“头”,但是“头”的weights不能是随机初始值,而应该用已经训练好的weights,因为随机初始值会产生较大的梯队,会破坏前面VGG16卷积层的预训练后weights。

    2,微调仅仅针对VGG16网络的后面几层卷积层,而不是全部卷积层,是为了防止过拟合,整个模型结构具有绝大的熵容量,因此有很高的过拟合倾向。并且底层的特征更加具有一般性,不需要调整。

    3,在learning rate上,训练“头”的weights时可以用较大的lr,使其快速收敛,而在微调阶段,需要用较小的lr,使其性能达到最优,并且使用的optimizer也通常使用SGD而不是其他自适应学习率的优化算法,比如RMSProp,就是为了保证每次改进的幅度比较低,以免破坏VGG16提取的特征。

    # 上面的build_model2中间的my_model.layers[:25]要修改为my_model.layers[:15],原博文此处也是错的。
    # 4,构建模型
    from keras.preprocessing.image import ImageDataGenerator
    from keras.models import Sequential
    from keras.layers import Dropout, Flatten, Dense
    from keras import applications
    from keras import optimizers
    from keras.models import Model
    def build_model2():
        base_model = applications.VGG16(weights='imagenet', include_top=False,input_shape=(IMG_W, IMG_H,IMG_CH))
        # 此处我们只需要卷积层不需要全连接层,故而inclue_top=False,一定要设置input_shape,否则后面会报错
        # 这一步使用applications模块自带的VGG16函数直接加载了模型和参数,作为我们自己模型的“身子”
        
        # 下面定义我们自己的分类器,作为我们自己模型的“头”
        top_model = Sequential()
        top_model.add(Flatten(input_shape=base_model.output_shape[1:])) # 如果没有设置input_shape,这个地方报错,显示output_shape有很多None
        top_model.add(Dense(256, activation='relu'))
        top_model.add(Dropout(0.5))
        top_model.add(Dense(1, activation='sigmoid')) # 二分类问题
        
        top_model.load_weights('E:PyProjectsDataSetFireAIDeepLearningFireAI006/top_FC_model') 
        # 上面定义了模型结构,此处要把训练好的参数加载进来,
        
        my_model = Model(inputs=base_model.input, outputs=top_model(base_model.output)) # 将“身子”和“头”组装到一起
        # my_model就是我们组装好的完整的模型,也已经加载了各自的weights
        
        # 普通的模型需要对所有层的weights进行训练调整,但是此处我们只调整VGG16的后面几个卷积层,所以前面的卷积层要冻结起来
        for layer in my_model.layers[:15]: # 15层之前都是不需训练的,原博文此处也是错的。我晕。。。。
            layer.trainable = False
            
        # 模型的配置
        my_model.compile(loss='binary_crossentropy',
                      optimizer=optimizers.SGD(lr=1e-4, momentum=0.9), # 使用一个非常小的lr来微调
                      metrics=['accuracy'])
        return my_model
    

    这个模型的构建中需要将不训练的层冻结,如上设置trainable=False即可,下面进行微调训练:

    # 开始用train set来微调模型的参数
    print('start to train model2')
    my_model2=build_model2()
    history_ft2 = my_model2.fit_generator(
            train_generator,
            steps_per_epoch=train_samples_num // batch_size,
            epochs=epochs,
            validation_data=val_generator,
            validation_steps=val_samples_num // batch_size)
    

    -------------------------------------输---------出--------------------------------

    start to train model2
    Epoch 1/20
    125/125 [] - 727s 6s/step - loss: 0.3288 - acc: 0.8865 - val_loss: 0.2991 - val_acc: 0.9113
    Epoch 2/20
    125/125 [
    ] - 732s 6s/step - loss: 0.1822 - acc: 0.9395 - val_loss: 0.2988 - val_acc: 0.9113
    Epoch 3/20
    125/125 [==============================] - 724s 6s/step - loss: 0.1557 - acc: 0.9445 - val_loss: 0.2742 - val_acc: 0.9125

    ...

    Epoch 18/20
    125/125 [] - 703s 6s/step - loss: 0.0260 - acc: 0.9905 - val_loss: 0.3304 - val_acc: 0.9313
    Epoch 19/20
    125/125 [
    ] - 704s 6s/step - loss: 0.0138 - acc: 0.9955 - val_loss: 0.3267 - val_acc: 0.9413
    Epoch 20/20
    125/125 [==============================] - 705s 6s/step - loss: 0.0103 - acc: 0.9960 - val_loss: 0.3551 - val_acc: 0.9325

    --------------------------------------------完-------------------------------------

    在我这个破笔记本上训练非常耗时,20个epoch花了将近四个小时,唉。所以就不打算继续训练下去了。看一下loss和acc的结果:

    可以看出test上的acc还可以继续改进一下,train 和test上的loss也没有达到平台期,可以增大epoch继续训练。但是也可以看出有些过拟合现象。

    ########################小**********结###############################

    1,Fine-Tune的核心是对原始的骨架网络(此处为VGG16)进行参数的微调,所以需要用非常小的学习率,而且要用SGD优化器。

    2,在使用微调之后,准确率从90%左右提升到了约93%,增加epoch数目可以提升的更多。

    #################################################################


    注:本部分代码已经全部上传到(我的github)上,欢迎下载。

  • 相关阅读:
    Leetcode1716. 计算力扣银行的钱
    Leetcode 575. 分糖果(可以,一次过)
    Lesson9——Pandas iteration遍历
    Lesson10——Pandas sorting排序
    Lesson8——Pandas reindex重置索引
    NumPy 教程目录
    Lesson3——NumPy 数据类型
    Lesson6——NumPy 数组操作
    Lesson5——NumPy 创建数组
    Lesson1——NumPy NumPy 安装
  • 原文地址:https://www.cnblogs.com/RayDean/p/9957562.html
Copyright © 2020-2023  润新知