温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Tensorflow2.4怎么完成迁移和模型微调

发布时间:2023-01-09 10:56:32 来源:亿速云 阅读:139 作者:iii 栏目:开发技术

这篇文章主要介绍“Tensorflow2.4怎么完成迁移和模型微调”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Tensorflow2.4怎么完成迁移和模型微调”文章能帮助大家解决问题。

实现过程

1. 获取数据

首先我们要使用 tensorflow 的内置函数,从网络上下载猫狗图片集,训练数据中的猫、狗图片各 1000 张,验证数据中的猫、狗图片各 500 张。每张图片的大小都是(160, 160, 3),每个 batch 中有 32 张图片。

import matplotlib.pyplot as plt
import numpy as np
import os
import tensorflow as tf
URL = 'https://storage.googleapis.com/mledu-datasets/cats_and_dogs_filtered.zip'
BATCH_SIZE = 32
IMG_SIZE = (160, 160)
path_to_zip = tf.keras.utils.get_file('cats_and_dogs.zip', origin=URL, extract=True)
PATH = os.path.join(os.path.dirname(path_to_zip), 'cats_and_dogs_filtered')
train_dir = os.path.join(PATH, 'train')
validation_dir = os.path.join(PATH, 'validation')
train_dataset = tf.keras.utils.image_dataset_from_directory(train_dir, shuffle=True, batch_size=BATCH_SIZE, image_size=IMG_SIZE)
validation_dataset = tf.keras.utils.image_dataset_from_directory(validation_dir, shuffle=True, batch_size=BATCH_SIZE, image_size=IMG_SIZE)
class_names = train_dataset.class_names

在这里我们挑选了部分图片和标签进行展示。

plt.figure(figsize=(10, 10))
for images, labels in train_dataset.take(1):
    for i in range(3):
        ax = plt.subplot(3, 3, i + 1)
        plt.imshow(images[i].numpy().astype("uint8"))
        plt.title(class_names[labels[i]])
        plt.axis("off")

2. 数据扩充与数据缩放

(1)由于我们没有测试集数据,使用 tf.data.experimental.cardinality 确定验证集中有多少个 batch 的数据,然后将其中的 20% 的 batch 编程测试集。

