吃水不忘挖井人
参考博客:https://blog.csdn.net/u012524708/article/details/79609118
https://blog.csdn.net/sinat_26917383/article/details/72861152
简单的keras网络搭建参考,很清楚
https://www.jianshu.com/p/09b5a5d82eec
接下来编写两种VGG 迁移方法训练
1. fine-tuning方式一:使用预训练网络的bottleneck特征
1、加载预训练权重
2、运行,提取bottleneck feature(网络在全连接之前的最后一层激活的feature
map,卷积-全连接层之间),单独拿出来,并保存
3、bottleneck层数据,之后 + dense全连接层,进行fine-tuning
加载预训练权重
#from keras.applications.vgg16_matt import VGG16
from keras.applications import VGG16
model=applications.VGG16(weights="imagenet",include_top=False,input_shape=(img_size, img_size, 3))
model.summary()
提取bottleneck特征
需要步骤:
载入图片;
灌入pre-model的权重;
得到bottleneck feature
数据目录格式
date
- class1
- class2
读取数据,载入训练集,测试集
#本实验用pkl保存数据
#读取数据
Train_Combined = pd.read_pickle("./date/engage_Dateframe/df_human_train.pkl") # no animate,only rename
Test_Humans = pd.read_pickle("./date/engage_Dateframe/df_human_test.pkl")
print(Train_Combined.shape, Test_Humans.shape) # (141, 4) (29, 4) (43, 4)
TrainCombined_batch_pointer = 0
CVHumans_batch_pointer = 0
TestHumans_batch_pointer = 0
print("now training")
TrainCombined_Labels = pd.get_dummies(Train_Combined["Labels"]).as_matrix()
print(TrainCombined_Labels.shape)
TestHuman_Labels = pd.get_dummies(Test_Humans["Labels"]).as_matrix()
print(TestHuman_Labels.shape)
# model.compile(loss='categorical_crossentropy', optimizer='sgd', metrics=['acc'])
# model.compile(loss='categorical_crossentropy', optimizer=SGD(lr=0.001, momentum=0.9, nesterov=True), metrics=["acc"])由于pkl
#由于pkl文件的格式,这里需要进行对其处理
def loadCombinedTrainBatch(size): #训练集
global TrainCombined_batch_pointer
batch_images = []
batch_labels = []
for i in range(size):
path1 = Train_Combined.iloc[TrainCombined_batch_pointer + i]["folderName"]
path2 = Train_Combined.iloc[TrainCombined_batch_pointer + i]["imageName"]
read_image = Image.open(os.path.join(path1, path2))
read_image_final = np.array(read_image) / 255.0 # here, we are normalizing the images
# print(read_image_final.shape)
batch_images.append(read_image_final)
# print(batch_images)
batch_labels.append(TrainCombined_Labels[TrainCombined_batch_pointer + i]) # appending corresponding labels
TrainCombined_batch_pointer += size
return np.array(batch_images), np.array(batch_labels)
def loadCombinedTestBatch(size): #测试集
global TestHumans_batch_pointer
batch_images = []
batch_labels = []
for i in range(size):
path1 = Test_Humans.iloc[TestHumans_batch_pointer + i]["folderName"]
path2 = Test_Humans.iloc[TestHumans_batch_pointer + i]["imageName"]
read_image = Image.open(os.path.join(path1, path2))
read_image_final = np.array(read_image) / 255.0 # here, we are normalizing the images
# print(read_image_final.shape)
batch_images.append(read_image_final)
# print(batch_images)
batch_labels.append(TestHuman_Labels[TestHumans_batch_pointer + i]) # appending corresponding labels
TestHumans_batch_pointer += size
return np.array(batch_images), np.array(batch_labels)
x_train, y_train = loadCombinedTrainBatch(len(Train_Combined))
x_test, y_test = loadCombinedTestBatch(len(Test_Humans))
提取bottleneck特征
bottleneck_features_train = model.predict(x_train)
np.save(('bottle_feature/bottleneck_features_train.npy'), bottleneck_features_train)
np.save(('bottle_feature/bottleneck_features_train_labels.npy'), y_train)
bottleneck_features_validation = model.predict(x_test)
np.save(('bottle_feature/bottleneck_features_validation.npy'), bottleneck_features_validation)
np.save(('bottle_feature/bottleneck_features_validation_labels.npy'), y_test)
训练
#全链接层,顶层设计
def train_top_model():
train_data = np.load('bottle_feature/bottleneck_features_train.npy')
print('train_data shape',train_data.shape)
train_labels = np.load('bottle_feature/bottleneck_features_train_labels.npy')
validation_data = np.load('bottle_feature/bottleneck_features_validation.npy')
print("validation_data.shape",validation_data.shape)
validation_labels = np.load('bottle_feature/bottleneck_features_validation_labels.npy')
model = Sequential()
print(train_data.shape[1:])
model.add(Flatten(input_shape=train_data.shape[1:]))
model.add(Dense(256,activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(2,activation='sigmoid'))
model.compile(optimizer='rmsprop',
loss='binary_crossentropy',
metrics=['accuracy'])
#training
history=model.fit(train_data,train_labels, #此处用fit,遗憾不可以在线数据增强
epochs=epochs,
batch_size=batch_size,
validation_data=(validation_data,validation_labels))
model.save_weights('date/model_save/bottleneck_fc_model.h5')
model.save('date/model_save/model.h5')
return history,model
#可视化
history,model_all=train_top_model()
print(history.history)
# 绘制训练 & 验证的准确率值
plt.plot(history.history['acc'])
plt.plot(history.history['val_acc'])
plt.title('Model accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.legend(['acc', "val_acc"], loc='upper right')
plt.show()
参考
#如何提取bottleneck feature
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
# (1)载入图片
# 图像生成器初始化
from keras.preprocessing.image import ImageDataGenerator
import numpy as np
datagen = ImageDataGenerator(rescale=1./255)
# 训练集图像生成器
generator = datagen.flow_from_directory(
'/home/ubuntu/keras/animal5/train',
target_size=(150, 150),
batch_size=32,
class_mode=None,
shuffle=False)
# 验证集图像生成器
generator = datagen.flow_from_directory(
'/home/ubuntu/keras/animal5/validation',
target_size=(150, 150),
batch_size=32,
class_mode=None,
shuffle=False)
#(2)灌入pre-model的权重
model.load_weights('/.../vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5')
#(3)得到bottleneck feature
bottleneck_features_train = model.predict_generator(generator, 500)
# 核心,steps是生成器要返回数据的轮数,每个epoch含有500张图片,与model.fit(samples_per_epoch)相对
np.save(open('bottleneck_features_train.npy', 'w'), bottleneck_features_train)
bottleneck_features_validation = model.predict_generator(generator, 100)
# 与model.fit(nb_val_samples)相对,一个epoch有800张图片,验证集
np.save(open('bottleneck_features_validation.npy', 'w'), bottleneck_features_validation)
fine-tuning方式二:要调整权重
文件格式
date
- train
- 0
- 1
- test
- 0
- 1
fine-tune分三个步骤:
搭建vgg-16并载入权重,将之前定义的全连接网络加在模型的顶部,并载入权重
冻结vgg16网络的一部分参数
模型训练
此方法与上述方法有所不同
1.不需提取离线特征
2.图像文件格式不同
3.此方法将全链接层与卷基层连在一起,并冻结之前的卷基层。
注意:
1、fine-tune,所有的层都应该以训练好的权重为初始值,例如,你不能将随机初始的全连接放在预训练的卷积层之上,这是因为由随机权重产生的大梯度将会破坏卷积层预训练的权重。
2、选择只fine-tune最后的卷积块,而不是整个网络,这是为了防止过拟合。整个网络具有巨大的熵容量,因此具有很高的过拟合倾向。由底层卷积模块学习到的特征更加一般,更加不具有抽象性,因此我们要保持前两个卷积块(学习一般特征)不动,只fine-tune后面的卷积块(学习特别的特征)
3、fine-tune应该在很低的学习率下进行,通常使用SGD优化而不是其他自适应学习率的优化算法,如RMSProp。这是为了保证更新的幅度保持在较低的程度,以免毁坏预训练的特征
1.加载权重
model=applications.VGG16(weights="imagenet",include_top=False,input_shape=(img_size, img_size, 3))
model.summary()
2.顶层网络添加方法
x = model.output
# 最有问题的层:flatten层
x = Flatten(name='flatten')(x)
x = Dense(256, activation='relu', name='fc1')(x)
x = Dropout(0.5)(x)
predictions = Dense(2, activation='softmax')(x)
from keras.models import Model
vgg_model = Model(inputs=model.input, outputs=predictions) #网络链接要注意
print(model)
print(len(vgg_model.layers)) #23
dict_layers = dict([(layer.name,layer) for layer in vgg_model.layers])
print(len(dict_layers) )# 23
plot_model(vgg_model,to_file='vgg16_fc.png',show_shapes=True)
# 冻结了conv block5之前的所有卷积层权重,不包括输入层
for layer in vgg_model.layers[1:15]:
# print layer.name,layer
layer.trainable = False
vgg_model.compile(loss='binary_crossentropy', optimizer=SGD(lr=1e-4, momentum=0.9), metrics=['acc'])
训练模型
# 准备数据
train_data_dir = '/.../train'
validation_data_dir = '/.../validation'
img_width, img_height = 150, 150
nb_train_samples = 500
nb_validation_samples = 100
epochs = 50
batch_size = 16
# 图片预处理生成器
train_datagen = ImageDataGenerator(
rescale=1./255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True)
test_datagen = ImageDataGenerator(rescale=1./255)
# 图片generator
train_generator = train_datagen.flow_from_directory(
train_data_dir,
target_size=(img_height, img_width),
batch_size=32,
class_mode='categorical')
validation_generator = test_datagen.flow_from_directory(
validation_data_dir,
target_size=(img_height, img_width),
batch_size=32,
class_mode='categorical')
# 训练
vgg_model.fit_generator(
train_generator,
steps_per_epoch=nb_train_samples // batch_size,
epochs=epochs,
validation_data=validation_generator,
validation_steps=nb_validation_samples // batch_size)
相比第二种更简单。
但是会报各种错误。
好吧,都报错,第二种相对于可视化更为方便。