源码于文末,可直接运行
迁移学习: 利用数据、任务和模型间的相识性,将旧的领域学习过或训练好的模型,应用于新的领域这样的一个过程。
注意:这两个任务的输入属于同一性质:都是图像或者都是语音或者都是其他别的。
原模型(具有较好的基础参数)--->修改原模型的输出结果--->得到新的模型(适用于新的情形)
微调:
预训练模型(pre-trained model): 就是用来进行迁移学习的样本模型。
https://github.com/tensorflow/models/tree/master/research/slim
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4c4fIGlU-1648475306806)(picture/img1.png)]
该项目基于现有VGG模型
ImageDataGenerator()
train_generator = ImageDataGenerator()
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()
在运行完代码后
在根目录下有一个.keras
,其中的models
文件夹中就生成一个VGG的notop模型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z26HHEK4-1648475306807)(picture/img.png)]
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)]
在进行迁移学习中,仅需要去训练后面的全连接层,所以不需要大量的参数,使用过多参数会导致过拟合。
例如:输出大少为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
编译模型
训练模型
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