0
点赞
收藏
分享

微信扫一扫

keras深度学习之猫狗分类四(微调模型)

朱悟能_9ad4 2022-02-15 阅读 98

另一种广泛使用的模型复用方法是模型微调(fine-tuning),与特征提取互为补充。对于用于特征提取的冻结的模型基,微调是指将其顶部的几层“解冻”,并将这解冻的几层和新增加的部分(本例中是全连接分类器)联合训练。之所以叫作微调,是因为它只是略微调整了所复用模型中更加抽象的表示,以便让这些表示与手头的问题更加相关。
在这里插入图片描述
前面说过,冻结 VGG16 的卷积基是为了能够在上面训练一个随机初始化的分类器。同理,只有上面的分类器已经训练好了,才能微调卷积基的顶部几层。如果分类器没有训练好,那么训练期间通过网络传播的误差信号会特别大,微调的几层之前学到的表示都会被破坏。因此,微调网络的步骤如下。

  1. 在已经训练好的基网络(base network)上添加自定义网络。
  2. 冻结基网络。
  3. 训练所添加的部分。
  4. 解冻基网络的一些层。
  5. 联合训练解冻的这些层和添加的部分。

为什么不微调更多层?为什么不微调整个卷积基?

  1. 卷积基中更靠底部的层编码的是更加通用的可复用特征,而更靠顶部的层编码的是更专业化的特征。微调这些更专业化的特征更加有用,因为它们需要在你的新问题上改变用途。微调更靠底部的层,得到的回报会更少。
  2. 训练的参数越多,过拟合的风险越大。卷积基有 1500 万个参数,所以在你的小型数据集上训练这么多参数是有风险的。
    微调模型的训练代码如下:
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from PIL import Image
from tensorflow.keras import layers
from tensorflow.keras import models
from tensorflow.keras import optimizers
import matplotlib.pyplot as plt
from tensorflow.keras.preprocessing import image as kimage
from tensorflow.keras.applications import VGG16
import numpy as np

'''
ImageDataGenerator可完成读取图像数据
读取图像文件
将jpeg图像解码为RGB像素网络
将这些像素转换到为浮点型张量并缩放到0~1之间
'''
#训练样本的目录
train_dir='./dataset/training_set/'
#验证样本的目录
validation_dir='./dataset/validation_set/'
#测试样本目录
test_dir='./dataset/test_set/'

#训练样本生成器
#注意数据增强只能用于训练数据,不能用于验证数据和测试数据
'''
进行数据增强
'''
#设置数据增强
'''
rotation_range 是角度值(在 0~180 范围内),表示图像随机旋转的角度范围。
width_shift 和 height_shift 是图像在水平或垂直方向上平移的范围(相对于总宽
度或总高度的比例)。
shear_range 是随机错切变换的角度。
zoom_range 是图像随机缩放的范围。
horizontal_flip 是随机将一半图像水平翻转。如果没有水平不对称的假设(比如真
实世界的图像),这种做法是有意义的。
fill_mode是用于填充新创建像素的方法,这些新像素可能来自于旋转或宽度/高度平移。
'''
train_datagen=ImageDataGenerator(
    rescale=1./255,
    rotation_range=40,
    width_shift_range=0.2,
    height_shift_range=0.2,
    shear_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True,
    fill_mode='nearest')

train_generator=train_datagen.flow_from_directory(
    directory=train_dir,
    target_size=(150,150),
    class_mode='binary',
    batch_size=20
)

#验证样本生成器
validation_datagen=ImageDataGenerator(rescale=1./255)
validation_generator=train_datagen.flow_from_directory(
    directory=validation_dir,
    target_size=(150,150),
    class_mode='binary',
    batch_size=20
)

#测试样本生成器
test_datagen=ImageDataGenerator(rescale=1./255)
test_generator=train_datagen.flow_from_directory(
    directory=test_dir,
    target_size=(150,150),
    class_mode='binary',
    batch_size=20
)



if __name__=='__main__':
    conv_base=VGG16(weights='imagenet',
                include_top=False,
                input_shape=(150,150,3))
    #冻结卷积基 保证其权重在训练过程中不变
    conv_base.trainable=False
    #构建训练网络
    model=models.Sequential()
    model.add(conv_base)
    model.add(layers.Flatten())
    model.add(layers.Dense(units=256,activation='relu'))
    model.add(layers.Dense(units=1,activation='sigmoid'))

    model.compile(optimizer=optimizers.RMSprop(learning_rate=1e-4),
                  loss='binary_crossentropy',
                  metrics=['acc'])

    model.summary()

    # 第一次拟合网络 卷积基全部冻结 训练所添加的分类部分
    model.fit_generator(
        train_generator,
        steps_per_epoch=100,
        epochs=30,
        validation_data=validation_generator,
        validation_steps=50
    )

    #测试测试集的准确率
    test_eval=model.evaluate_generator(test_generator)
    print(test_eval)

    #解冻卷积基中的某些层
    conv_base.trainable=True
    for layer in conv_base.layers:
        layer.trainable=False
        if layer.name=='block5_conv1':
            break
    
    for layer in conv_base.layers:
        print(layer.name+':'+str(layer.trainable))

    model.compile(loss='binary_crossentropy',
                  optimizer=optimizers.RMSprop(lr=1e-5),
                  metrics=['acc'])
    # 第二次拟合网络 卷积基部分解冻 训练解冻的部分 
    history=model.fit_generator(
        train_generator,
        steps_per_epoch=100,
        epochs=30,
        validation_data=validation_generator,
        validation_steps=50
    )
    test_eval=model.evaluate_generator(test_generator)
    print(test_eval)

    acc = history.history['acc']
    val_acc = history.history['val_acc']
    loss = history.history['loss']
    val_loss = history.history['val_loss']
    epochs = range(1, len(acc) + 1)
    plt.plot(epochs, acc, 'bo', label='Training acc')
    plt.plot(epochs, val_acc, 'b', label='Validation acc')
    plt.title('Training and validation accuracy')
    plt.legend()
    plt.figure()
    plt.plot(epochs, loss, 'bo', label='Training loss')
    plt.plot(epochs, val_loss, 'b', label='Validation loss')
    plt.title('Training and validation loss')
    plt.legend()
    plt.show()

模型训练过程中的准确率曲线如图:
在这里插入图片描述

模型训练过程中的损失函数曲线如图:
在这里插入图片描述

举报

相关推荐

0 条评论