卷积神经网络训练准确率突然下降_Tensorflow+Keras入门练习(五):使用预训练的卷积神经网络...

d38c39c79cf6d357b0f35769307c8060.png

这个系列记录下自己的深度学习练习,本文算是对上一篇文章的补充,算是稍微进阶一些的图像处理方法。之前的模型都是自己从头开始构建,如果网络上有别人已经写好的模型,我们就可以对其微调,然后使用自己的数据集进行分析,可以节省构建模型的步骤。而且这些外部模型一般样本量很大,算是真正经过大数据考验的模型,如果和你在分析的问题类似,且你自己手头上的数据较少,用这种预处理的模型就非常有效。

使用预训练网络有两种方法:特征提取微调模型。

本文使用VGG16模型作为预训练的模型,用来预测上篇文章的猫狗问题。其实这个模型性能并不算高,选这个主要是因为这个模型结构和之前的很像,方便说明。

1.特征提取

首先,之前所有的模型其实是包含两部分的,一个是各种卷积层,最后是密集连接层(Dense)组成的分类器。前一部分叫卷积基,调用的模型其实就在调取模型的卷积基部分,最后更换自己需要的分类器来进行数据处理。

以这次使用的VGG16模型的结构为例,调用模型时只选取卷积基:

(这个模型算比较大的,如果在程序内下载不方便,可以在网上搜搜VGG16,keras关键词手动放到本地)

from keras.applications import VGG16

conv_base = VGG16(
    weights = 'imagenet', #指定模型初始化的权重检查点
    include_top = False, #指定模型最后是否包含密集连接分类器(Dense)层,VGG16是1000个类别,本次预测猫狗的话,是2个类别,所以不使用
    input_shape = (150,150,3) #输入到网络中的张量形状,这个可以不用输入,能够自动检测
)
conv_base.summary()

f9e5aad32185351164c8e913506f2e2a.png

最后的特征形状是(4,4,512),需要在这个特征基础上添加一个密集连接器。接下来又有两种方法选择

1.1不使用数据增强的快速提取

这种方法本质上就是转换自己的数据结构,来满足调用模型输出的结构(4,4,512),从而连接模型。优点是速度快,不适用GPU,节省计算量;缺点是容易过拟合。

#使用预训练的卷积基提取特征,把图片变成模型需要的numpy结构

import os
import numpy as np
from keras.preprocessing.image import ImageDataGenerator

base_small_dir = 'D:/data/cat-dog/small_samples'
train_dir = os.path.join(base_small_dir, 'train')
validation_dir = os.path.join(base_small_dir, 'validation')
test_dir = os.path.join(base_small_dir, 'test')

datagen = ImageDataGenerator(rescale=1/255)
batch_size = 20

def extract_features(directory, sample_count):
    features = np.zeros(shape=(sample_count, 4, 4, 512))
    labels = np.zeros(shape=(sample_count))
    generator = datagen.flow_from_directory(
        directory,
        target_size = (150,150),
        batch_size = batch_size,
        class_mode = 'binary'
    )
    i = 0
    for inputs_batch, labels_batch in generator:
        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)

目前提取的特征形状均为(样本数,4,4,512)。如果要输入到密集连接分类器中时,必须转换为形状为(样本数,4*4*512),可以理解成Flatten层的作用。

train_features = np.reshape(train_features, (2000, 4*4*512))
validation_features = np.reshape(validation_features, (1000, 4*4*512))
test_features = np.reshape(test_features, (1000, 4*4*512))

开始训练模型

from keras import models
from keras.layers import Dense, Dropout
from keras import optimizers

model = models.Sequential()
model.add(Dense(256, activation = 'relu', input_shape = (512*4*4,)))
model.add(Dropout(0.5))
model.add(Dense(1, activation = 'sigmoid'))

model.compile(loss='binary_crossentropy', optimizer=optimizers.RMSprop(lr=2e-5), metrics=['acc'])

history = model.fit(
    train_features, train_labels,
    epochs = 30,
    batch_size = 20,
    validation_data= (validation_features, validation_labels)
)

观察结果:

3a4d745e4085de02c4b8a0a024c76638.png

注意y轴,可以看出进度已经逼近90%左右的准确率,自己构造模型的话,在数据增强的加持下,不断微调也只能达到87%左右的准确率。但是这个模型在随机50%dropout的情况下还是过拟合很明显。

