使用预训练网络(迁移学习)
预训练网络是一个保存好的之前已在大型数据集(大规模图像分类任务)上训练好的卷积神经网络
如果这个原始数据集足够大且足够通用,那么预训练网络学到的特征的空间层次结构可以作为有效的提取视觉世界特征的模型。
即使新问题和新任务与原始任务完全不同学习到的特征在不同问题之间是可移植的,这也是深度学习与浅层学习方法的一个重要优势。它使得深度学习对于小数据问题非常的有效。
Keras内置预训练网络Keras库中包含VGG16、VGG19ResNet50、Inception v3、Xception等经典的模型架构。
-
ImageNet
-
ImageNet是一个手动标注好类别的图片数据库(为了机器视
觉研究),目前已有22,000个类别。 -
当我们在深度学习和卷积神经网络的背景下听到“ImageNet”一词时,我们可能会提到ImageNet视觉识别比赛,称为ILSVRC。
-
这个图片分类比赛是训练一个模型,能够将输入图片正确分
类到1000个类别中的某个类别。训练集120万,验证集5万,测试集10万。 -
这1,000个图片类别是我们在日常生活中遇到的,例如狗,猫,各种家居物品,车辆类型等等。
-
在图像分类方面,ImageNet比赛准确率已经作为计算机视觉分类算法的基准。自2012年以来,卷积神经网络和深度学习技术主导了这一比赛的排行榜。
-
-
VGG16与VGG19
-
在2014年,VGG模型架构由Simonyan和Zisserman提出,在“极深的大规模图像识别卷积网络”(Very Deep Convolutional Networks for Large Scale Image Recognition)这篇论文中有介绍
-
VGG模型结构简单有效,前几层仅使用3×3卷积核来增加网络深度,通过max pooling(最大池化)依次减少每层的神经元数量,最后三层分别是2个有4096个神经元的全连接层和一个输出softmax层。
-
-
VGG有两个很大的缺点
-
- 网络架构weight数量相当大,很消耗磁盘空间。
-
- 训练非常慢
由于其全连接节点的数量较多,再加上网络比较深,VGG16有533MB+,VGG19有574MB。这使得部署VGG比较耗时。
- 训练非常慢
-
-
微调
-
所谓微调:冻结模型库的底部的卷积层,共同训练新添加的分类器层和顶部部分卷积层。这允许我们“微调”基础模型中的高阶特征表示,以使它们与特定任务更相关。。
-
只有分类器已经训练好了,才能微调卷积基的顶部卷积层。如果有没有这样的话,刚开始的训练误差很大,微调之前这些卷积层学到的表示会被破坏掉
-
-
微调步骤
- 一、在预训练卷积基上添加自定义层
- 二、冻结卷积基所有层
- 三、训练添加的分类层
- 四、解冻卷积基的一部分层
- 五、联合训练解冻的卷积层和添加的自定义层
案例
import tensorflow as tf
from tensorflow import keras
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
import glob
import os
gpu_ok = tf.test.is_gpu_available()
print("tf version:", tf.__version__)
print("use GPU", gpu_ok) # 判断是否使用gpu进行训练
# 定义好keras,layers方便后面调用
keras = tf.keras
layers = tf.keras.layers
train_image_path = glob.glob("./dc/maogou/*.jpg")
test_image_path = glob.glob("./dc/maogou_test/*.jpg") # 获取图片数据集
len(train_image_path),len(test_image_path)
train_image_path[995:1005],test_image_path[593:600]
# 定义目标值 如果是猫 = 1 是狗 = 0
train_image_label = [int(p.split("\")[1].split(".")[0]=="cat") for p in train_image_path]
test_image_label = [int(p.split("\")[1].split(".")[0]=="cat") for p in test_image_path]
train_image_label[995:1005],test_image_label[593:600]
# 图片加载与预处理
def load_preprosess_image(path,lable):
image = tf.io.read_file(path) # 读取图片路径
image = tf.image.decode_jpeg(image,channels=3) # 对图片进行解码(jpeg格式图片,channels=3 将读入图片统一为三通道)
image = tf.image.resize(image,[256,256])# 对图片进行变形360*360像素(非裁剪)
image = tf.cast(image,tf.float32) # 改变图片格式
image = image/255 # 对图片进行归一化
#lable = tf.reshape(lable,[1]) # 把目标值转换成2维形状 如:[1,2,3] =>[[1],[2],[3]]
return image,lable
# 创建数据集 (包含了 路径 以及目标值)
train_image_ds = tf.data.Dataset.from_tensor_slices((train_image_path,train_image_label))
test_image_ds = tf.data.Dataset.from_tensor_slices((test_image_path,test_image_label))
AUTOTUNE = tf.data.experimental.AUTOTUNE # AUTOTUNE 根据计算机cpu个数自动进行计算
train_image_ds = train_image_ds.map(load_preprosess_image,num_parallel_calls=AUTOTUNE) # 对所有图片以及目标值进行计算 使用AUTOTUNE 根据计算机cpu个数自动进行计算
test_image_ds = test_image_ds.map(load_preprosess_image,num_parallel_calls=AUTOTUNE)
train_image_ds ,test_image_ds# 查看处理后图片的格式
for img,label in train_image_ds.take(2):
plt.imshow(img)
for img,label in test_image_ds.take(600):
plt.imshow(img)
BATCH_SIZE = 64 # 中文为批,一个batch由若干条数据构成。batch是进行网络优化的基本单位,网络参数的每一轮优化需要使用一个batch。
# batch中的样本是被并行处理的。与单个样本相比,一个batch的数据能更好的模拟数据集的分布,
#batch越大则对输入数据分布模拟的越好,反应在网络训练上,则体现为能让网络训练的方向“更加正确”。
#但另一方面,一个batch也只能让网络的参数更新一次,因此网络参数的迭代会较慢。
#在测试网络的时候,应该在条件的允许的范围内尽量使用更大的batch,这样计算效率会更高。
train_count = len(train_image_path) # 查看数据的个数
test_count = len(test_image_path)
train_count,test_count
train_image_ds = train_image_ds.shuffle(train_count).repeat().batch(BATCH_SIZE) # 乱序
#train_image_ds = train_image_ds.prefetch(AUTOTUNE) # prefetch 该函数有一个后台线程和一个内部缓存区,在数据被请求前,
# 就从 dataset 中预加载一些数据(进一步提高性能) prefetch(AUTOTUNE) 根据计算机性能自动分配数据条数
test_image_ds = test_image_ds.repeat().batch(BATCH_SIZE)
#test_image_ds = test_image_ds.prefetch(AUTOTUNE)
使用keras内置经典网络实现
# weights="imagenet"使用预训练权重,include_top= False不包含全链接层
covn_base = keras.applications.VGG16(weights="imagenet",include_top= False)
covn_base.summary()
# 添加全连接层
model = keras.Sequential() # 建立一个顺序模型
model.add(covn_base) # 添加vgg16卷积机
model.add(layers.GlobalAveragePooling2D()) # 全局平均池化,深度神经网络中经常使用的一个层,使用前后的
#尺寸分别为[B,H,W,C]->[B,C].特别要注意,这个层使用之后特征图尺寸的维度变成了2维而非4维。
model.add(layers.Dense(512,activation="relu")) # 输出512个单元
model.add(layers.Dense(1,activation="sigmoid")) # 输出1个单元,sigmoid概率值
model.summary() # Trainable params: 14,977,857
covn_base.trainable = False # 因covn_base已经训练好了所以我们设置false不可训练(冻结)
model.summary() # 设置了covn_base.trainable = False后再次查看模型建构Trainable params: 263,169
# 编译模型
model.compile(optimizer=keras.optimizers.Adam(lr=0.001),
loss="binary_crossentropy",
metrics=["acc"])
# 训练模型
history = model.fit(
train_image_ds,
steps_per_epoch=train_count//BATCH_SIZE,
epochs=12,
validation_data=test_image_ds,
validation_steps=test_count//BATCH_SIZE)
微调
covn_base.trainable = True # 解冻所有卷积层
len(covn_base.layers) # 一共19个层
fine_tune_at = -3 # 定义倒数3层
for layers in covn_base.layers[:fine_tune_at]:
layers.trainable = False # 除了倒数3层其他层全部冻结
# 编译模型
model.compile(optimizer=keras.optimizers.Adam(lr=0.0005/10), # 使用更低学习速率
loss="binary_crossentropy",
metrics=["acc"])
# 训练模型
initial_epochs = 12 # 前面训练了12次
fine_tune_epochs = 10 # 对新的数据训练10次
total_epochs = initial_epochs+fine_tune_epochs # 总共次数
history = model.fit(
train_image_ds,
steps_per_epoch=train_count//BATCH_SIZE,
epochs=total_epochs, # 总共需要训练的次数
initial_epoch=initial_epochs, # 初始化时已经训练过多少次
validation_data=test_image_ds,
validation_steps=test_count//BATCH_SIZE)
常见预训练网络
-
可用的模型
在 ImageNet 上预训练过的用于图像分类的模型:
VGG16 VGG19 ResNet50
InceptionV3 InceptionResNetV2 Xception
MobileNet MobileNetV2
DenseNet NASNet -
模型概览
-
Xception
使用实例:
tf.keras.applications.xception.Xception(
include_top=True, weights='imagenet', input_tensor=None,
input_shape=None, pooling=None, classes=1000
)
在 ImageNet 上预训练的 Xception V1 模型,在 ImageNet 上,该模型取得了验证集 top1 0.790 和 top5 0.945 的准确率。
注意该模型只支持 channels_last 的维度顺序(高度、宽度、通道)。
模型默认输入尺寸是 299x299。
- Xception重要参数
pooling: 可选,
当 include_top 为 False 时,该参数指定了特征提取时的池化方式。
None 代表不池化,直接输出最后一层卷积层的输出,该输出是一个 4D 张量。
‘avg’ 代表全局平均池化(GlobalAveragePooling2D),相当于在最后一层卷积层后面再加一层全局平均池化层,输出是一个 2D 张量。
‘max’ 代表全局最大池化。
其他训练网络参数参数添加链接描述
案例
import tensorflow as tf
from tensorflow import keras
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
import glob
import os
os.environ['TF_FORCE_GPU_ALLOW_GROWTH'] = 'true'
gpu_ok = tf.test.is_gpu_available()
print("tf version:", tf.__version__)
print("use GPU", gpu_ok) # 判断是否使用gpu进行训练
keras = tf.keras
layers = tf.keras.layers
train_image_path = glob.glob("./dc/maogou/*.jpg")
test_image_path = glob.glob("./dc/maogou_test/*.jpg") # 获取图片数据集
len(train_image_path),len(test_image_path)
train_image_path[995:1005],test_image_path[593:600]
# 定义目标值 如果是猫 = 1 是狗 = 0
train_image_label = [int(p.split("\")[1].split(".")[0]=="cat") for p in train_image_path]
test_image_label = [int(p.split("\")[1].split(".")[0]=="cat") for p in test_image_path]
train_image_label[995:1005],test_image_label[593:600]
# 图片加载与预处理
def load_preprosess_image(path,lable):
image = tf.io.read_file(path) # 读取图片路径
image = tf.image.decode_jpeg(image,channels=3) # 对图片进行解码(jpeg格式图片,channels=3 将读入图片统一为三通道)
image = tf.image.resize(image,[256,256])# 对图片进行变形360*360像素(非裁剪)
image = tf.cast(image,tf.float32) # 改变图片格式
image = image/255 # 对图片进行归一化
#lable = tf.reshape(lable,[1]) # 把目标值转换成2维形状 如:[1,2,3] =>[[1],[2],[3]]
return image,lable
# 创建数据集 (包含了 路径 以及目标值)
train_image_ds = tf.data.Dataset.from_tensor_slices((train_image_path,train_image_label))
test_image_ds = tf.data.Dataset.from_tensor_slices((test_image_path,test_image_label))
AUTOTUNE = tf.data.experimental.AUTOTUNE # AUTOTUNE 根据计算机cpu个数自动进行计算
train_image_ds = train_image_ds.map(load_preprosess_image,num_parallel_calls=AUTOTUNE) # 对所有图片以及目标值进行计算 使用AUTOTUNE 根据计算机cpu个数自动进行计算
test_image_ds = test_image_ds.map(load_preprosess_image,num_parallel_calls=AUTOTUNE)
train_image_ds ,test_image_ds# 查看处理后图片的格式
BATCH_SIZE = 64 # 中文为批,一个batch由若干条数据构成。batch是进行网络优化的基本单位,网络参数的每一轮优化需要使用一个batch。
# batch中的样本是被并行处理的。与单个样本相比,一个batch的数据能更好的模拟数据集的分布,
#batch越大则对输入数据分布模拟的越好,反应在网络训练上,则体现为能让网络训练的方向“更加正确”。
#但另一方面,一个batch也只能让网络的参数更新一次,因此网络参数的迭代会较慢。
#在测试网络的时候,应该在条件的允许的范围内尽量使用更大的batch,这样计算效率会更高。
train_count = len(train_image_path) # 查看数据的个数
test_count = len(test_image_path)
train_image_ds = train_image_ds.shuffle(train_count).repeat().batch(BATCH_SIZE) # 乱序
#train_image_ds = train_image_ds.prefetch(AUTOTUNE) # prefetch 该函数有一个后台线程和一个内部缓存区,在数据被请求前,
# 就从 dataset 中预加载一些数据(进一步提高性能) prefetch(AUTOTUNE) 根据计算机性能自动分配数据条数
test_image_ds = test_image_ds.repeat().batch(BATCH_SIZE)
#test_image_ds = test_image_ds.prefetch(AUTOTUNE)
# 建立卷积机 weights="imagenet"使用预训练权重,include_top= False不包含全链接层
covn_base = keras.applications.xception.Xception(weights="imagenet",
include_top= False,
input_shape=(256,256,3),
pooling="avg")
covn_base.trainable = False # 因covn_base已经训练好了所以我们设置false不可训练(冻结)
covn_base.summary()
# 添加全连接层
model = keras.Sequential() # 建立一个顺序模型
model.add(covn_base) # 添加vgg16卷积机
model.add(layers.Dense(512,activation="relu")) # 输出512个单元
model.add(layers.Dense(1,activation="sigmoid")) # 输出1个单元,sigmoid概率值
model.summary() #
# 编译模型
model.compile(optimizer=keras.optimizers.Adam(lr=0.001),
loss="binary_crossentropy",
metrics=["acc"])
initial_epochs = 5
# 训练模型
history = model.fit(
train_image_ds,
steps_per_epoch=train_count//BATCH_SIZE,
epochs=initial_epochs,
validation_data=test_image_ds,
validation_steps=test_count//BATCH_SIZE)
微调
covn_base.trainable = True # 解冻所有卷积层
len(covn_base.layers) # 总层数
fine_tune_at = -33 # 定义倒数33层
for layers in covn_base.layers[:fine_tune_at]:
layers.trainable = False # 除了倒数33层其他层全部冻结
# 编译模型
model.compile(optimizer=keras.optimizers.Adam(lr=0.0005/10), # 使用更低学习速率
loss="binary_crossentropy",
metrics=["acc"])
# 训练模型
fine_tune_epochs = 5 # 微调后再次训练5次
total_epochs = initial_epochs+fine_tune_epochs # 总共次数
history = model.fit(
train_image_ds,
steps_per_epoch=train_count//BATCH_SIZE,
epochs=total_epochs, # 总共需要训练的次数
initial_epoch=initial_epochs, # 初始化时已经训练过多少次
validation_data=test_image_ds,
validation_steps=test_count//BATCH_SIZE)