val_batches = tf.data.experimental.cardinality(validation_dataset)
test_dataset = validation_dataset.take(val_batches // 5)
validation_dataset = validation_dataset.skip(val_batches // 5)

(2)为了保证在加载数据的时候不会出现 I/O 不会阻塞,我们在从磁盘加载完数据之后,使用 cache 会将数据保存在内存中,确保在训练模型过程中数据的获取不会成为训练速度的瓶颈。如果说要保存的数据量太大,可以使用 cache 创建磁盘缓存提高数据的读取效率。另外我们还使用 prefetch 在训练过程中可以并行执行数据的预获取。

AUTOTUNE = tf.data.AUTOTUNE
train_dataset = train_dataset.prefetch(buffer_size=AUTOTUNE)
validation_dataset = validation_dataset.prefetch(buffer_size=AUTOTUNE)
test_dataset = test_dataset.prefetch(buffer_size=AUTOTUNE)

(3)因为我们没有大量的图片数据,我们将现有的图片数据进行旋转或者翻转操作可以增加样本的多样性,这样也有助于减少过拟合现象。RandomFlip 函数将根据 mode 属性将图片进行水平或垂直翻转图像。"可选的有 "horizontal" 、 "vertical" 或 "horizontal_and_vertical" 。"horizontal" 是左右翻转, "vertical" 是上下翻转。RandomRotation 函数通过设置 factor 来讲图片进行旋转,假如 factor=0.2 会将图片在 [-20% * 2pi, 20% * 2pi] 范围内随机旋转。

data_augmentation = tf.keras.Sequential([
  tf.keras.layers.RandomFlip('horizontal'),
  tf.keras.layers.RandomRotation(0.1),
])	

我们这里将随便使用一张图片,使用数据增强的方法对其进行变化,第一张图是原图,其他的都是进行了变化的图片

for image, _ in train_dataset.take(1):
    plt.figure(figsize=(10, 10))
    first_image = image[0]
    for i in range(3):
        if  i==0:
            ax = plt.subplot(3, 3, i + 1)
            plt.imshow(first_image/ 255)
            plt.axis('off')
        else:
            ax = plt.subplot(3, 3, i + 1)
            augmented_image = data_augmentation(tf.expand_dims(first_image, 0))
            plt.imshow(augmented_image[0] / 255)
            plt.axis('off')

(4)由于 MobileNetV2 模型的输入值范围是在 [-1, 1] 范围内,但此时我们的图片数据中的像素值处于 [0, 255] 范围内,所以要重新缩放这些像素值, Rescaling 函数可以实现该操作。如果将 [0, 255] 范围的输入缩放到 [0, 1] 的范围,我们可以通过设置参数 scale=1./255 来实现。如果将 [0, 255] 范围内的输入缩放到 [-1, 1] 范围内,我们可以通过设置参数 scale=1./127.5, offset=-1 来实现。

rescale = tf.keras.layers.Rescaling(1./127.5, offset=-1)	

3. 迁移学习

(1)我们将 MobileNet V2 模型当做一个基础模型,此模型已基于 ImageNet 数据集进行完美的预训练,一般来说最后一层就是一个分类器,我们选择将在 MobileNet V2 的倒数第二个网络层上搭建自己的分类器,与最后一层相比倒数第二层能够保留更丰富的图片特征。在实际操作中我们实例化 MobileNetV2 模型,通过指定 include_top=False 参数,可以加载不包括最顶层的整个预训练网络结果。 该模型的作用就是将(160,160,3)大小的图片转换为(5,5,1280)的特征输出。

(2)第一层是将我们的训练数据都进行数据增强操作,也就是随机的翻转和旋转。

(3)第二层是接收输入为 (160,160,3)大小的图片的输入层。

(4)第三层是我们直接拿来用的 MobileNetV2 ,因为我们要直接使用基础模型的图片特征提取能力,所以为了在训练过程中其权重不发生变化,我们将基础模型中的权重参数都冻结。

(5)第四层是一个池化层,可以将每个 batch 从 (32,5,5,1280) 压缩为 (32,1280) 大小的输出。

(6)第五层是 Dropout ,防止过拟合。

(7)第六层是对该图片一个预测值,也就是 logit 。如果是正数预测标签 1 ,如果是负数预测标签 0 。

(8)我们从模型的 summary 中可以看到,此时我们的模型中的 2259265 个参数被冻结,只有 1281 个参数是可以训练的,它们是分属两个变量的可训练参数,即最后一个全连接层即权重 1280 个可训练参数和偏差 1 个可训练参数。

IMG_SHAPE = IMG_SIZE + (3,)
inputs = tf.keras.Input(shape=(160, 160, 3))
x = data_augmentation(inputs)
x = tf.keras.applications.mobilenet_v2.preprocess_input(x)
base_model = tf.keras.applications.MobileNetV2(input_shape=IMG_SHAPE, include_top=False, weights='imagenet')
base_model.trainable = False
x = base_model(x, training=False)
x = tf.keras.layers.GlobalAveragePooling2D()(x)
x = tf.keras.layers.Dropout(0.2)(x)
outputs = tf.keras.layers.Dense(1)(x)
model = tf.keras.Model(inputs, outputs)
model.summary()

模型结构如下:

Model: "model_2"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 input_8 (InputLayer)        [(None, 160, 160, 3)]     0         
 sequential_2 (Sequential)   (None, 160, 160, 3)       0         
 tf.math.truediv_3 (TFOpLamb  (None, 160, 160, 3)      0         
 da)                                                             
 tf.math.subtract_3 (TFOpLam  (None, 160, 160, 3)      0         
 bda)                                                            
 mobilenetv2_1.00_160 (Funct  (None, 5, 5, 1280)       2257984   
 ional)                                                          
 global_average_pooling2d_3   (None, 1280)             0         
 (GlobalAveragePooling2D)                                        
 dropout_2 (Dropout)         (None, 1280)              0         
 dense_2 (Dense)             (None, 1)                 1281      
=================================================================
Total params: 2,259,265
Trainable params: 1,281
Non-trainable params: 2,257,984

可以看到模型中的可训练的 2 个变量:

model.trainable_variables

结果如下:

[<tf.Variable 'dense_2/kernel:0' shape=(1280, 1) dtype=float32, numpy=
 array([[ 0.08899798],
        [-0.06681276],
        [ 0.00906871],
        ...,
        [-0.00114891],
        [-0.01134416],
        [-0.02000826]], dtype=float32)>,
 <tf.Variable 'dense_2/bias:0' shape=(1,) dtype=float32, numpy=array([0.03746362], dtype=float32)>]    

(9)选择 Adam 优化器,将我们的学习率设置为 0.0003 。选择 BinaryCrossentropy 作为损失函数。选择常规的 accuracy 作为评估指标。此时我们先对基础模型训练 10 个 epoch 。

lr = 0.0003
initial_epochs = 10
model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=lr),
              loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
              metrics=['accuracy'])
