再谈迁移学习:微调网络

在《站在巨人的肩膀上:迁移学习》一文中,咱们谈到了一种迁移学习方法:将预训练的卷积神经网络做为特征提取器,而后使用一个标准的机器学习分类模型(好比Logistic回归),以所提取的特征进行训练,获得分类器,这个过程至关于用预训练的网络取代上一代的手工特征提取方法。这种迁移学习方法,在较小的数据集(好比17flowers)上也能取得不错的准确率。git

在那篇文章中,我还提到了另一种迁移学习:微调网络,这篇文章就来谈谈微调网络。github

所谓微调网络,至关于给预训练的模型作一个“换头术”,即“切掉”最后的全链接层(能够想象为卷积神经网络的“头部”),而后接上一个新的参数随机初始化的全链接层,接下来咱们在这个动过“手术”的卷积神经网络上用咱们比较小的数据集进行训练。算法

在新模型上进行训练,有几点须要注意:bash

  1. 开始训练时,“头部”如下的层(也就是没有被替换的网络层)的参数须要固定(frozen),也就是进行前向计算,但反向传递时不更新参数,训练过程只更新新替换上的全链接层的参数。
  2. 使用一个很是小的学习率进行训练,好比0.001
  3. 最后,做为可选,在全链接层的参数学习得差很少的时候,咱们能够将“头部”如下的层解冻(unfrozen),再总体训练整个网络。

特征提取和微调网络

对照一下上一篇文章中的特征提取,咱们以直观的图形来展示它们之间的不一样:微信

若是咱们在VGG16预训练模型上进行特征提取,其结构以下图所示:网络

对比原模型结构,从最后一个卷积池化层直接输出,即特征提取。而微调网络则以下图所示:架构

一般状况下,新替换的全链接层参数要比原来的全链接层参数要少,由于咱们是在比较小的数据集上进行训练。训练过程一般分两个阶段,第一阶段固定卷积层的参数,第二阶段则全放开:机器学习

相比特征提取这种迁移学习方法,网络微调一般能获得更高的准确度。但记住,天下没有免费的午饭这个原则,微调网络须要作更多的工做:函数

首先训练时间很长,相比特征提取只作前向运算,而后训练一个简单的Logisitic回归算法,速度很快,微调网络由于是在很深的网络模型上训练,特别是第二阶段要进行全面的反向传递,耗时更长。在个人GTX 960显卡上用17flowers数据集,训练了几个小时,尚未训练完,结果我睡着了:(,也不知道最终花了多长时间。post

其次须要调整的超参数比较多,好比选择多大的学习率,全链接网络设计多少个节点比较合适,这个依赖经验,但在某些特别的数据集上,可能须要多尝试几回才能获得比较好的结果。

网络层及索引

在“动手术”以前,咱们须要了解模型的结构,最起码咱们须要知道层的名称及索引,没有这些信息,就如同盲人拿起手术刀。在keras中,要了解层的信息很是简单:

print("[INFO] loading network ...")
model = VGG16(weights="imagenet", include_top=args["include_top"] > 0)

print("[INFO] showing layers ...")
for (i, layer) in enumerate(model.layers):
  print("[INFO] {}\t{}".format(i, layer.__class__.__name__))
复制代码

VGG16的模型结构以下:

能够看到第20 ~ 22层为全链接层,这也是微调网络要替换的层。

网络“换头术”

首先,咱们定义一组全链接层:INPUT => FC => RELU => DO => FC => SOFTMAX。相比VGG16中的全链接层,这个更加简单,参数更少。而后将基本模型的输出做为模型的输入,完成拼接。这个拼接在keras中也至关简单。

class FCHeadNet:
  @staticmethod
  def build(base_model, classes, D):
    # initialize the head model that will be placed on top of
    # the base, then add a FC layer
    head_model = base_model.output
    head_model = Flatten(name="flatten")(head_model)
    head_model = Dense(D, activation="relu")(head_model)
    head_model = Dropout(0.5)(head_model)

    # add a softmax layer
    head_model = Dense(classes, activation="softmax")(head_model)

    return head_model
复制代码

由于在VGG16的构造函数中有一个include_top参数,能够决定是否包含头部的全链接层,因此这个“换头”步骤至关简单:

base_model = VGG16(weights="imagenet", include_top=False, input_tensor=Input(shape=(224, 224, 3)))

# initialize the new head of the network, a set of FC layers followed by a softmax classifier
head_model = FCHeadNet.build(base_model, len(class_names), 256)

# place the head FC model on top of the base model -- this will become the actual model we will train
model = Model(inputs=base_model.input, outputs=head_model)
复制代码

这时获得的model就是通过“换头术”的网络模型。

训练

微调网络的训练和以前谈到的模型训练过程差很少,只是多了一个freeze层的动做,其实是进行两个训练过程。如何固定层的参数呢?一句话就能够搞定:

for layer in base_model.layers:
  layer.trainable = False
复制代码

“解冻”相似,只是layer.trainable值设为True。

为了更快的收敛,尽快的学习到全链接层的参数,在第一阶段建议采用RMSprop优化器。但学习率须要选择一个比较小的值,例如0.001。

在通过一个至关长时间的训练以后,新模型在17flowers数据集上的结果以下:

[INFO] evaluating after fine-tuning ...
              precision    recall  f1-score   support

    bluebell       0.95      0.95      0.95        20
   buttercup       1.00      0.90      0.95        20
   coltsfoot       0.95      0.91      0.93        22
     cowslip       0.93      0.87      0.90        15
      crocus       1.00      1.00      1.00        23
    daffodil       0.92      1.00      0.96        23
       daisy       1.00      0.94      0.97        16
   dandelion       0.94      0.94      0.94        16
  fritillary       1.00      0.95      0.98        21
        iris       0.96      0.96      0.96        27
  lilyvalley       0.94      0.89      0.91        18
       pansy       0.90      0.95      0.92        19
    snowdrop       0.86      0.95      0.90        20
   sunflower       0.95      1.00      0.98        20
   tigerlily       0.96      0.96      0.96        23
       tulip       0.70      0.78      0.74        18
  windflower       1.00      0.95      0.97        19

   micro avg       0.94      0.94      0.94       340
   macro avg       0.94      0.93      0.94       340
weighted avg       0.94      0.94      0.94       340
复制代码

相比特征提取这种迁移学习,准确率有了至关可观的提高。

小结

网络微调是一项很是强大的技术,咱们无需从头开始训练整个网络。相反,咱们能够利用预先存在的网络架构,例如在ImageNet数据集上训练的最早进模型,该模型由丰富的过滤器组成。使用这些过滤器,咱们能够“快速启动”咱们的学习,使咱们可以进行网络手术,最终获得更高精度的迁移学习模型,而不是从头开始训练,并且工做量少。

以上实例均有完整的代码,点击阅读原文,跳转到我在github上建的示例代码。

另外,我在阅读《Deep Learning for Computer Vision with Python》这本书,在微信公众号后台回复“计算机视觉”关键字,能够免费下载这本书的电子版。

往期回顾

  1. 站在巨人的肩膀上:迁移学习
  2. 聊一聊rank-1和rank-5准确度
  3. 使用数据加强技术提高模型泛化能力

image
相关文章
相关标签/搜索