【Deep Learning with Python】C5 迁移学习两种方法

迁移学习

这本书写得很好,我学到了很多keras迁移学习的技巧。

  • 特征提取的迁移学习
  • 迁移学习方法二
  • 微调

其中有很多非常值得参考的东西,为了表达方便,我直接用书中的例子,一步步对模型进行改进。

前置背景

前面已经建立了一个识别猫和狗的模型,只使用了2000个样本!

前面一步中,原始卷积神经网络识别结果是,val 0.7,超级过拟合,原因是数据实在太少。

引入数据增广,val 0.8+,已经改善了很多。

下面使用迁移学习的方式,对上述进行改善。下面是keras中获取训练好的模型的方法。

 

from keras.applications import VGG16

使用VGG16进行特征抽取,这里获得部分模型

conv_base = VGG16(weights='imagenet',

    include_top=False,

    input_shape=(150,150,3) ) 注意声明输入大小,很好奇这个是怎么指定输入大小的,通过加一层卷积?不指定的话,网络允许任何大小输入

 

notes

尽量避免使用全连接层,简单来说,卷积层特征更有泛化性。

  1. 全连接层的特征更接近输出结果
  2. 全连接层并没有新的信息
  3. 全连接层会失去特征在卷积层的位置信息

特征提取

说起来思路很简单,用部分已经训练好的模型将数据转为特征(特征提取),特征提取之后,再放到dnn中训练。

  1. 获取已经训练的模型的一部分
  2. 做特征提取——用部分模型predit
  3. 将predit结果输入新网络

下面实现之所以显得复杂,是因为其中很大部分是结合了数据生成的代码。

 

特征提取,其实就是运行部分模型得到权重

import os

import numpy as np

from keras.preprocessing.image import ImageDataGenerator

base_dir = '/content/data/cats_and_dogs_small'

train_dir = os.path.join(base_dir, 'train')

validation_dir = os.path.join(base_dir, 'validation')

test_dir = os.path.join(base_dir, 'test')

datagen = ImageDataGenerator(rescale=1./255)

batch_size = 20

def extract_features(directorysample_count): 输入文件夹地址,还有采样数量

    features = np.zeros(shape=(sample_count, 44512))这个大小注意,需要查看模型输出的特征大小

    labels = np.zeros(shape=(sample_count))

    generator = datagen.flow_from_directory( 从目标文件夹中进行采样

        directory,

        target_size=(150150),

        batch_size=batch_size,

        class_mode='binary')

    i = 0

    for inputs_batch, labels_batch in generator: 使用conv_base对采样的数据集进行预测,得到所谓的特征

        features_batch = conv_base.predict(inputs_batch)

        features[i * batch_size : (i + 1) * batch_size] = features_batch

        labels[i * batch_size : (i + 1) * batch_size] = labels_batch

        i += 1

        if i * batch_size >= sample_count:

            break

    return features, labels

train_features, train_labels = extract_features(train_dir, 2000)

validation_features, validation_labels = extract_features(validation_dir, 1000)

test_features, test_labels = extract_features(test_dir, 1000)

模拟flatten,输入全连接层之前,需要对其进行维度调整

train_features = np.reshape(train_features, (20004 * 4 * 512))

validation_features = np.reshape(validation_features, (10004 * 4 * 512))

test_features = np.reshape(test_features, (10004 * 4 * 512))

跑模型不需要再模型中体现只需要在fit中作为输入这是啥这的意思是其实我们用已经有的网络把数据集变成了特征.

#  用特征向量再进行deep learning

from keras import models

from keras import layers

from keras import optimizers

model = models.Sequential()

model.add(layers.Dense(256, activation='relu', input_dim = 4*4*512))

model.add(layers.Dropout(0.5))

model.add(layers.Dense(1, activation='sigmoid'))

model.compile(optimizer=optimizers.RMSprop(lr=2e-5),学习率???

              loss = 'binary_crossentropy',

              metrics=['acc'])

history = model.fit(train_features, train_labels,

                    epochs=30,

                    batch_size=20,

                    validation_data = (validation_features, validation_labels))

 

结果如下:

对以上结果分析:

  1. 特征提取的结果非常好。准确率直接从0.7飙升到0.9,注意,这里并没有使用数据增广。
    • 其实按道理说,使用数据增广还是可能的,只是得准备两个生成器。
  2. 从图像来看,从一开始就过拟合,尽管dropout已经很大了。
  3. 这是因为,数据太少了!小图像数据集中的数据增广非常重要。

迁移学习二

就是比较常见的方式,取出部分模型,在后面添加上自己设计的输出层。

 

冻结

from keras import models

from keras import layers

model = models.Sequential()

conv_base.trainable = False

model.add(conv_base)

model.add(layers.Flatten())

model.add(layers.Dense(256, activation='relu'))

model.add(layers.Dense(1, activation='sigmoid'))

 

 

这是迁移学习+数据增广,结果相当好,val 从0.9直接到0.96+。

微调

微调的方式之一,就是解冻已有模型一部分层,和输出一起训练。相当于对后面相对抽象的特征进行进一步的训练。

 

微调微调后三层

conv_base.trainable = True

set_trainable = False

for layer in conv_base.layers:

    if layer.name == 'block5_conv1':

        set_trainable = True

    if set_trainable:

        layer.trainable = True

    else:

        layer.trainable = False

 

微调注意,

  • 加入新的输出层后先进行训练,训练完再解冻后n层,再进行整体微调。

原因很简单,如果你加入初试输出层进行训练,那么输出层必定距离”正确“相差甚远,前面的层级会根据后面的错误进行调整(反向传播),那么前面的层级已经学到的特征就可能丢失。

为什么微调只对后几层进行比较好?作者给出了两个理由。

  1. 前面的层次的特征比较通用,后面的特征相对特殊,所以训练前面的层次回报不高。
  2.  训练越多参数,越容易过拟合。这个本身就是一个小数据集,训练太多参数,有非常严重的过拟合危险。

以下是训练结果。

 

可以看到,微调之后,区间基本集中在0.97+,也就是说,比起上个状态已经提升了1%。

看到这里有一个比较奇怪的地方,损失并没有减少,甚至明显升高,而准确率却比较平稳, 有时甚至还增加了。

作者的解释:

我们可视化的损失是每个epoch的平均损失,但是模型关心的实际上是损失的分布。

另外一个原因是,我们分类问题其实有一个阈值,比如二分类,大于阈值直接设定为1. 所以意味着,损失并不一定反映在准确率上。比如0.6升到0.8,对于准确率毫无影响,0.4到0.6却有影响。

总结

keras利用已训练模型进行特征提取,迁移和微调。

  • 小数据集,基础卷积网络,val 0.7
  • 数据增广,val 0.8
  • 迁移学习方法一,特征提取,val 0.9
  • 迁移学习方法二,迁移+数据增广,val 0.96
  • 微调,训练,解冻,训练,val 0.97

粗调和微调,我感觉这个原理相当重要。

Reference

Main2

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值