history = model.fit(train_dataset, validation_data=test_dataset, epochs=initial_epochs)

训练过程如下:

Epoch 1/10
63/63 [==============================] - 24s 352ms/step - loss: 0.5517 - accuracy: 0.6835 - val_loss: 0.2720 - val_accuracy: 0.8958
Epoch 2/10
63/63 [==============================] - 21s 327ms/step - loss: 0.2792 - accuracy: 0.8865 - val_loss: 0.1499 - val_accuracy: 0.9531
...
Epoch 9/10
63/63 [==============================] - 20s 321ms/step - loss: 0.1075 - accuracy: 0.9530 - val_loss: 0.0766 - val_accuracy: 0.9740
Epoch 10/10
63/63 [==============================] - 21s 329ms/step - loss: 0.1040 - accuracy: 0.9560 - val_loss: 0.0742 - val_accuracy: 0.9740

(10)使用测试数据对模型进行评估。

model.evaluate(validation_dataset)

结果如下:

loss: 0.0664 - accuracy: 0.9765

4. 微调

(1)在上面的操作中,我们仅仅在 MobileNetV2 基础模型的顶部添加了一层池化层、一层 Dropout、一层全连接层作为我们的自定义分类器。预训练模型 MobileNetV2 中权重在训练过程中未曾发生过更新。

(2)我们还有一种被称为微调的方法,可以在训练基础模型权重的同时,同时训练我们上面自定义添加的用于分类的分类器。这个微调的训练过程可以将基础模型的通用的图片特征提取能力调整为专门提取本任务中猫狗数据集特征的能力。这个微调的操作只能在我们进行了上面的迁移学习操作之后才能进行,否则如果一开始直接将基础模型和我们自定义的若干层分类器一起进行训练,则会由于随机初始化的分类器导致整个模型的更新梯度太大,从而使得基础模型丧失了其预训练的有效能力。其次在微调过程中我们应该选择性地去微调少量顶部的网络层而不是整个 MobileNet 模型,因为在卷积神经网络中,低层的网络层一般捕获到的是通用的图片特征,这个能力可以泛化应用到几乎所有类型的图片,但是越往顶部的网络层,越来越聚焦于捕获训练时所用到的训练数据的特征,而微调的目标正是是让这个模型更加适用于所用的专门的数据集,也就是本次进行的猫狗图片。

(3)MobileNetV2 模型一共有 154 层结构,我们将模型的前 100 层的参数进行冻结,对顶部的 54 层网络结构中的参数与我们自定义的分类器的若干层一起进行训练,我们通过打印模型的 summary 可以看到,此时共有 54 个可以训练的变量,这些变量中共有可训练的参数 1862721 个。

