修改说明:为了能使迁移学习所训练出的模型能适应后续的知识蒸馏,因此对迁移的代码做了细节上的修订。后续红色标明字样,皆为修改后的内容
采用模型分离的方式构造迁移学习,可以降低反向传播时卷积运算的工作量。
1、卷积基的提取
首先选定迁移的目标并提取其卷积基 可按照不同的需求提取迁移特征。keras除此外还有 DenseNet121、MobileNet、Xception等网络可以用于迁移
注意:1、要按照个人需求和电脑配置调整 shape,batch_size, 和models数组内的模型名称
2、要从tensorflow.kreas中引入application库,这样子才能支持更多的模型库(如EfficientNet系列)
3、新增os环境设置,避免价值tensorflow时输出太多不必要的信息
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' # or any {'0', '1', '2'}
from keras.preprocessing.image import ImageDataGenerator
from keras.utils import np_utils
#from tensorflow.python.keras.utils import np_utils #上面语句报错可以改成这个
from tensorflow.keras import applications,Model
import numpy as np
import os,gc
batch_size=32#ResNet50,VGG16,InceptionV3 EfficientNetB7 NASNetLarge
shape=(226, 226)
# DenseNet169 DenseNet201 #DenseNet121 ResNet152V2 ,"InceptionV3","MobileNetV3Small","ResNet50V2","VGG19"
models=["EfficientNetB0"]#
for modelname in models:
mstr="applications.%s(include_top=False,input_shape=(114,114,3), weights='imagenet')"%modelname
base = eval(mstr)
print(modelname)
print(modelname)
model= Model(input=base.input, output=base.layers[-1].output)
2、数据集的划分
数据集的划分可以查看python工具方法 4 依据随机种子将图片文件划分为训练集、测试集、验证集_a486259的博客-CSDN博客,代码复制后即可运行
文件夹:flower 通过该代码可得到:flower-训练,flower-验证,flower-测试三个文件夹
3、特征的提取与保存
所提取的特征保存在features文件夹中,命名方式为:
modelname_data_训练、modelname_data_验证、modelname_data_测试
modelname_labels_训练、modelname_labels_验证、modelname_labels_测试
#代码复制过来不想调整缩进,所以使用了if True来控制
if True:
datagen = ImageDataGenerator()#(rescale=1.0 / 255)
root_path='./features/'
if not os.path.exists(root_path):
os.makedirs(root_path)
dirList=['训练','验证','测试']
data_path='flower//'
for path in dirList:
generator = datagen.flow_from_directory(data_path+'-'+path,
target_size=shape,
batch_size=batch_size,
class_mode='categorical',
shuffle=False)
print(generator.class_indices)#输出数据的labels
labels= np_utils.to_categorical(generator.classes)
np.save(open(root_path+'label.npy', 'wb'), generator.class_indices)
features= model.predict_generator(generator, len(generator.classes)/generator.batch_size)
print('result.shape: ',features.shape)
#import sys;sys.exit()
print(len(generator.classes))
print(path,'集-卷积池化数据固化成功!')
np.save(open('%s%s_data_%s.npy'%(root_path,modelname,path), 'wb'), features)
np.save(open('%s%s_labels_%s.npy'%(root_path,modelname,path), 'wb'), labels)
np.save(open('%s%s_fileName_%s.npy'%(root_path,modelname,path), 'wb'), generator.filenames)
gc.collect()
4、读取特征
import numpy as np
root_path='./features/VGG16_'
train_data = np.load(root_path+'data_训练.npy')
train_data=train_data.reshape(train_data.shape[0],-1)
train_label = np.load(root_path+'labels_训练.npy')
shuffle=True
if shuffle:
import random
randnum = random.randint(0,100)
random.seed(randnum)
random.shuffle(train_data)
random.seed(randnum)
random.shuffle(train_label)
validation_data = np.load(root_path+'data_验证.npy')
validation_data=validation_data.reshape(validation_data.shape[0],-1)
validation_label =np.load(root_path+'labels_验证.npy')
test_data = np.load(root_path+'data_测试.npy')
test_data=test_data.reshape(test_data.shape[0],-1)
test_label =np.load(root_path+'labels_测试.npy')
#数据的扁平化
inputShape=np.array(train_data.shape[1:]).prod()
train_data=train_data.reshape(-1,inputShape)
test_data=test_data.reshape(-1,inputShape)
validation_data=validation_data.reshape(-1,inputShape)
5、重新训练分类器
在这里修改了两行代码,移除了最后一个Dense中的激活函数,并为每一个dense设置了name,通过这种操作,可以已另一种方式实现模型拼接。需要进行更高级的知识蒸馏(实现kl loss,softmax升温)的朋友,请不要使用后续的模型拼接代码。
output = Dense(class_types,name='dense_out')(x)
output = Activation(tf.nn.softmax,name='Activation_out')(output)
import tensorflow as tf
from keras.models import Model
from keras.layers import Dense,Input,Dropout,Activation
inputs = Input(shape=(inputShape,))
x = Dense(100, activation='relu',name='dense1')(inputs)
x = Dense(50, activation='relu',name='dense2')(x)
x = Dropout(0.2)(x)
output = Dense(class_types,name='dense_out')(x)
output = Activation(tf.nn.softmax,name='Activation_out')(output)
model = Model(inputs=inputs, outputs=output)
model.compile(optimizer='adam',loss='categorical_crossentropy',metrics=['acc'])
from keras.callbacks import History,ModelCheckpoint,EarlyStopping
#最佳模型保存
ModelCheckpoint=ModelCheckpoint('best_model.h5', monitor='acc', verbose=0, save_best_only=True, save_weights_only=False, mode='auto', period=1)
#提前终止
EarlyStopping=EarlyStopping(monitor='val_loss', patience=2, verbose=2, mode='auto')
#历史数据记录
loss_history = History()
#模型训练
model.fit(train_data,train_label,epochs=100,batch_size=32,
callbacks=[ModelCheckpoint,EarlyStopping,loss_history]
validation_data=(validation_data, validation_label),
verbose=2)
#模型评估
loss,acc=model.evaluate(test_data, test_label )
print('loss:',loss,'acc:',acc)
#历史数据绘图
import pandas as pd
d=pd.DataFrame(data=loss_history.history)
d.to_csv('history.csv')
6、全部代码
01.feature_save.py 复制代码保存为01.feature_save.py,然后执行,完成迁移特征的提取与保存
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' # or any {'0', '1', '2'}
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.python.keras.utils.np_utils import to_categorical
from tensorflow.keras import applications,Model
import numpy as np
import os,gc
batch_size=32#ResNet50,VGG16,InceptionV3 EfficientNetB7 NASNetLarge
shape=(56, 56)
# DenseNet169 DenseNet201 #DenseNet121 ResNet152V2 ,"InceptionV3","MobileNetV3Small","ResNet50V2","VGG19"
models=["EfficientNetB0"]#
for modelname in models:
mstr="applications.%s(include_top=False,input_shape=(114,114,3), weights='imagenet')"%modelname
base = eval(mstr)
print(modelname)
print(modelname)
model= Model(inputs=base.input, outputs=base.layers[-1].output)
datagen = ImageDataGenerator()#(rescale=1.0 / 255)
root_path='./features/'
if not os.path.exists(root_path):
os.makedirs(root_path)
dirList=['训练','验证','测试']
data_path = r"./flower"
for path in dirList:
generator = datagen.flow_from_directory(data_path+'-'+path,
target_size=shape,
batch_size=batch_size,
class_mode='categorical',
shuffle=False)
print(generator.class_indices)#输出数据的labels
labels= to_categorical(generator.classes)
np.save(open(root_path+'label.npy', 'wb'), generator.class_indices)
features= model.predict_generator(generator, len(generator.classes)/generator.batch_size)
print('result.shape: ',features.shape)
#import sys;sys.exit()
print(len(generator.classes))
print(path,'集-卷积池化数据固化成功!')
np.save(open('%s%s_data_%s.npy'%(root_path,modelname,path), 'wb'), features)
np.save(open('%s%s_labels_%s.npy'%(root_path,modelname,path), 'wb'), labels)
np.save(open('%s%s_fileName_%s.npy'%(root_path,modelname,path), 'wb'), generator.filenames)
gc.collect()
02.new_cls.py 复制代码保存为02.new_cls.py,然后执行,完成分类器训练
#------------------------------特征数据读取---------------------------------------
import numpy as np
class_types=2
root_path='./features/VGG16_'
train_data = np.load(root_path+'data_训练.npy')
train_label = np.load(root_path+'labels_训练.npy')
shuffle=True
if shuffle:
import random
randnum = random.randint(0,100)
random.seed(randnum)
random.shuffle(train_data)
random.seed(randnum)
random.shuffle(train_label)
validation_data = np.load(root_path+'data_验证.npy')
validation_label =np.load(root_path+'labels_验证.npy')
test_data = np.load(root_path+'data_测试.npy')
test_label =np.load(root_path+'labels_测试.npy')
#-----------------------------------重新训练全连接分类器--------------------------
from keras.models import Model
from keras.layers import Dense,Input,Dropout,GlobalAveragePooling2D
inputs = Input(shape=test_data[0].shape)
x = GlobalAveragePooling2D()(inputs)#GlobalAveragePooling2D 为了减少对数据的形变操作,和降低模型参数量
x = Dense(100, activation='relu')(x)
x = Dense(50, activation='relu')(x)
x = Dropout(0.2)(x)
output = Dense(class_types, activation='softmax')(x)
model = Model(inputs=inputs, outputs=output)
model.compile(optimizer='adam',loss='categorical_crossentropy',metrics=['acc'])
from keras.callbacks import History,ModelCheckpoint,EarlyStopping
#最佳模型保存
ModelCheckpoint=ModelCheckpoint('gap_best_model.h5', monitor='acc', verbose=0, save_best_only=True, save_weights_only=False, mode='auto', period=1)
#提前终止
EarlyStopping=EarlyStopping(monitor='val_loss', patience=15, verbose=2, mode='auto')
#历史数据记录
loss_history = History()
#模型训练
model.fit(train_data,train_label,epochs=100,batch_size=32,
callbacks=[ModelCheckpoint,EarlyStopping,loss_history],
validation_data=(validation_data, validation_label),
verbose=2)
#模型评估
loss,acc=model.evaluate(test_data, test_label)
print('test loss:',loss,'acc:',acc)
loss,acc=model.evaluate(train_data, train_label)
print('train loss:',loss,'acc:',acc)
loss,acc=model.evaluate(validation_data, validation_label)
print('valid loss:',loss,'acc:',acc)
#历史数据绘图
import pandas as pd
d=pd.DataFrame(data=loss_history.history)
d.to_csv(root_path+'history.csv')
7、模型拼接
复制代码既可用 深度学习 11 keras迁移学习模型拼接_a486259的博客-CSDN博客
8、迁移模型知识蒸馏
迁移后的模型体量太大,复制该代码既可