这一节我们将用Tensorflow2.0完成一个图像领域处理的重要任务,即是“迁移学习”。迁移学习简单来说就是一个预训练的模型(已经在别的数据集上训练过的)重新使用在另一个数据集或任务中。迁移学习不仅大大减小了我们的新数据集的训练时间和难度,而且使得模型的泛化能力更强。那么这一节课我们就通过迁移学习来完成一个猫狗分类的例子。关于迁移学习的底层原理或更多信息,朋友们可以观看其它博主更详细的博客,或者博主在后续的博客更新中也会提到更深的原理部分。
顺便附上Tensorflow官方教程的链接:https://tensorflow.google.cn/tutorials/images/transfer_learning。
一.数据集的加载
1.导入相关库。
from __future__ import absolute_import, division, print_function, unicode_literals
import os
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
import tensorflow_datasets as tfds
import tensorflow.contrib.eager as tfe
2.允许Tensorflow的eager execution模块运行。
tf.enable_eager_execution()
3.数据集的划分和加载。其中(8,1,1)分别代表训练集、测试集和验证集的比例。tfds.load中的with_info如果是true代表返回的数据集是一个元组,里面不仅仅包含了数据集还包含了和数据集有关的一切信息。而as_supervised=True则代表返回的是数据集特征+标签,如果是False那么只仅仅返回了数据集特征而没有标签。
split_weights = (8,1,1) #数据划分的比例
splits = tfds.Split.TRAIN.subsplit(weighted=split_weights)
(raw_train,raw_validation,raw_test),metadata = tfds.load('cats_vs_dogs',split=list(splits),with_info=True,as_supervised=True)
4.查看切分的数据集信息。
print(raw_train)
print(raw_validation)
print(raw_test)
可以看出返回的types中包含了unit8(图片)和int64(标签)。
5.将标签转为字符串并定义显示函数查看图片。
get_label_name = metadata.features['label'].int2str #数字转字符串
for image, label in raw_train.take(2): #显示2张图片
plt.figure()
plt.imshow(image)
plt.title(get_label_name(label))
6.定义图片预处理函数并用tf.dataset相关的映射函数对数据集进行预处理以及划定batch_size的大小。
IMG_SIZE =100
def format_example(image,label):
image = tf.cast(image,tf.float32) #将图片转为float32格式
image = image/255.0 #图片归一化
image = tf.image.resize(image,(IMG_SIZE,IMG_SIZE)) #修改图片尺寸
return image,label
train = raw_train.map(format_example) #将预处理函数映射到数据中
valiadation = raw_validation.map(format_example)
test = raw_test.map(format_example)
batch_size = 32
shuffle_buffer_size = 1000 #训练缓存空间大小
train_batches = train.shuffle(shuffle_buffer_size).batch(batch_size)
valiadation_batches = valiadation.batch(batch_size)
test_batches = test.batch(batch_size)
7.查看经过预处理后,图片的尺寸。
for image_batch,label_batch in train_batches.take(1): #取一张图片
pass
image_batch.shape
可以看出图片尺寸已成功被预处理了。
二.预训练模型的加载
1.在这次训练迁移学习中,我们使用google的预训练后的MobileNetV2来作为base_model,在这里我们的include_top参数设为False,因为我们只需要预训练模型的前面层,而最后一层(分类的输出个数)必须要满足我们现在任务的需求,因此需要重新定义。而weight='imagenet'的意思则为,该预训练模型的权重是采用在imagenet数据上训练后的权重。
image_shape = (IMG_SIZE,IMG_SIZE,3)
base_model = tf.keras.applications.MobileNetV2(input_shape=image_shape,include_top=False,weights='imagenet')
2.查看一个image_batch输入模型后的输出是什么样的。
feature_batch = base_model(image_batch)
print(feature_batch.shape)
3.将模型的可训练模式设为False。这里的意思是说我模型训练时权重参数不可再改变,冻结所有层并以之前imagenet训练的权重参数进行训练。
base_model.trainable = False
4.定义top层来满足我们本次任务的需要(分类个数),原因看第1点。这里globalaveragepooling2D层相当于全连接层,基本在迁移学习中都是用它来将前面特征数量转为单维向量。
global_average_layer = tf.keras.layers.GlobalAveragePooling2D()
feature_batch_average = global_average_layer(feature_batch) #输入该层并查看输出
print(feature_batch_average.shape)
5.定义最后的分类结果层并查看输出结果。
prediction_layer = tf.keras.layers.Dense(1)
prediction_batch = prediction_layer(feature_batch_average)
print(prediction_batch.shape)
6.将base_model和后面top层混合一起。
model = tf.keras.Sequential([
base_model,
global_average_layer,
prediction_layer
])
三.模型的参数设置及训练。
1.模型相关参数设置。因为为二分类任务(猫和狗),因此loss函数为binary_crossentropy。
base_learning_rate = 0.0001
model.compile(optimizer=tf.keras.optimizers.RMSprop(lr=base_learning_rate),
loss='binary_crossentropy',
metrics=['accuracy'])
2.打印模型概要。
model.summary()
3.设置模型训练次数。为了加快训练时间,这里epochs选为2。
initial_epochs = 2
steps_per_epoch = 10
validation_steps = 20
4.模型训练。
history = model.fit(train_batches,epochs=initial_epochs,validation_data=valiadation_batches)
5.定义函数训练过程显示函数并查看结果曲线。
def show_train_history(train_history,train,validation):
plt.plot(train_history.history[train]) #绘制训练数据的执行结果
plt.plot(train_history.history[validation]) #绘制验证数据的执行结果
plt.title('Train History') #图标题
plt.xlabel('epoch') #x轴标签
plt.ylabel(train) #y轴标签
plt.legend(['train','validation'],loc='upper left') #添加左上角图例
show_train_history(history,'acc','val_acc')
四.对模型进行微调并训练。
我们还可以通过微调的方式提高模型的训练成功率。什么是微调?这里指的是冻结预训练模型MobileNetV2的基础层(基础层提取的图片特征基本适用于大部分图片,所以不微调这部分),微调它的后面一些重要的分类层,对它们进行解冻然后训练。
1.将模型首先设为可训练的,然后查看模型的总层数,设置101层-155层为可训练,前面100层为不可训练。
base_model.trainble = True
print(len(base_model.layers))
fine_tune_at = 100
for layer in base_model.layers[:fine_tune_at]:
layer.trainble = False
2.模型参数重新设置,调低学习率。
model.compile(loss='binary_crossentropy',optimizer=tf.keras.optimizers.RMSprop(lr=base_learning_rate/10),metrics=['accuracy'])
3.模型重新训练次数设置。训练次数同样为2次。
fine_tune_epochs=2
total_epochs = initial_epochs + fine_tune_epochs
4.模型重新训练。这边initial_epoch的意思为从上次的训练之后再接着训练。
history_fine = model.fit(train_batches,epochs=total_epochs,initial_epoch=history.epoch[-1],validation_data=valiadation_batches)
5.查看训练结果曲线。
本节分享了一个迁移学习的实例,朋友们如果有兴趣还可以试试其它的pretrained的base_model并看看结果如何。谢谢你们的观看和支持!