base_model.trainable = True
fine_tune_at = 100
for layer in base_model.layers[:fine_tune_at]:
    layer.trainable = False
print("%d trainable variables "%len(model.trainable_variables))
model.summary()

结果如下:

56 trainable variables 
Model: "model_2"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 input_8 (InputLayer)        [(None, 160, 160, 3)]     0         
 sequential_2 (Sequential)   (None, 160, 160, 3)       0         
 tf.math.truediv_3 (TFOpLamb  (None, 160, 160, 3)      0         
 da)                                                             
 tf.math.subtract_3 (TFOpLam  (None, 160, 160, 3)      0         
 bda)                                                            
 mobilenetv2_1.00_160 (Funct  (None, 5, 5, 1280)       2257984   
 ional)                                                          
 global_average_pooling2d_3   (None, 1280)             0         
 (GlobalAveragePooling2D)                                        
 dropout_2 (Dropout)         (None, 1280)              0         
 dense_2 (Dense)             (None, 1)                 1281      
=================================================================
Total params: 2,259,265
Trainable params: 1,862,721
Non-trainable params: 396,544

(4)之前我们对基础模型训练了 10 个 epoch ,现在我们进行微调过程中再对模型训练 10 个 epoch ,且我们将从上面训练结束的 epoch 开始恢复并进行现在的训练过程。

(5)这里我们改用 RMSprop 优化器,且因为此时我们是要对整体模型进行微调,所以设置的学习率比之前降低 10 倍,否则如果较大会很快产生过拟合。

fine_tune_epochs = 10
total_epochs =  initial_epochs + fine_tune_epochs
model.compile(loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
              optimizer = tf.keras.optimizers.RMSprop(learning_rate=lr/10),
              metrics=['accuracy'])
history_fine = model.fit(train_dataset,  epochs=total_epochs,  initial_epoch=history.epoch[-1],  validation_data=validation_dataset)	

训练结果输出:

Epoch 10/20
63/63 [==============================] - 39s 561ms/step - loss: 0.1073 - accuracy: 0.9570 - val_loss: 0.1017 - val_accuracy: 0.9592
Epoch 11/20
63/63 [==============================] - 34s 538ms/step - loss: 0.0688 - accuracy: 0.9725 - val_loss: 0.0448 - val_accuracy: 0.9827
...
Epoch 19/20
63/63 [==============================] - 34s 537ms/step - loss: 0.0244 - accuracy: 0.9900 - val_loss: 0.0709 - val_accuracy: 0.9777
Epoch 20/20
63/63 [==============================] - 33s 528ms/step - loss: 0.0220 - accuracy: 0.9905 - val_loss: 0.0566 - val_accuracy: 0.9851

(6)使用测试数据对模型进行评估。

model.evaluate(test_dataset)

结果输出:

loss: 0.0544 - accuracy: 0.9792

之前模型最后的验证准确率为 0.9740 ,测试准确率为 0.9765 ,而经过微调的模型,最后饿验证准确率为 0.9851 ,测试准确率为 0.9792 ,都有所提升。

5. 预测

我们随机挑选一个 batch 进行预测,并将图片与预测标签进行显示,结果表明预测全都正确。

image_batch, label_batch = test_dataset.as_numpy_iterator().next()
predictions = model.predict_on_batch(image_batch).flatten()
predictions = tf.nn.sigmoid(predictions)
predictions = tf.where(predictions < 0.5, 0, 1)
plt.figure(figsize=(10, 10))
for i in range(9):
    ax = plt.subplot(3, 3, i + 1)
    plt.imshow(image_batch[i].astype("uint8"))
    plt.title(class_names[predictions[i]])
    plt.axis("off")

关于“Tensorflow2.4怎么完成迁移和模型微调”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注亿速云行业资讯频道,小编每天都会为大家更新不同的知识点。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI