【1】做不做迁移式学习(Transfer Learning)的差异

关於:

本系列文将借用 Google 提供的 Colab 平台,在上面执行 30 个影像分类训练任务,每个主题都会探讨在不同的状况或不同的超参数对於同一个任务会有什麽影响,在面对不同的领域时,机器学习运作起来就像个黑箱子,很难第一次训练就得到最佳解,只能透过不断的调教来慢慢修正,本篇系列文会拿我在机器学习工作中,有时想到但没有时间细察的假设问题来当主题,并且在 Colab 上实际执行看结果如何,会有满满的假设与实作!

问题:做不做迁移式学习(Transfer Learning)的差异?

Colab连结

每当我们遇到一个新的训练任务时,普遍的做法我们会拿预训练模型(pre-train model)的权重值为基底再加上一层 Dense Layer 後开始训练任务,这麽一来可以比从头训练(train from scratch)来得有效率多,但这时我不禁在想,这种做法是否也会因为前段缺少训练而限制了模型能够学到的天花板呢?

基於这个问题,我们用 tfds 的 oxford_flowers102 作为训练任务,该资料集拥有102个花的分类,其中 train 和 validation 的部分都是每个分类 10 张图片,共1020张。

https://ithelp.ithome.com.tw/upload/images/20210915/201072996qOc6SVL0Z.png

我们将 Batch size 固定为32,学习率固定为0.1来实验,训练50个 epochs。第一个实验我们直接拿 tensorflow 提供的 mobilenetV2 作为预训练模型,但是将权重锁住(freeze)并再最後加上 102个节点的 Dense Layer 来做输出,也就是整个模型只有最後一层会用来学习。

LR = 0.1
EPOCHS = 50

base = tf.keras.applications.MobileNetV2(input_shape=(224, 224, 3), include_top=False, weights='imagenet')
base.trainable = False
net = tf.keras.layers.GlobalAveragePooling2D()(base.output)
net = tf.keras.layers.Dense(NUM_OF_CLASS)(net)

model = tf.keras.Model(inputs=[base.input], outputs=[net])

model.compile(
    optimizer=tf.keras.optimizers.SGD(LR),
    loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
    metrics=[tf.keras.metrics.SparseCategoricalAccuracy()],
)

history = model.fit(
    ds_train,
    epochs=EPOCHS,
    validation_data=ds_test,
    verbose=True)

val_loss = history.history['val_loss'][-1]
val_acc = history.history['val_sparse_categorical_accuracy'][-1]
print(f'val loss: {val_loss}')
print(f'val acc: {val_acc}')
loss: 0.0237 - sparse_categorical_accuracy: 1.0000 - val_loss: 0.7445 - val_sparse_categorical_accuracy: 0.8088

实验结果得出 loss 值约为0.74,准确度80.9%

实验二,我们同样使用 mobilenetV2,但是权重是初始化的状态来训练。

LR = 0.1
EPOCHS = 50

base = tf.keras.applications.MobileNetV2(input_shape=(224, 224, 3), include_top=False, weights=None)
net = tf.keras.layers.GlobalAveragePooling2D()(base.output)
net = tf.keras.layers.Dense(NUM_OF_CLASS)(net)

model = tf.keras.Model(inputs=[base.input], outputs=[net])

model.compile(
    optimizer=tf.keras.optimizers.SGD(LR),
    loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
    metrics=[tf.keras.metrics.SparseCategoricalAccuracy()],
)

history = model.fit(
    ds_train,
    epochs=EPOCHS,
    validation_data=ds_test,
    verbose=True)

val_loss = history.history['val_loss'][-1]
val_acc = history.history['val_sparse_categorical_accuracy'][-1]
print(f'val loss: {val_loss}')
print(f'val acc: {val_acc}')
loss: 0.0118 - sparse_categorical_accuracy: 1.0000 - val_loss: 6.2431 - val_sparse_categorical_accuracy: 0.0098

再经过 50 个 epochs 後,我们发现学习效率非常之差,准确度仅有0.9%,训练集却已经100%,看来模型已经过拟和,无法真正有效学到特徵...

第三个实验和第一个类似,我们使用 mobilenetV2 的预训练权重,但是不把预训练模型的权重锁住,而是开放整个模型的权重都可以更新学习。

