《深度学习 第五章 深度学习用于计算机视觉2》

5.使用预训练的卷积训练神经网络
想要将深度学习应用于小型数据集上,一种非常有效的方法是使用预训练的网络
预训练网络是指之前在大型数据集上训练好的网络,比如在ImageNet上训练了一个网络(其类别主要是动物和日用品),然后将这个网络应用于其他识别的图片(比如家具上)。这种学到的特征在不同问题之间具有可移植性,是深度学习的重要优势,它是的深度学习对小数据问题也很有效

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

(1)特征提取
下面使用ImageNet数据集上预训练得到VGG16网络的卷积基用于猫狗图像中提取有趣的特征,然后在这些特征上训练一个猫狗分类器(只有卷积基是可以重复使用的,密集连接分类器是避免多次复用的,原因在于卷积基学到的表示可能更加通用,因此更适合重复使用)

VGG16等模块内置于keras.applications模块中

from keras.applications import VGG16

conv_base = VGG16(weights = 'imagenet',#制定模式初始化的权重点
                  include_top = False,#指定模型最后是否包含密集连接分类器
                  input_shape = (150,150,3))#指定输入网络中的图形张量的形状

conv_base.summary()

程序会自动下载VGG16模型
在这里插入图片描述

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_3 (InputLayer)         (None, 150, 150, 3)       0         
_________________________________________________________________
block1_conv1 (Conv2D)        (None, 150, 150, 64)      1792      
_________________________________________________________________
block1_conv2 (Conv2D)        (None, 150, 150, 64)      36928     
_________________________________________________________________
block1_pool (MaxPooling2D)   (None, 75, 75, 64)        0         
_________________________________________________________________
block2_conv1 (Conv2D)        (None, 75, 75, 128)       73856     
_________________________________________________________________
block2_conv2 (Conv2D)        (None, 75, 75, 128)       147584    
_________________________________________________________________
block2_pool (MaxPooling2D)   (None, 37, 37, 128)       0         
_________________________________________________________________
block3_conv1 (Conv2D)        (None, 37, 37, 256)       295168    
_________________________________________________________________
block3_conv2 (Conv2D)        (None, 37, 37, 256)       590080    
_________________________________________________________________
block3_conv3 (Conv2D)        (None, 37, 37, 256)       590080    
_________________________________________________________________
block3_pool (MaxPooling2D)   (None, 18, 18, 256)       0         
_________________________________________________________________
block4_conv1 (Conv2D)        (None, 18, 18, 512)       1180160   
_________________________________________________________________
block4_conv2 (Conv2D)        (None, 18, 18, 512)       2359808   
_________________________________________________________________
block4_conv3 (Conv2D)        (None, 18, 18, 512)       2359808   
_________________________________________________________________
block4_pool (MaxPooling2D)   (None, 9, 9, 512)         0         
_________________________________________________________________
block5_conv1 (Conv2D)        (None, 9, 9, 512)         2359808   
_________________________________________________________________
block5_conv2 (Conv2D)        (None, 9, 9, 512)         2359808   
_________________________________________________________________
block5_conv3 (Conv2D)        (None, 9, 9, 512)         2359808   
_________________________________________________________________
block5_pool (MaxPooling2D)   (None, 4, 4, 512)         0         
=================================================================
Total params: 14,714,688
Trainable params: 14,714,688
Non-trainable params: 0
_________________________________________________________________

文件自动下载到C盘下.keras/models文件夹下
在这里插入图片描述
可以看到最后提取的特征是(4,4,512)的形状,我们在这个特征上添加一个密集分类器

在数据集上运行卷积基,将输出保存成硬盘中的Numpy数组,然后用这个数据作为输入,输入到独立的密集连接分类器中,这种方法速度快,计算代价低,因为这样每次图像只用运行一次卷积基,卷积基是计算代价最高的,但是这种方法不能使用数据增强

先卷积运算,得到的特征保存下来,再输入到Dense层中分类:

from keras.applications import VGG16

conv_base = VGG16(weights = 'imagenet',#制定模式初始化的权重点
                  include_top = False,#指定模型最后是否包含密集连接分类器
                  input_shape = (150,150,3))#指定输入网络中的图形张量的形状

#conv_base.summary()

