zl程序教程

您现在的位置是:首页 >  其他

当前栏目

深度学习笔记:使用预训练模型之模型微调+数据增强

训练笔记学习数据 模型 深度 增强 微调
2023-09-14 09:15:01 时间

目录

0. 前言

1. 模型微调的步骤

2. 在已经训练好的基网络(base network)上添加了自定义网络

3. 数据生成

4. 冻结基网络,训练所添加的部分

5. 释放基网络最后若干层,重新训练

6. 总结

7. Reference

 


0. 前言

本系列介绍如何搭建一个卷积神经网络用于图像分类的深度学习问题,尤其是再训练数据集比较小的场合。通常来说,深度学习需要大量的数据进行训练,尤其是像在图像处理这种通常数据维度非常高的场合。但是当你没有一个足够大的数据集进行训练的时候应该怎么办呢?

解决训练数据集太小的方法通常有两种(两者可以结合使用):

(1) 使用数据增强策略

(2) 使用预训练模型进行特征提取

在F.chollet《Deep Learning with Python》#5.3.1中提到利用预训练模型进行特征提取然后再训练分类器时有三种做法:

(2-1):用预训练模型的卷积基对数据集进行处理生成原数据集的特征,我称之为预特征提取。然后,基于预提取的特征训练最后的密集连接层分类器。关于这个方法的实验参见: 深度学习笔记:利用预训练模型之特征提取训练小数据集上的图像分类器[ref3]

这种方法不能与数据增强技术结合使用,这个在[ref4]中已经解释过了.

(2-2):在卷积基的基础上进行扩展,追加最终的密集连接分类器。然后在冻结卷积基的系数的条件下基于数据增强技术对整个模型进行训练。由于卷积基被冻结,其系数没有更新,所以卷积基的作用也仅仅是用于特征提取,但是它是在线(on-the-fly)进行的,所以我称之为在线特征提取,以区别于上面的预特征提取,参见[ref5]。

(2-3):在采用预训练模型进行特征提取的基础上进一步进行模型微调。所谓模型微调是相对于(2-2)的完全冻结卷积基的做法。在(2-2)中我们将采用了预训练模型的卷积基,但是卷积基的系数被冻结,不参与训练。而本方法是指开放卷积基的最后几层允许它们在训练中得到更新,这样有忘得到比(2-2)更加优化的模型。如果[ref5]的小伙伴可能记得在那一篇中作为对比我们已经做过将卷积基完全设置为可训练的实验,但是本文所要说的模型微调与之有所区别,参见以下说明。

此外,在本文的实验中,将继续采取在线特征提取及数据增强技术,以及在上一篇中已经证明了最有效的一个技术要素:使用ImageDataGenerator(preprocess_input)。

1. 模型微调的步骤

模型微调的步骤如下:

(1) 在已经训练好的基网络(base network)上添加自定义网络

(2) 冻结基网络,训练所添加的部分

(3) 解冻基网络的一部分, 再次联合训练解冻的这些层和添加的部分

为什么要分为两个步骤进行训练呢?

因为如果一开始就进行完全的联合训练,新追加的部分由于从随机状态出发,在训练期间通过网络传播的误差信号会特别大,微调的几层在之前的预训练中所学习到的表示都会被破坏,这样会降低训练的效率,即可能要求更多的轮次才能训练好。

加载所有需要的库

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras import models
from tensorflow.keras import utils
import numpy as np
import matplotlib.pyplot as plt
import os
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.applications.imagenet_utils import preprocess_input
from PIL import Image
from tensorflow.keras import optimizers
print(tf.__version__)

        本文中代码在jupyter notebook中运行验证过。tensorflow版本2.5.0 + GPU。

        大部分的代码在本系列前面几篇中都已经出现过,这个不再解释,只需要串起来运行即可。

2. 在已经训练好的基网络(base network)上添加了自定义网络

# Model loading
conv_base = keras.applications.vgg16.VGG16(
    weights="imagenet",
    include_top=False,
    input_shape=(180, 180, 3))

inputs = keras.Input(shape=(5, 5, 512)) # This shape has to be the same as the output shape of the convbase
x = layers.Flatten()(inputs)
x = layers.Dense(256,activation='relu')(x)
outputs = layers.Dense(1, activation="sigmoid")(x)
model = keras.Model(inputs, outputs)

model = models.Sequential()
model.add(conv_base)
model.add(layers.Flatten())
model.add(layers.Dense(256,activation='relu'))
model.add(layers.Dense(1, activation="sigmoid"))

model.summary()

