迁移学习和在深度学习中使用预训练模型的艺术

英文原文:https://www.analyticsvidhya.com/blog/2017/06/transfer-learning-the-art-of-fine-tuning-a-pre-trained-model/
对原文的表达有部分改动

什么是迁移学习(Transfer learning)

我们从一个简单的老师,学生的比喻来理解吧:教师在他教授的特定主题上拥有多年的经验。有了所有这些积累的信息,学生们得到的讲座是对该主题的简明扼要的概述。所以它可以被看作是从老手到新手的信息“迁移”。


神经网络是在数据上训练的。该网络从这些数据中获得知识,这些数据被映射为网络的“权重”。这些权重可以被提取,然后转移到任何其他神经网络。我们不是从头开始训练另一个神经网络,而是“迁移”学习到的特征。

现在,让我们通过与我们的进化相关联来反思迁移学习的重要性。还有什么比使用迁移学习更好的方法呢!在语言被发明之前,每一代人都必须为自己重新发明知识,这就是知识从一代传到另一代的方式:


然后,我们发明了语言!一种将学习从一代转移到另一代的方法,这就是在同一时间范围内发生的事情:


通过传递权重进行迁移学习相当于人类进化过程中用于在几代人中传播知识的语言。

什么是预训练模型(Pre-trained Model)

简单地说,预训练模型是由其他人创建的用于解决类似问题的模型。您不是从头开始构建模型来解决类似问题,而是使用在其他问题上训练的模型作为起点。

例如,如果你想建造一辆自学汽车。您可以花费数年时间从头开始构建一个像样的图像识别算法,或者您可以从 Google 获取建立在 ImageNet 数据上的初始模型(一种预先训练的模型)来识别这些图片中的图像。

预先训练的模型在您的应用程序中可能不是 100% 准确,但它可以节省重新发明轮子所需的巨大努力。让我用一个最近的例子向你展示这一点。

为什么我们要使用预训练模型?

简单地说,预训练模型是由其他人创建的用于解决类似问题的模型。您不是从头开始构建模型来解决类似问题,而是使用在其他问题上训练的模型作为起点。

例如,如果你想建造一辆自学汽车。您可以花费数年时间从头开始构建一个像样的图像识别算法,或者您可以从 Google 获取建立在 ImageNet 数据上的初始模型(预训练模型)来识别这些图片中的图像。

预先训练的模型在您的应用程序中可能不是 100% 准确,但它可以节省重新发明轮子所需的巨大努力。让我用一个最近的例子向你展示这一点。

为什么我们要使用预训练模型?
我之前研究的一个问题——从手机案例图像中识别主题。这是一个图像分类问题,我们4591 张图像的训练集,1200张图像的测试集。我们的目标是将图像分类为 16 个类别之一。 在基本的预处理步骤之后,我开始使用具有以下架构的简单 MLP 模型,在将输入图像 224 X 224 X 3 展平为 150528 后简化上述架构,我使用了三个 500 的隐藏层。输出层有 16 个神经元,对应于我们需要对输入图像进行分类的类别数。


summary

我勉强达到了 6.8% 的训练准确率,结果非常糟糕。 尝试了多种隐藏层设计、神经元数量和 Dropout 率,都无法大幅提高我的训练准确性。 增加隐藏层和神经元数量,导致在我在 12 GB VRAM 的 Titan X GPU 上运行单个 epoch 需要 20 秒(需要学习的参数过多)。

是时候更换为卷积神经网络架构了:


CNN

这里使用了 3 个卷积层,每个块都遵循以下架构:

  • 32 个 5 X 5 的卷积过滤器
  • 激活函数 ReLu
  • 4 X 4 的 MaxPooling

最终卷积块后得到的结果被展平成 256 并传递到具有 64 个神经元的单个隐藏层。隐藏层的输出以 0.5 的 Dropout 率传递到输出层。尽管与 MLP 输出相比,我的准确度有所提高(从0.068提升到0.157),但它也增加了运行单个 epoch 所需的时间到21 秒。

数据集中的多数类占比约为 17.6%。因此,即使我们将训练数据集中每个图像的类别预测为多数类别,我们的表现也优于 MLP 和 CNN。添加更多的卷积块大大地增加了我的训练时间。这让我转而使用预训练模型,在这种模型中,我不必训练整个架构,而只需训练几层。

因此,我使用了 VGG16 模型,该模型在 ImageNet 数据集上进行了预训练,并在 keras 库中提供以供使用。下面是我使用的 VGG16 模型的架构:


VGG16

我对 VGG16 现有架构所做的唯一更改是将具有 1000 个输出的 softmax 层更改为适合我们问题的 16 个类别,并重新训练 Dense 层。这种架构给了我 70% 的准确率,比 MLP 和 CNN 好得多。此外,使用 VGG16 预训练模型的最大好处是几乎以忽略不计时间获得更高的精度训练Dense 层。

