预训练网络:之前在大型数据集上训练好,保存好的网络。如果原始数据集足够大,足够通用,则预训练网络学到特征的空间层次结构可以有效的作为通用模型。
使用预训练网络的两种方法:
卷积层提取表示的通用性取决于层中模型的深度,更靠近底部的是局部的高度通用的特征图,靠近顶部的是更加抽象的概念。
在添加密集连接分类器时,有两种添加方式:
1)先运行数据,将输出结果转为Numpy数组输入密集连接分类器。无法使用数据增强。
2)直接为提取的卷积基添加添加密集连接分类器,再在完整的网络上训练。代价高,但可以设置数据增强。
from keras.applications import VGG16
#卷积基VGG16实例化
#参数(模型初始化的权重检查点,是否包含密集连接分类器,输入到网络中张量的形状(可选))
conv_base = VGG16(weights='imagenet',include_top=False,input_shape=(150,150,3))
#使用预训练的卷积基提取特征
import os
import numpy as np
from keras.preprocessing.image import ImageDataGenerator
#此处对应笔记(三)中存放复制猫狗数据的根文件路径
base_dir='D:\\jupyter_code\\kaggle\\train1'
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(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)
#提取形状展平 ,便于输入到密集连接分类器
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 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))
#采用数据增强的特征提取。拓展conv_base
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'))
#冻结卷积基,防止卷积基权重更新。导致之前所学的表示被破坏
conv_base.trainable=False
from keras.preprocessing.image import ImageDataGenerator
from keras import optimizers
#利用ImageDataGenerator设置数据增强
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'
)
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'
)
validation_generator=test_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
)
2.模型微调:将顶部几层解冻,对解冻的几层和新增加的Dense层进行联合训练,他只是略微调整了所复用模型中更加抽象的表示。
卷积基中更靠近底部的层编码是更加通用可复用特征,靠近顶部的层编码是更专业化的特征。
训练的参数越多,过拟合的风险越大。
使用预训练的卷积神经网络 |
特征提取 |
在已经训练好的基网络上添加自定义网络 |
冻结基网络 |
||
训练所添加的部分 |
||
模型微调 |
解冻基网络中部分层 |
|
联合训练解冻层和添加层 |
#模型微调
#解冻直到某一层的所有层
#可通过conv_base.summary()查询卷积基的架构
conv_base.trainable=True
set_trainable=False
for layer in conv_base.layers:
if layer.name=='block_conv1':
set_trainable=True
if set_trainable:
layer.trainable=True
else:
layers.trainable=False
#微调模型
model.compile(loss='binary_crossentropy',
optimizer=optimizers.RMSprop(lr=1e-5),
metrics=['acc'])
history=model.fit_generator(
train_generator,
steps_per_epoch=100,
epochs=100,
validation_data=validation_generator,
validation_steps=50
)
#测试数据上最终评估这个模型
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 acc':test_acc)