赞
踩
训练深度学习模型需要大量的标记数据。在大多数情况下,这些数据需要可用或更易于清理。多年来,已经创建了许多处理有限数据集的方法,迁移学习是突破之一。迁移学习使我们能够在任务的大型数据集上微调预先训练的模型。
迁移学习和微调是深度学习中的关键技术。它们允许将从解决一个问题中获得的知识转移到相关问题,从而减少训练所需的数据和计算量。迁移学习的过程涉及使用预训练模型作为起点,微调涉及通过更新其权重在新任务上进一步训练预训练模型。通过利用通过迁移学习和微调获得的知识,与从头开始相比,可以改进和加快训练过程。迁移学习和微调是许多深度学习应用中必不可少的组成部分,并且已被证明可以有效地实现最先进的结果。
本文通过创建一个网络来探索迁移学习和微调的概念,该网络可以通过微调在 ImageNet 数据集上预训练的模型(1000 个类)来识别猫和狗数据集中的两个不同类。
在深度学习管道中,迁移学习通常是在可用数据太少而无法正确训练网络时完成的。迁移学习工作流的一般方法如下。
本文将探讨如何采用在 ImageNet 上训练的模型,并根据新数据对其进行微调。我们将在 Tensorflow 中创建此实现。
在微调模型之前,我们必须确定我们需要什么基础模型。我们还需要加载和预处理数据集。由于迁移学习通常用于小型数据集,因此在此示例中,我们采用猫和狗数据集的子集。
我们首先导入所需的库。我们将 Tensorflow 用于整个管道。
- import numpy as np
- import tensorflow as tf
- from tensorflow import keras
- from tensorflow.keras import layers
- import tensorflow_datasets as tfds
- import os
- import zipfile
由于 Cats and Dogs 数据集不是 Tensorflow 的一部分,因此我们从 Kaggle 下载它,然后使用 tensorflow_datasets 库将其加载到内存中。
加载后,我们将数据拆分为训练和测试,同时对其进行子集。
- train_dataset, validation_dataset, test_dataset = tfds.load(
- "cats_vs_dogs",
- split=["train[:40%]", "train[40%:50%]", "train[50%:60%]"],
- as_supervised=True,
- )
数据的示例子集如下所示。
然后,我们可以将数据转换为批处理,将它们拆分为数据加载器,并使用缓存和预取来优化数据加载。在此示例中,我们使用 32 的批处理大小。加载后,我们还可以应用一些简单的数据增强方法。例如,我们使用随机水平翻转和随机旋转。
- size = (150, 150)
- bs = 32
- aug_transforms = keras.Sequential(
- [layers.RandomFlip("horizontal"), layers.RandomRotation(0.1),]
- )
-
- train_dataset = train_dataset.map(lambda x, y: (tf.image.resize(x, size), y))
- validation_dataset = validation_dataset.map(lambda x, y: (tf.image.resize(x, size), y))
- test_dataset = test_dataset.map(lambda x, y: (tf.image.resize(x, size), y))
-
-
- train_dataset = train_dataset.cache().batch(bs).prefetch(buffer_size=10)
- validation_dataset = validation_dataset.cache().batch(bs).prefetch(buffer_size=10)
- test_dataset = test_dataset.cache().batch(bs).prefetch(buffer_size=10)
本文使用在 ImageNet 数据集上预训练并应用于图像的 Xception 模型150 x 150 x 3在大小上。重要的一点是排除预训练模型的最终分类层。最后一层仅用于分类;我们只关心它前面的层。
- model_pretrained = keras.applications.Xception(
- weights="imagenet",
- input_shape=(150, 150, 3),
- include_top=False,
- )
此处显示了 Xception 模型架构。
微调是通过在小型数据集上训练预训练模型来使预训练模型适应新任务。当我们的任务可用的数据有限时,这种适应特别有用,因为它允许我们在更大的数据集上利用模型学到的知识。微调还可以使预训练模型适应新领域或提高其在特定任务上的性能。这种技术可以显著减少从头开始训练深度学习模型所需的时间和计算资源。以下部分演示了相同的代码。
现在,我们通过将可训练参数设置为 False 来冻结刚刚加载的模型层。
之后,我们在冻结层之上创建一个模型,并应用我们定义的数据增强。
Xception 模型需要注意的是,它定义了从原始范围缩放的输入(0,255)( 0,2 5 5)到范围(−1.0,1.0)(−1.0, 1.0)。我们使用 Rescaling 图层执行此重新缩放,如下所示。
- model_pretrained.trainable = False
-
- inputs = keras.Input(shape=(150, 150, 3))
- rescale_layer = keras.layers.Rescaling(scale=1 / 127.5, offset=-1)
-
- x = aug_transforms(inputs)
- x = rescale_layer(x)
Xception 模型还包含批量归一化层,当模型解冻时,不应训练这些层。为了确保情况确实如此,我们禁用了训练模式。我们还应用了 GlobalAveragePooling,然后应用了 Dropout 层,以进一步提高性能。全局平均池化是全连接层 (FC) 的替代方法,可更好地保留空间信息。由于我们的预训练模型使用不同的数据,因此这些层在这里很有用。最后一层是用于二元分类任务的 FC 层。
- x = model_pretrained(x, training=False)
- x = keras.layers.GlobalAveragePooling2D()(x)
- x = keras.layers.Dropout(0.2)(x)
-
- outputs = keras.layers.Dense(1)(x)
- final_model = keras.Model(inputs, outputs)
现在,我们可以训练我们创建的新层。
- final_model.compile(
- optimizer=keras.optimizers.Adam(),
- loss=keras.losses.BinaryCrossentropy(from_logits=True),
- metrics=[keras.metrics.BinaryAccuracy()],
- )
-
- num_epochs = 5
- final_model.fit(train_dataset, epochs=num_epochs, validation_data=validation_dataset)
从训练进度可以看出,经过 5 个 epoch 后,我们的验证准确率为 0.97。在此之后,我们可以通过训练整个模型来提高性能。
- Epoch 1/5
- 291/291 [==============================] - 44s 105ms/step - loss: 0.1577 - binary_accuracy: 0.9338 - val_loss: 0.0790 - val_binary_accuracy: 0.9716
- Epoch 2/5
- 291/291 [==============================] - 25s 86ms/step - loss: 0.1130 - binary_accuracy: 0.9503 - val_loss: 0.0744 - val_binary_accuracy: 0.9729
- Epoch 3/5
- 291/291 [==============================] - 26s 90ms/step - loss: 0.1068 - binary_accuracy: 0.9553 - val_loss: 0.0713 - val_binary_accuracy: 0.9733
- Epoch 4/5
- 291/291 [==============================] - 26s 91ms/step - loss: 0.1072 - binary_accuracy: 0.9544 - val_loss: 0.0700 - val_binary_accuracy: 0.9733
- Epoch 5/5
- 291/291 [==============================] - 26s 91ms/step - loss: 0.1002 - binary_accuracy: 0.9584 - val_loss: 0.0725 - val_binary_accuracy: 0.9729
-
- <keras.callbacks.History at 0x7f225245fc10>
现在我们已经训练了新层,我们解冻了整个模型,然后以非常小的学习率训练它。这种循序渐进的训练会带来更好的表现。请注意,在此训练期间,批量规范化层不会更新,即使更新了,也会严重损害性能。
- model_pretrained.trainable = True
- final_model.summary()
-
- final_model.compile(
- optimizer=keras.optimizers.Adam(1e-5),
- loss=keras.losses.BinaryCrossentropy(from_logits=True),
- metrics=[keras.metrics.BinaryAccuracy()],
- )
-
- num_epochs = 5
- final_model.fit(train_dataset, epochs=num_epochs, validation_data=validation_dataset)
- Epoch 1/5
- 291/291 [==============================] - 101s 324ms/step - loss: 0.0853 - binary_accuracy: 0.9667 - val_loss: 0.0544 - val_binary_accuracy: 0.9755
- Epoch 2/5
- 291/291 [==============================] - 93s 318ms/step - loss: 0.0592 - binary_accuracy: 0.9765 - val_loss: 0.0461 - val_binary_accuracy: 0.9824
- Epoch 3/5
- 291/291 [==============================] - 94s 322ms/step - loss: 0.0463 - binary_accuracy: 0.9812 - val_loss: 0.0493 - val_binary_accuracy: 0.9794
- Epoch 4/5
- 291/291 [==============================] - 93s 320ms/step - loss: 0.0342 - binary_accuracy: 0.9870 - val_loss: 0.0575 - val_binary_accuracy: 0.9785
- Epoch 5/5
- 291/291 [==============================] - 93s 321ms/step - loss: 0.0301 - binary_accuracy: 0.9889 - val_loss: 0.0479 - val_binary_accuracy: 0.9811
-
- <keras.callbacks.History at 0x7f22c8fc5c10>
在对整个模型进行了五个周期的训练后,我们看到验证准确率增加到 0.98。这一进展表明,与仅训练某些层相比,重新训练整个模型确实提高了性能。
此示例显示了迁移学习对于快速训练小型数据集的有用性。训练模型后,我们评估测试数据集。尽管训练周期很少,数据也较少,但该模型仍然表现良好。
Layer | Output Shape | Param # |
---|---|---|
input_2 | [(None,150,150,3)] | 0 |
sequential | (None,150,150,3) | 0 |
Rescaling | (None,150,150,3) | 0 |
xception | (None,5,5,2048) | 20861480 |
global_average_pooling2D | (None,2048) | 0 |
dropout | (None,2048) | 0 |
dense | (None,1) | 2049 |
考虑一张模型从未见过的狗的图像,并且不是数据集的一部分。
数据集中的类列在此字典中。
- label_dict = {
- 0: "dog",
- 1 : "cat"
- }
从给定的字典中,我们可以看到网络正确地预测了图像。要自动获取生成的类,我们可以使用以下代码。
- Image.open('dog-583007.jpg').resize((150,150))
- label_dict[int(final_model.predict(image))]
- 当存在较少的数据时,迁移学习是一种强大的方法。
- 只要预训练模型使用类似的数据,就可以使用它对利基模型进行微调。
- 有选择地冻结预训练的层并训练其余层是实现微调效果的一种方式。
- 在第一轮选择性训练之后,解冻模型并训练整个模型可以提高性能。
- 因此,迁移学习和微调方法是深度学习的宝贵突破。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。