###不使用数据增强的快速特征提取
#使用预训练的卷积基提取特征
import os
import numpy as np
from keras.preprocessing.image import ImageDataGenerator


#在cats_and_dogs_small文件夹分别选中三个文件夹的路径,变量名可以直接表示路径了
base_dir = 'E:\\dataset\\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

#这是一个将图片,变成labels和features的函数,sample_count是图片数量,directory是文件夹名称
def extract_feature(directory,sample_count):
    features = np.zeros(shape = (sample_count,4,4,512))
    labels = np.zeros(shape = (sample_count))
    #构造一个生成器,不断生成20个一批的大小为(150,150,3)的数据
    generator = datagen.flow_from_directory(directory,
                                            target_size = (150,150),
                                            batch_size = batch_size,
                                            class_mode = 'binary')
    i = 0
    #生成器不断生成inputs_batch,labels_batch就是20个一批的(150,150)大小的输入和labels
    for inputs_batch,labels_batch in generator:
        #将这些inputs_batch和labels_batch变成features和labels
        features_batch = conv_base.predict(inputs_batch)#利用predict方法从这些图像中提取特征
        features[i*batch_size:(i+1)*batch_size] = features_batch
        labels[i*batch_size:(i+1)*batch_size] = labels_batch
        i = i+1
        if i*batch_size >= sample_count:
            break #生成器会不断循环,必须在读取完所有图片之后终止循环
    return features,labels


train_features,train_labels = extract_feature(train_dir,2000)#将train文件夹里面的2000张图片提取特征,还有labels
validation_features,validation_labels = extract_feature(validation_dir,1000)#将validation文件夹里1000张图片提取特征,还有labels
test_features,test_labels = extract_feature(test_dir,1000)#将test文件夹里面1000张图片提取特征,还有labels

思路就是:从生成器中得到inputs_batch,labels_batch
inputs_batch得到features_batch得到features
labels_batch得到labels

到目前为止,提取的特征为(图片数量,4,4,512)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
我们要把它输入到密集分类器中,首先将其形状展平为(图片数量,8192)

#将特征展平为(samples,8192)的形状
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))

定义密集连接分类器,训练

#定义密集连接分类器(用dropout正则化),并训练
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))

绘制图形

#绘制结果
import matplotlib.pyplot as plt

acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']

epochs = range(1,len(acc) + 1)

plt.plot(epochs,acc,'bo',label = 'Training acc')
plt.plot(epochs,val_acc,'r',label = 'Validation acc')
plt.title('Training and Validation accuracy')
plt.legend()

plt.figure()

plt.plot(epochs,loss,'bo',label = 'Training loss')
plt.plot(epochs,val_loss,'r',label = 'Validation loss')
plt.title('Training and Validation loss')
plt.legend()

plt.show()

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
训练精度进一步提高,可以达到90%了

特征提取还有另一种方法,就是先用Dense层来扩展已有的模型,就是直接端到端的运行整个模型,不用分两步,先提取特征,再分类,直接把分类器加到卷积层下面,构成一个大的模型,这种方法计算代价大,但是可以使用数据增强
(这个计算量太大,没有做图像数据的增强):

###1.在已经训练好的基网络上添加自定义网络
from keras.applications import VGG16

conv_base = VGG16(weights = 'imagenet',#制定模式初始化的权重点
                  include_top = False,#指定模型最后是否包含密集连接分类器
                  input_shape = (150,150,3))#指定输入网络中的图形张量的形状

from keras import models
from keras import layers

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

model.summary()

在这里插入图片描述
两层密集连接层前面有一个VGG16的model

然后需要冻结网络,VGG16的卷积基有14714688各参数,非常多,冻结过程中,冻结多个层,使训练过程权重不变,不然Dense层的权重将会传到前面的网络中,对之前学到的模型造成破坏

###2.冻结基网络
print('number of trainable weights:',len(model.trainable_weights))
conv_base.trainable = False
print('number of trainable weights:',len(model.trainable_weights))
#这样设置之后,只有Dense层的权重才会被训练

number of trainable weights: 30
number of trainable weights: 4
可以看到冻结之前有30个权重张量要训练,之后只有四个权重张量,每个Dense层两个(主权重矩阵,偏置向量)

###3.训练所添加的部分
from keras.preprocessing.image import ImageDataGenerator
from keras import optimizers
import os