1.2使用数据增强的快速提取

这种方法原理就比较简单了,直接使用原始的预训练VGG16模型,然后接入到使用数据处理后的数据集中就好。

注意:

在图像处理的应用上,这种模型的计算量十分大,最好使用keras的GPU模式,反正我是没敢试只有CPU进行处理的情况。

在预训练模型卷积基上直接添加密集连接分类器:

from keras import models
from keras.layers import Dense, Dropout, Flatten
from keras import optimizers

model = models.Sequential()
model.add(conv_base)
model.add(Flatten())
model.add(Dense(256, activation = 'relu'))
model.add(Dense(1, activation = 'sigmoid'))

还有一点,需要在编译前冻结卷积基,防止训练时的结果污染之前的学习结果,因为Dense层是随机初始化的。

conv_base.trainable = False

数据增强和训练模型,和之前猫狗模型的数据增强设置一样,再贴一次代码:

train_datagen = ImageDataGenerator(
    rescale = 1/255, #对图片的每个像素值均乘上这个放缩因子,把像素值放缩到0和1之间有利于模型的收敛
    rotation_range = 40, #角度值,0-180.表示图像随机旋转的角度范围
    width_shift_range = 0.2, #平移比例,下同
    height_shift_range = 0.2,
    shear_range = 0.2, #随机错切变换角度
    zoom_range = 0.2, #随即缩放比例
    horizontal_flip = True, #随机将一半图像水平翻转,主要用于真实世界的图像(即没有水平不对称的假设前提下)
    fill_mode='nearest' #填充新创建像素的方法
)

test_datagen = ImageDataGenerator(rescale=1/255) #注意验证集的数据不能增强



#训练模型
train_generator = train_datagen.flow_from_directory(
    train_dir, #路径
    target_size = (150, 150), #图像大小
    batch_size = 20, #每批量样本大小
    class_mode = 'binary' #因为使用了binary_crossentropy损失函数,所以用二进制标签
)

validation_generator = test_datagen.flow_from_directory(
    validation_dir, 
    target_size = (150, 150), 
    batch_size = 20, 
    class_mode = 'binary' 
)

history = model.fit_generator(
    train_generator,
    steps_per_epoch = 100, 
    epochs = 30, 
    validation_data = validation_generator, 
    validation_steps = 50 
)

训练结果图找不到了,精度大概在90-92%左右。

2.微调模型

模型微调也是广泛使用的模型复用方法。其原理是修改(解冻)调用模型的部分层,让这些层和分类层联合训练。结合上文的知识,之前冻结整个卷积基是防止模型被破坏,现在解冻最后加的层,相当于手动“破坏”模型靠顶部的编码,这些编码是更专业化的特征。至于特征专业化和顶部底部的影响就不展开了,大家可以自己去查找资料。

先看下VGG16的模型结构,共有5个卷积块:

6bd8137f9bfe52659c03edb07712d145.png

我们将调整最后一个卷积块的最后3个卷积层,也就是说到block4_pool的所有层都该冻结,后边的层是可进行训练的。

在上面的例子中,所有的卷积层都是冻结的,从这种情况下开始编程:

conv_trainable = True

set_trainable = False
for layer in conv_base.layers:
    if layer.name == 'bloc5_conv1': #这层之后的所有层解冻,包含该层
        set_trainable = True
    if set_trainable:
        layer.trainable = True
    else:
        layer.trainable = False

之后仍然是画图观察和选择参数(抱歉图又找不到了,代码跟之前的类似)。最后在测试集上评估模型:

test_generator = test_datagen.flow_from_directory(
    test_dir,
    target_size = (150,150),
    batch_size = 20,
    class_mode = 'binary'
)

test_loss, test_acc = model.evaluate_generator(test_generator, steps = 50)
print(test_loss, test_acc)

最后使用了带数据增强的预训练+模型微调的方法,最后的精度能够达到95-97%。这个结果已经达到当时这个数据集比赛的最佳结果之一,而且这还是在我们只使用2000个样本的情况下(样本共有20000个,占总数的10%)。对我们日常的工作来说,很可能无法有较大样本量用于训练,这个方法还是比较有参考意义的。

图像处理类的深度学习应该就告一段落,也许之后有时间会补充一些可视化相关的内容,但个人电脑的显卡不是很给力,到时候视情况而定吧。如果有哪些不太明白的可以在评论或私信联系我。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值