概述
目录
简介
冻结层
迁移学习工作流
微调
参考
注意事项
简介
当前目标的数据集数据太少,无法训练一个有效的模型时,可以使用迁移学习。因为一般情况下,在某种大型数据集上训练出来的模型,含有大量的特征,这些特征可用于类似的新问题。深度学习中迁移学习的一般流程:
1从某训练好的模型中获取层并冻结
2在其顶部添加一些可训练的层,这些层能够将模型适应你的新的数据集
3训练你添加的新层
4最后可选地进行微调(微调会对整体模型进一步训练)
下面以tensorflow keras为例进一步说明。
冻结层
层和模型具有布尔特性 trainable
。此特性的值可以更改。将 layer.trainable
设置为 False
会将层的所有权重从可训练移至不可训练。这一过程称为“冻结”层:已冻结层的状态在训练期间不会更新(无论是使用 fit()
进行训练,还是使用依赖于 trainable_weights
来应用梯度更新的任何自定义循环进行训练时)。一般而言,所有权重都是可训练权重。唯一具有不可训练权重的内置层是 BatchNormalization
层(参考文章末尾的链接)。另外注意,如果在模型或具有子层的任何层上设置 trainable = False
,则所有子层也将变为不可训练。
如下代码所示:
Dense 层具有 2 个可训练权重(内核与偏差)在训练期间,它使用不可训练权重跟踪其输入的平均值和方差。
BatchNormalization 层具有 2 个可训练权重和 2 个不可训练权重
import numpy as np
import tensorflow as tf
from tensorflow import keras
#Dense 层具有 2 个可训练权重(内核与偏差)在训练期间,它使用不可训练权重跟踪其输入的平均值和方差。
layer = keras.layers.Dense(3)
layer.build((None, 4)) # Create the weights
print("weights:", len(layer.weights))
print("trainable_weights:", len(layer.trainable_weights))
print("non_trainable_weights:", len(layer.non_trainable_weights))
#BatchNormalization 层具有 2 个可训练权重和 2 个不可训练权重
layer = keras.layers.BatchNormalization()
layer.build((None, 4)) # Create the weights
print("weights:", len(layer.weights))
print("trainable_weights:", len(layer.trainable_weights))
print("non_trainable_weights:", len(layer.non_trainable_weights))
#实施冻结
layer = keras.layers.Dense(3)
layer.build((None, 4)) # Create the weights
layer.trainable = False # Freeze the layer
print("weights:", len(layer.weights))
print("trainable_weights:", len(layer.trainable_weights))
print("non_trainable_weights:", len(layer.non_trainable_weights))
迁移学习
迁移学习一般有两种:
一个是将基础模型的所有层冻结后,将其中的某些层的输出嵌入到你的新模型中,最后在你的数据集中进行训练。
另外一个是将基础模型的所有层冻结后,输入你的数据,将某些层的输出(即特征)作为你的新模型的输入,最后在你的数据集中进行训练。
keras中可用模型见https://keras.io/api/applications/
例子:
#应用Xception模型
base_model = keras.applications.Xception(
weights='imagenet', # Load weights pre-trained on ImageNet.
input_shape=(150, 150, 3),
include_top=False) # Do not include the ImageNet classifier at the top.
#冻结模型
base_model.trainable = False
inputs = keras.Input(shape=(150, 150, 3))
#通过training=False确保基础模型在推断模式下运行。否则就失去了迁移学习和微调的意义
x = base_model(inputs, training=False)
#添加的你的新模型
x = keras.layers.GlobalAveragePooling2D()(x)
outputs = keras.layers.Dense(1)(x)
model = keras.Model(inputs, outputs)
#编译并拟合模型
model.compile(optimizer=keras.optimizers.Adam(),
loss=keras.losses.BinaryCrossentropy(from_logits=True),
metrics=[keras.metrics.BinaryAccuracy()])
model.fit(new_dataset, epochs=20, callbacks=..., validation_data=...)
微调
最后一个可选步骤是微调,需要解冻上面获得的整个模型(或模型的一部分),然后在新数据上以极低的学习率对该模型进行重新训练(否则容易过拟合)。
例子:
# 解冻基础模型
base_model.trainable = True
#记得重新编译你的模型,且以极低的学习率进行拟合
model.compile(optimizer=keras.optimizers.Adam(1e-5), # Very low learning rate
loss=keras.losses.BinaryCrossentropy(from_logits=True),
metrics=[keras.metrics.BinaryAccuracy()])
#端到端进行训练,及时停止训练,防止过拟合
model.fit(new_dataset, epochs=10, callbacks=..., validation_data=...)
一个完整可复现的端到端的例子:
我们加载在 ImageNet 上预训练的 Xception 模型,并将其用于 Kaggle Dogs vs. Cats 分类数据集:(我的环境为最新版tensorflow2.4)
1.首先获取数据集,划分训练集验证集测试集:
#取数据 划分数据集
import tensorflow_datasets as tfds
import tensorflow as tf
tfds.disable_progress_bar()
train_ds, validation_ds, test_ds = tfds.load(
"cats_vs_dogs",
# Reserve 10% for validation and 10% for test
split=["train[:40%]", "train[40%:50%]", "train[50%:60%]"],
as_supervised=True, # Include labels
)
print("Number of training samples: %d" % tf.data.experimental.cardinality(train_ds))
print( "Number of validation samples:
%d" % tf.data.experimental.cardinality(validation_ds))
print("Number of test samples: %d" % tf.data.experimental.cardinality(test_ds))
2.标准化数据
这一步包括图像大小的调整,数据批处理,使用缓存和预提取、数据扩充等。
#将图像的大小调整为 150x150:
size = (150, 150)
train_ds = train_ds.map(lambda x, y: (tf.image.resize(x, size), y))
validation_ds = validation_ds.map(lambda x, y: (tf.image.resize(x, size), y))
test_ds = test_ds.map(lambda x, y: (tf.image.resize(x, size), y))
#对数据进行批处理并使用缓存和预提取来优化加载速度
batch_size = 32
train_ds = train_ds.cache().batch(batch_size).prefetch(buffer_size=10)
validation_ds = validation_ds.cache().batch(batch_size).prefetch(buffer_size=10)
test_ds = test_ds.cache().batch(batch_size).prefetch(buffer_size=10)
#使用随机数据扩充,数据增强,增添数据 对称旋转等
from tensorflow import keras
from tensorflow.keras import layers
data_augmentation = keras.Sequential(
[
layers.experimental.preprocessing.RandomFlip("horizontal"),
layers.experimental.preprocessing.RandomRotation(0.1),
]
)
3.构建模型
在构建自己的新模型时,需要根据情况确定迁移学习方式(上文提到),这里为第一种。
一般情况下,需要去掉基础模型的最后一层,这一层通常是为不同任务需要而设置的;另外,在基础模型的头部,根据情况是否添加归一化层,以将输入数据符合基础模型的要求。
base_model = keras.applications.Xception(
weights="imagenet", # Load weights pre-trained on ImageNet.
input_shape=(150, 150, 3),
include_top=False,
) # 去掉基础模型的最后一层用于imagenet的分类器,并在后面添加你的新模型
#冻结基础模型
base_model.trainable = False
# 开始创建新模型
inputs = keras.Input(shape=(150, 150, 3))
x = data_augmentation(inputs) # Apply random data augmentation
#由于Xception模型要求输入不是(0,255),而是(-1,1)。
#添加 Normalization 层以将输入值(最初在 [0, 255] 范围内)缩放到 [-1, 1] 范围。
#即outputs = (inputs - mean) / sqrt(var)
norm_layer = keras.layers.experimental.preprocessing.Normalization()
mean = np.array([127.5] * 3)
var = mean ** 2
x = norm_layer(x)
norm_layer.set_weights([mean, var])
# 基础模型中包含batchnorm层,冻结它以使其在推理模式下运行(值不变)
#在微调阶段进行解冻,再对模型以极低的学习率进行训练
x = base_model(x, training=False)
x = keras.layers.GlobalAveragePooling2D()(x)
x = keras.layers.Dropout(0.2)(x) # Regularize with dropout
outputs = keras.layers.Dense(1)(x)
model = keras.Model(inputs, outputs)
model.summary()
4编译训练
model.compile(
optimizer=keras.optimizers.Adam(),
loss=keras.losses.BinaryCrossentropy(from_logits=True),
metrics=[keras.metrics.BinaryAccuracy()],
)
epochs = 20
model.fit(train_ds, epochs=epochs, validation_data=validation_ds)
5微调
注意这里的微调,官方的具体解释为:
重要的是,尽管基础模型变得可训练,但在构建模型过程中,由于我们在调用该模型时传递了 training=False
,因此它仍在推断模式下运行。这意味着内部的批次归一化层不会更新其批次统计信息。如果它们更新了这些统计信息,则会破坏该模型到目前为止所学习的表示。这里面批归一化层中Batch的均值和方差,请查看文章尾部的注意事项。
base_model.trainable = True
model.summary()
model.compile(
optimizer=keras.optimizers.Adam(1e-5), # 低学习率
loss=keras.losses.BinaryCrossentropy(from_logits=True),
metrics=[keras.metrics.BinaryAccuracy()],
)
epochs = 10
model.fit(train_ds, epochs=epochs, validation_data=validation_ds)
为节约时间。迭代了2次,准确率提升可观。
参考资料
tensorflow官方文档
kaggle
注意事项
(以下对理解BN层冻结问题十分重要,建议浏览一遍)
K.learning_phase() 和 training=False/True ,BN层和Dropout层在迁移学习中冻结(Frozen)的注意事项:
中文讲解:
https://zhuanlan.zhihu.com/p/56225304
英文原文详解(英文好的看原版)
http://blog.datumbox.com/the-batch-normalization-layer-of-keras-is-broken/#comment-22015
最后
以上就是文艺黑夜为你收集整理的迁移学习和微调( 以Xception为基础模型在Kaggle Dogs vs. Cats 猫狗分类问题上为例) 简介冻结层迁移学习微调一个完整可复现的端到端的例子:参考资料注意事项的全部内容,希望文章能够帮你解决迁移学习和微调( 以Xception为基础模型在Kaggle Dogs vs. Cats 猫狗分类问题上为例) 简介冻结层迁移学习微调一个完整可复现的端到端的例子:参考资料注意事项所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复