机器视觉基于卷积神经网络之VGG迁移学习
源码在文末,可直接运行
迁移学习
迁移学习:利用数据、任务和模型之间的了解,将旧领域中学习或训练的模型应用到新领域的过程。
注意:这两个任务的输入具有相同的性质:图像或语音或其他内容。
原模型(具有较好的基础参数)--->修改原模型的输出结果--->得到新的模型(适用于新的情形)
- 当有海量数据资源时,不能使用迁移学习。可以使用深度学习直接训练稳健的模型
- 迁移学习可以降低训练成本,站在巨人的肩膀上,从头训练需要较长时间且需要依赖较大的GPU资源。
微调(fine-tuning)
微调:
- 无需大量调整即可调整模型参数
- 调整模型结构,无需大量调整
预训练模型(pre-trained model):就是用来进行迁移学习的样本模型。
https://github.com/tensorflow/models/tree/master/research/slim
迁移学习步骤
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4c4fIGlU-1648475306806)(picture/img1.png)]
- 搭建自己的网络,根据预训练好的模型修改最终输出结构,加载并训练模型的模型参数
- 根据数据大小调整
- 如果待训练模型数据量小,那么我们可以选择将预训练模型的所有的层进行freeze(可以通过Tensorflow的trainable=False参数实现),而剩下的输出层部分可以选择调整参数训练。
- 如果待训练模型数据量大,那么我们可以将预训练模型中一半或者大部分的层进行freeze,而剩下的部分的layer可以进行新任务数据基础的微调。
VGG
该项目基于现有VGG模型
- 准备训练和测试集
- 修改模型分类层
- freeze原始VGG模型
- 训练模型
- 输入数据以进行预测
准备训练和测试集
相关api
- ImageDataGenerator()
- 定义数据转换和数据扩充
- 为图像产生批量张量值并提供数据增强
- rescale=1.0 / 255,:标准化
- zca_whitening=False: # zca白化的作用是针对图片进行PCA降维操作,减少图片的冗余信息
- rotation_range=20:默认0, 旋转角度,在这个角度范围随机生成一个值
- width_shift_range=0.2,:默认0,水平平移
- height_shift_range=0.2:默认0, 垂直平移
- shear_range=0.2:# 平移变换
- zoom_range=0.2:
- horizontal_flip=True:水平翻转
- train_generator = ImageDataGenerator()
- flow(x, y, batch_size)
- 直接从文件中读取
- flow_from_directory(
- directory=path,# 读取目录
- target_size=(h,w),# 目标形状
- batch_size=size,# 批数量大小
- class_mode=‘binary’, # 目标值格式
- shuffle=True)
- 从本地读取
- 该api要求数据存储格式固定
data/
train/
dogs/
dog001.jpg
dog002.jpg
...
cats/
cat001.jpg
cat002.jpg
...
validation/
dogs/
dog001.jpg
dog002.jpg
...
cats/
cat001.jpg
cat002.jpg
...
注意这里在进行数据增强后,在训练的时候也要将增强数据放进去,该使用fit_generator(),而不是fit()
修改模型分类层
VGG-notop模型
运行代码后
在根目录下有一个.keras
,其中的models
文件夹中就生成一个VGG的notop模型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z26HHEK4-1648475306807)(picture/img.png)]
- notop模型不包含最后三个全连接层,专门用来fine-tuning,不需要我们来自主进行删除
VGG16源码片
if include_top:
# Classification block
x = layers.Flatten(name='flatten')(x)
x = layers.Dense(4096, activation='relu', name='fc1')(x)
x = layers.Dense(4096, activation='relu', name='fc2')(x)
imagenet_utils.validate_activation(classifier_activation, weights)
x = layers.Dense(classes, activation=classifier_activation,
name='predictions')(x)
else:
if pooling == 'avg':
x = layers.GlobalAveragePooling2D()(x)
elif pooling == 'max':
x = layers.GlobalMaxPooling2D()(x)
# Ensure that the model takes into account
# any potential predecessors of `input_tensor`.
if input_tensor is not None:
inputs = layer_utils.get_source_inputs(input_tensor)
else:
inputs = img_input
# Create model.
model = training.Model(inputs, x, name='vgg16')
# Load weights.
if weights == 'imagenet':
if include_top:
weights_path = data_utils.get_file(
'vgg16_weights_tf_dim_ordering_tf_kernels.h5',
WEIGHTS_PATH,
cache_subdir='models',
file_hash='64373286793e3c8b2b4e3219cbf3544b')
else:
weights_path = data_utils.get_file(
'vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5',
WEIGHTS_PATH_NO_TOP,
cache_subdir='models',
file_hash='6d6bbae143d832006294945121d1f1fc')
model.load_weights(weights_path)
elif weights is not None:
model.load_weights(weights)
return model
下面是VGG16(imgenet)模型报告
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Eap5iMzo-1648475306807)(picture/img2.png)]
全局平均池化(GobalAveragePooling2D)
在迁移学习中,只需要训练后的全连接层,所以不需要大量的参数,使用过多的参数会导致过拟合。
例如:输出大少为shape=[8,8,2048],通过全局平均池化后,将88的64位向量进行取均值,所以原本的882048就转变成了12048
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KyBGK8e4-1648475306808)(picture/pooling.png)]
冻结预训练模型
如果我们模型的测试集很大,可以把模型的所有参数取出来一起训练;
如果我们模型的测试集很小,那么模型的前层就被冻结了,只训练全连接层的参数
做法:将要冻结的每层中的trainable参数设置为False
layer.trainable = False
# 将基类模型中的每一层进行冻结
for layer in self.base_model.layer:
layer.trainable = False
训练模型
- 编译模型
- 优化器:Adam
- optimizer=keras.optimizers.Adam()
- loss损失计算:交叉熵损失
- loss=keras.losses.sparse_categorical_crossentropy
- 评估矩阵
- metrics=[‘accuracy’]
- 训练模型
- 数据增强过,需要使用fit_generator()
- 在fit_generator()的callbacks中可以加入ModelCheckpoint进行记录每次迭代的准确率,fit()是加不进去这个monitor的
- 在新版本的tensorflow中fit_generator()已经被弃用了,fit()合并了fit_generator()的作用
ModelCheckpoint
- 这里检测验证准确率“val_acc”(注意在新版本中,这里可能要改成“val_accuracy”)
- 只保留权重参数
- 只保留最好的模型
- 自动检测最大准确率与最小的loss
modelckpt = keras.callbacks.ModelCheckpoint('./ckpt/transfer_{epoch:02d}-{val_acc:.2f}.h5',
monitor='val_acc',
save_weights_only=True,
save_best_only=True,
mode='auto',
period=1)
输入数据以进行预测
训练好模型后,需要保存模型,然后加载模型使用
- 保存模型
model.save_weights("./xxx.h5")
- 负载模型
model.load_weights("./xxx.h5")
源代码
项目源码_Github
运行环境:tensorflow-2.3.0,keras-2.8.0
文章出处登录后可见!