model.compile(loss="binary_crossentropy",
              #optimizer="rmsprop",
              optimizer=optimizers.RMSprop(learning_rate=2e-5),
              metrics=["accuracy"])

3. 数据生成

# Data generators with data augmentation
batch_size = 32
train_dir = os.path.join('F:\DL\cats_vs_dogs_small', 'train')
test_dir = os.path.join('F:\DL\cats_vs_dogs_small', 'test')
train_datagen = ImageDataGenerator(preprocessing_function=preprocess_input,
      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')
test_datagen = ImageDataGenerator(preprocessing_function=preprocess_input)

train_generator = train_datagen.flow_from_directory(
    directory=train_dir,
    target_size=(180, 180),
    color_mode="rgb",
    batch_size=batch_size,
    class_mode="binary",
    subset='training',
    shuffle=True,
    seed=42
)
test_generator = test_datagen.flow_from_directory(
    directory=test_dir,
    target_size=(180, 180),
    color_mode="rgb",
    batch_size=batch_size,
    class_mode='binary',
    shuffle=False,
    seed=42
)

4. 冻结基网络,训练所添加的部分

conv_base.trainable=False
model.compile(loss="binary_crossentropy",
              optimizer=optimizers.RMSprop(learning_rate=2e-5),
              metrics=["accuracy"])

callbacks = [
    keras.callbacks.ModelCheckpoint(
        filepath="feature_extraction_with_data_augmentation.keras",
        save_best_only=True,
        monitor="val_loss")
]
history = model.fit(
    train_generator,
    epochs=50,
    validation_data=test_generator,
    callbacks=callbacks)

# Plotting loss and accuracy curve
import matplotlib.pyplot as plt
accuracy = history.history["accuracy"]
val_accuracy = history.history["val_accuracy"]
loss = history.history["loss"]
val_loss = history.history["val_loss"]
epochs = range(1, len(accuracy) + 1)
plt.plot(epochs, accuracy, "bo", label="Training accuracy")
plt.plot(epochs, val_accuracy, "b", label="Validation accuracy")
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()

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA56yo54mb5oWi6ICV,size_20,color_FFFFFF,t_70,g_se,x_16

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA56yo54mb5oWi6ICV,size_13,color_FFFFFF,t_70,g_se,x_16 watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA56yo54mb5oWi6ICV,size_12,color_FFFFFF,t_70,g_se,x_16

5. 释放基网络最后若干层,重新训练

conv_base.trainable = True
for layer in conv_base.layers[:-4]:
    layer.trainable = False
    
model.compile(loss="binary_crossentropy",
              optimizer=keras.optimizers.RMSprop(learning_rate=1e-5),
              metrics=["accuracy"])

callbacks = [
    keras.callbacks.ModelCheckpoint(
        filepath="fine_tuning.keras", # file path for saving the model
        save_best_only=True,
        monitor="val_loss")
]
history = model.fit(
    train_generator,
    epochs=30,
    validation_data=test_generator,
    callbacks=callbacks)

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA56yo54mb5oWi6ICV,size_20,color_FFFFFF,t_70,g_se,x_16 watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA56yo54mb5oWi6ICV,size_13,color_FFFFFF,t_70,g_se,x_16watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA56yo54mb5oWi6ICV,size_12,color_FFFFFF,t_70,g_se,x_16

        注意,不要被最后这两张图的形状欺骗了。之所以显得这么‘难看’是应为它是直接从96.5%开始的。

6. 总结

以上结果表明,利用模型微调技术最终我们得到了在验证集上的最高98%的分类准确度。相比采用模型微调之前略有提高(1个百分点弱)。虽然看上去似乎微不足道,但是也许在现有的输入信息(我们只使用了3000张输入图像)条件下,也许这逼近了可能达到的极限吧。

到此为止,关于在小数据集上训练一个卷积神经网络用于图像分类的实验系列终于告一段落了,之前的各篇参见以下[ref1]~[ref5].

 

7. Reference

[1] 深度学习笔记:在小数据集上从头训练卷积神经网络_chenxy_bwave的专栏-CSDN博客

[2] 深度学习笔记:利用数据增强在小数据集上从头训练卷积神经网络_chenxy_bwave的专栏-CSDN博客

[3] 深度学习笔记:利用预训练模型之特征提取训练小数据集上的图像分类器_chenxy_bwave的专栏-CSDN博客

[4] 深度学习笔记:为什么(预)特征提取不能与数据增强结合使用_chenxy_bwave的专栏-CSDN博客

[5] 深度学习笔记:使用预训练模型之在线特征提取+数据增强_chenxy_bwave的专栏-CSDN博客

[6] Francois Chollet, Deep Learning with Python