因此,我继续采用这种使用预训练模型的方法。接下来需要微调 VGG16 模型以适应这个问题。

如何使用预训练模型

当我们训练神经网络时,我们的目标是什么?我们希望通过多次前向和后向迭代来确定网络的正确权重。通过使用先前在大型数据集上训练过的预训练模型,我们可以直接使用获得的权重和架构,并将学习应用于我们的问题陈述。这就是迁移学习。我们将预训练模型的学习“转移”到我们的特定问题中。

在选择应该在您的案例中使用的预训练模型时,您应该非常小心。如果我们手头的问题陈述与训练预训练模型的问题陈述非常不同——我们得到的预测将很可能不准确。例如,如果我们尝试使用它来识别物体,那么先前为语音识别训练的模型也许无法正常工作。

我们很幸运,在 Keras 库中可以直接使用许多预先训练好的架构。 Imagenet 数据集已被广泛用于构建各种架构,因为它有足够大(1.2M的数据集以创建通用模型。我们的问题是训练一个模型,该模型可以将图像正确分类为 1000 个单独的对象类别。这 1000 个图像类别代表了我们在日常生活中遇到的对象类别,例如狗、猫、各种家用物品、车辆类型等。

预训练的网络能够通过迁移学习泛化到 ImageNet 数据集之外的图像。我们通过微调模型对预先存在的模型进行修改。由于我们假设预训练的网络已经训练得很好,我们不希望过快地修改权重。在修改时,我们通常使用比初始训练模型更小的学习率

微调模型的方法

  1. 特征提取——我们可以使用预训练模型作为特征提取机制。我们可以做的是移除输出层(给出 1000 个类别中每个类别的概率的层),然后将整个网络用作新数据集的固定特征提取器。
  2. 使用预训练模型的架构——我们可以使用模型的架构,同时我们随机初始化所有权重并再次根据我们的数据集训练模型。
  3. 训练一些层,同时冻结其他层——使用预训练模型的另一种方法是局部训练。我们可以做的是保持模型初始层的权重不变,而我们只重新训练更高的层。我们可以尝试测试要冻结多少层以及要训练多少层。

下图应该可以帮助您决定如何在您的案例中继续使用预先训练的模型:


场景1:数据集规模小,数据相似度很高。在这种情况下,由于数据相似度很高,我们不需要重新训练模型。我们需要做的就是根据我们的问题陈述自定义和修改输出层。我们使用预训练模型作为特征提取器。假设我们决定使用在 Imagenet 上训练的模型来识别新图像集是否包含猫或狗。这里我们需要识别的图像类似于 Imagenet,但是我们只需要两个类别作为我的输出——猫或狗。在这种情况下,我们所做的只是修改密集层和最终的 softmax 层以输出 2 个类别而不是 1000 个类别。

场景 2:数据量很小,数据相似度非常低。在这种情况下,我们可以冻结预训练模型的初始(假设为 k)层,并再次仅训练剩余的(n-k)层。然后将根据新数据集自定义顶层。由于新数据集的相似性较低,因此根据新数据集重新训练和自定义更高层非常重要。由于初始层保持预训练(之前已在大型数据集上进行过训练)并且这些层的权重被冻结,因此数据集的小规模得到了补偿。

场景 3:数据集的大小很大但数据相似度非常低。在这种情况下,由于我们有一个大数据集,我们的神经网络训练将是有效的。然而,由于我们拥有的数据与用于训练我们的预训练模型的数据非常不同。使用预训练模型做出的预测不会有效。因此,最好根据您的数据从头开始训练神经网络。

场景 4:数据量大且数据相似度高。这是理想情况。在这种情况下,预训练模型应该是最有效的。使用模型的最佳方式是保留模型的架构和模型的初始权重。然后我们可以使用在预训练模型中初始化的权重重新训练这个模型。

使用预训练模型识别Fashion minist

现在让我们尝试使用预训练模型来解决一个简单的问题。已经在 imageNet 数据集上训练了各种架构。您可以在这里浏览各种架构。我使用 vgg16 作为预训练模型架构,并尝试使用它识别手写数字。让我们看看这个问题会属于上述哪种情况。我们有大约 60,000 张手写数字的训练图像。这个数据集肯定很小。所以情况要么属于场景1,要么是场景2。我们将尝试使用这两种场景来解决问题。数据集可以从这里下载。

  1. 导入相关库
# 使用迁移学习的思想,以VGG16作为模板搭建模型,训练识别手写字体
# 引入VGG16模块
from tensorflow.keras.applications.vgg16 import VGG16
 
# 其次加载其他模块
from tensorflow.keras.layers import Input
from tensorflow.keras.layers import Flatten
from tensorflow.keras.layers import Dense
from tensorflow.keras.layers import Dropout
from tensorflow.keras.models import Model
from tensorflow.keras.optimizers import Adam
 
from tensorflow.keras.datasets import fashion_mnist

import cv2
import numpy as np

from tensorflow.python.framework.ops import disable_eager_execution
disable_eager_execution()

from tensorflow.python.compiler.mlcompute import mlcompute
mlcompute.set_mlc_device(device_name='gpu')
  1. 仅重新训练输出密集层。这里我们将使用 Vgg16 作为特征提取器,然后我们使用这些特征并将它们发送到根据我们的数据集训练的密集层。输出层也被替换为与我们的问题相关的新 softmax 层。 Vgg16 中的输出层是具有 1000 个类别的 softmax 激活。我们移除这一层并用一个包含 10 个类别的 softmax 层替换它。我们只是训练这些层的权重并尝试识别数字。
# 建立一个模型,其类型是Keras的Model类对象,我们构建的模型会将VGG16顶层(全连接层)去掉,只保留其余的网络
# 结构。这里用include_top = False表明我们迁移除顶层以外的其余网络结构到自己的模型中
# VGG模型对于输入图像数据要求高宽至少为32个像素点,由于硬件配置限制,我们选用32个像素点而不是原来
# VGG16所采用的224个像素点。即使这样仍然需要24GB以上的内存,或者使用数据生成器
model_vgg = VGG16(include_top=False, weights='imagenet', input_shape=(32, 32, 3))#输入进来的数据是48*48 3通道
#选择imagnet,会选择当年大赛的初始参数
#include_top=False 去掉最后3层的全连接层看源码可知
for layer in model_vgg.layers:
    #别去调整之前的卷积层的参数
    layer.trainable = False
model = Flatten(name='flatten')(model_vgg.output)#去掉全连接层,前面都是卷积层
model = Dense(1024, activation='relu', name='fc1')(model)
model = Dropout(0.5)(model)
model = Dense(1024, activation='relu', name='fc2')(model)
model = Dropout(0.5)(model)
model = Dense(10, activation='softmax')(model)#model就是最后的y
model_vgg_mnist = Model(inputs=model_vgg.input, outputs=model, name='vgg16')
#把model_vgg.input  X传进来
#把model Y传进来 就可以训练模型了
 
# 打印模型结构,包括所需要的参数
model_vgg_mnist.summary()
  1. 定义优化函数以及损失函数
# 新的模型不需要训练原有卷积结构里面的1471万个参数,但是注意参数还是来自于最后输出层前的两个
# 全连接层,一共有1.2亿个参数需要训练
sgd = SGD(lr=0.05, decay=1e-5)#lr 学习率 decay 梯度的逐渐减小 每迭代一次梯度就下降 0.05*(1-(10的-5))这样来变
#随着越来越下降 学习率越来越小 步子越小
model_vgg_mnist.compile(loss='categorical_crossentropy',
                                 optimizer=sgd, metrics=['accuracy'])
  1. 数据预处理
# 因为VGG16对网络输入层需要接受3通道的数据的要求,我们用OpenCV把图像从32*32变成224*224,把黑白图像转成RGB图像
# 并把训练数据转化成张量形式,供keras输入
(X_train, y_train), (X_test, y_test) = fashion_mnist.load_data()
# X_train, y_train, X_test, y_test = X_train[:10000], y_train[:10000], X_test[:1500], y_test[:1500]

X_train = [cv2.cvtColor(cv2.resize(i, (32, 32)), cv2.COLOR_GRAY2RGB)
           for i in X_train]#变成彩色的
#np.concatenate拼接到一起把
X_train = np.concatenate([arr[np.newaxis] for arr in X_train]).astype('float32')
X_test = [cv2.cvtColor(cv2.resize(i, (32, 32)), cv2.COLOR_GRAY2RGB)
          for i in X_test]
X_test = np.concatenate([arr[np.newaxis] for arr in X_test]).astype('float32')
 
print(X_train.shape)
print(X_test.shape)
 
X_train = X_train / 255
X_test = X_test / 255
  1. 定义转 one-hot 函数
def tran_y(y):
    y_ohe = np.zeros(10)
    y_ohe[y] = 1
    return y_ohe
  1. 拟合训练
y_train_ohe = np.array([tran_y(y_train[i]) for i in range(len(y_train))])
y_test_ohe = np.array([tran_y(y_test[i]) for i in range(len(y_test))])
 
model_vgg_mnist.fit(X_train, y_train_ohe, validation_data=(X_test, y_test_ohe),
                             epochs=100, batch_size=256, workers=4,use_multiprocessing=True)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335

推荐阅读更多精彩内容