LR = 0.1
EPOCHS = 50

base = tf.keras.applications.MobileNetV2(input_shape=(224, 224, 3), include_top=False, weights='imagenet')
net = tf.keras.layers.GlobalAveragePooling2D()(base.output)
net = tf.keras.layers.Dense(NUM_OF_CLASS)(net)

model = tf.keras.Model(inputs=[base.input], outputs=[net])

model.compile(
    optimizer=tf.keras.optimizers.SGD(LR),
    loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
    metrics=[tf.keras.metrics.SparseCategoricalAccuracy()],
)

history = model.fit(
    ds_train,
    epochs=EPOCHS,
    validation_data=ds_test,
    verbose=True)

val_loss = history.history['val_loss'][-1]
val_acc = history.history['val_sparse_categorical_accuracy'][-1]
print(f'val loss: {val_loss}')
print(f'val acc: {val_acc}')
loss: 8.5328e-04 - sparse_categorical_accuracy: 1.0000 - val_loss: 0.5290 - val_sparse_categorical_accuracy: 0.8539

得到 loss 值为0.53,准确度85.4%,这样的结论代表实验一将预训练模型锁住虽然可以训练得非常快(因为只有最後一层要学),但他的天花板却被限制在80%。

那有没有一个比较折衷的办法?实验四我尝试 Unfreeze mobilenetV2 的倒数最後一个 bottleneck block,看看模型有无可能突破80%。

在 Unfreeze 之前,我们可以先用 for-loop 找到最後一个 block 是在第几层,像这边得出第142层的 block_15_add 是最後一个该被 Freeze 的 Layer 後,就可以锁住该层并训练。

for idx, layer in enumerate(model.layers):
  print(f'{idx}, {layer.name}')
...
140, block_15_project
141, block_15_project_BN
142, block_15_add
143, block_16_expand
144, block_16_expand_BN
...
LR = 0.1
EPOCHS = 50
FREEZE_INDEX = 142 # include

base = tf.keras.applications.MobileNetV2(input_shape=(224, 224, 3), include_top=False, weights='imagenet')
net = tf.keras.layers.GlobalAveragePooling2D()(base.output)
net = tf.keras.layers.Dense(NUM_OF_CLASS)(net)

model = tf.keras.Model(inputs=[base.input], outputs=[net])

# Unfreeze weights
for idx, layer in enumerate(model.layers):
  layer.trainable = FREEZE_INDEX < idx

model.compile(
    optimizer=tf.keras.optimizers.SGD(LR),
    loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
    metrics=[tf.keras.metrics.SparseCategoricalAccuracy()],
)

history = model.fit(
    ds_train,
    epochs=EPOCHS,
    validation_data=ds_test,
    verbose=True)

val_loss = history.history['val_loss'][-1]
val_acc = history.history['val_sparse_categorical_accuracy'][-1]
print(f'val loss: {val_loss}')
print(f'val acc: {val_acc}')
loss: 0.0022 - sparse_categorical_accuracy: 1.0000 - val_loss: 0.5326 - val_sparse_categorical_accuracy: 0.8510

训练结果得到 loss 值为0.53准确度为85.1%,拿到了和实验三差不多的准确度,但是训练所需时间却减少很多!


<<:  30天打造品牌特色电商网站 Day.1 网站介面基础知识

>>:  Day 2 | Dart 开发环境设定

来说说有哪些逻辑结构吧 - DAY 2

资料结构的逻辑结构 集合 逻辑:资料元素(紫色球)除了属於相同集合之外没有其他关系 类似结构 书:封...

如何让 IIS 底下的 PHP 显示错误内容 (500 Error)

今天在IIS上安装的PHP发生了错误,可是一直出现 500-内部服务器错误,这样会看不到正确的错误内...

Day4. 如何寻找设计切入点

在做新产品开发时,对於用户需求收集,寻找产品切入点,我们常有一个典型的错误假设,那就是认为用户最知...

Amazon SageMaker 机器学习线上研讨会

研讨会报名网址:https://supr.link/lmFHX 在这场1小时的线上研讨会中,您可以学...

初探 Vaadin on Kotlin - day03

什麽是 Vaadin-on-Kotlin? Vaadin-on-Kotlin (VoK) 是基於 V...