base_dir = 'E:\\dataset\\cats_and_dogs_small'
train_dir = os.path.join(base_dir,'train')
validation_dir = os.path.join(base_dir,'validation')
train_datagen = ImageDataGenerator(rescale = 1./255)
validation_datagen = ImageDataGenerator(rescale = 1./255)
train_generator = train_datagen.flow_from_directory(train_dir,
                                                    target_size = (150,150),
                                                    batch_size = 20,
                                                    class_mode = 'binary')
validation_generator = validation_datagen.flow_from_directory(validation_dir,
                                                              target_size = (150,150),
                                                              batch_size = 20,
                                                              class_mode = 'binary')
model.compile(loss = 'binary_crossentropy',
              optimizer = optimizers.RMSprop(lr = 2e-5),
              metrics = ['acc'])
history = model.fit_generator(train_generator,steps_per_epoch = 100,epochs = 30,
                              validation_data = validation_generator,
                              validation_steps = 50)

#绘制结果
import matplotlib.pyplot as plt

acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']

epochs = range(1,len(acc) + 1)

plt.plot(epochs,acc,'bo',label = 'Training acc')
plt.plot(epochs,val_acc,'r',label = 'Validation acc')
plt.title('Training and Validation accuracy')
plt.legend()

plt.figure()

plt.plot(epochs,loss,'bo',label = 'Training loss')
plt.plot(epochs,val_loss,'r',label = 'Validation loss')
plt.title('Training and Validation loss')
plt.legend()

plt.show()
model.save('cats_and_dogs_small_freeze.h5')

在这里插入图片描述
在这里插入图片描述
这里准确度达到了90%了
如果做了数据增强:

#做数据增强
train_datagen = ImageDataGenerator(rescale = 1./255,
                                   rotation_range = 40,
                                   width_shift_range = 0.2,
                                   height_shift_range = 0.2,
                                   shear_range = 0.2,
                                   zoom_range = 0.2,
                                   horizontal_flip = True,
                                   fill_mode = 'nearest')

将精确度为90%的模型用于预测之前预测错了的图片:
在这里插入图片描述
之前错认为是狗,现在可以正确识别是猫了:
在这里插入图片描述
(流泪。。)
在这里插入图片描述
在这里插入图片描述
结果出来并没有书上的96%那么好。

(2)模型微调
另一种方法是模型微调,微调是指,冻结几层和新增加几层,新增加的是全连接分类器,冻结的是卷积层的前面几层

冻结VGG16的卷积基是为了能够在上面训练一个随机初始化的分类器,训练好了分类器之后,才能将上面的卷积层解冻,然后联合训练这所有的层

步骤:
1.在已经训练好的基网络(base network)上添加自定义网络
2.冻结基网络
3.训练所添加的部分
4.解冻及网络的冻结部分
5.联合训练解冻的这些层和天机的部分

前面三个步骤与特征提取是一样的,接下来需要微调最后三个卷积层

#4.冻结直到‘block5_conv1’的所有层
from keras import layers
from keras import optimizers
set_trainable = False
for layer in conv_base.layers:
    if layer.name == 'block5_conv1':
        set_trainable = True
    if set_trainable == True:
        layer.trainable = True
    else:
        layer.trainable = False


#导入前三部训练好的模型
from keras.models import load_model
model = load_model('cats_and_dogs_small_freeze.h5')

#5.模型微调       
model.compile(loss = 'binary_crossentropy',
              optimizer = optimizers.RMSprop(lr = 1e-5),
              metrics = ['acc'])

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

#绘制图像
import matplotlib.pyplot as plt

acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']

epochs = range(1,len(acc) + 1)

plt.plot(epochs,acc,'bo',label = 'Training acc')
plt.plot(epochs,val_acc,'r',label = 'Validation acc')
plt.title('Training and Validation accuracy')
plt.legend()

plt.figure()

plt.plot(epochs,loss,'bo',label = 'Training loss')
plt.plot(epochs,val_loss,'r',label = 'Validation loss')
plt.title('Training and Validation loss')
plt.legend()

plt.show()
model.save('cats_and_dogs_small_freeze_weitiao.h5')

ResourceExhaustedError!
内存耗尽了。。。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值