深度学习单分类keras 迁移学习和模型微调

数据集是猫狗分类的 网上很多资源
数据集:https://download.csdn.net/download/zqx951102/12675761

""""


#深度学习006-移花接木-用Keras迁移学习提升性能 https://www.jianshu.com/p/4e3d13a7f4bf
#此处我们用VGG16的卷积层统一提取所有图片的特征,将这些特征保存,然后直接加载特征来训练,加载数字比加载图片要快的多,故而训练也快得多
#可以看出,主要调整Conv block 5,前面的4个block不需调整。  https://www.jianshu.com/p/5c766be9a9d7
import numpy as np
import os,random,shutil
np.random.seed(7)

# 1, 准备数据集
#1,指定一些超参数:
train_data_dir='D:/Kaggle/dogsvscats/cats_and_dogs_small/train'
val_data_dir='D:/Kaggle/dogsvscats/cats_and_dogs_small/test' # keras中将测试集称为validation set
train_samples_num=2000 # train set中全部照片数
val_samples_num=800
IMG_W,IMG_H,IMG_CH=150,150,3 # 单张图片的大小
batch_size=16 # 不能是32,因为2000/32不能整除,后面会有影响。
epochs=180  # 用比较少的epochs数目做演示,节约训练时间



# 此处的训练集和测试集并不是原始图片的train set和test set,而是用VGG16对图片提取的特征,这些特征组成新的train set和test set
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Dropout, Flatten, Dense
from keras import applications

def save_bottlebeck_features():
    datagen = ImageDataGenerator(rescale=1. / 255) # 不需图片增强  #生成一个 生成器

    # build the VGG16 network
    model = applications.VGG16(include_top=False, weights='imagenet')
    # 使用imagenet的weights作为VGG16的初始weights,由于只是特征提取,故而只取前面的卷积层而不需要DenseLayer,故而include_top=False

    generator = datagen.flow_from_directory( # 产生train set  以文件夹路径为参数,生成经过数据提升/归一化后的数据,在一个无限循环中无限产生batch数据
        train_data_dir,  #train数据集扩充
        target_size=(IMG_W, IMG_H),
        batch_size=batch_size,
        class_mode=None, #shuffle函数的是将序列中的所有元素随机排序
        shuffle=False) # 必须为False,否则顺序打乱之后,和后面的label对应不上。
    bottleneck_features_train = model.predict_generator(  #预训练
        generator, train_samples_num // batch_size) # 如果是32,这个除法得到的是62,抛弃了小数,故而得到1984个sample
    np.save('D:/bottleneck_features_train.npy', bottleneck_features_train)
    print('bottleneck features of train set is saved.')

    generator = datagen.flow_from_directory(
        val_data_dir,
        target_size=(IMG_W, IMG_H),
        batch_size=batch_size,
        class_mode=None,
        shuffle=False)
    bottleneck_features_validation = model.predict_generator(
        generator, val_samples_num // batch_size)  #这个验证 相当于 test数据集
    np.save('D:/bottleneck_features_val.npy',bottleneck_features_validation)
    print('bottleneck features of test set is saved.')
save_bottlebeck_features()
#上述通过扩充train和验证(test)数据集,然后在模型中预训练 提取特征 include_top=False保证了顶端的分类层失活

def my_model():
    '''
    自定义一个模型,该·模型仅仅相当于一个分类器,只包含有全连接层,对提取的特征进行分类即可
    :return:
    '''
    # 模型的结构
    model = Sequential()
    model.add(Flatten(input_shape=train_data.shape[1:])) # 将所有data进行flatten  train_data:训练图片集的所有图片
    model.add(Dense(256, activation='relu')) # 256个全连接单元
    model.add(Dropout(0.5)) # dropout正则
    model.add(Dense(1, activation='sigmoid')) # 此处定义的模型只有后面的全连接层,由于是本项目特殊的,故而需要自定义  2分类的模型结构

    # 模型的配置
    model.compile(optimizer='rmsprop',
                  loss='binary_crossentropy', metrics=['accuracy']) # model的optimizer等

    return model



# 只需要训练分类器模型即可,不需要训练特征提取器
train_data = np.load('D:/bottleneck_features_train.npy') # 加载训练图片集的所有图片的VGG16-notop特征
train_labels = np.array(
    [0] * int((train_samples_num / 2)) + [1] * int((train_samples_num / 2)))
# label是1000个cat,1000个dog,由于此处VGG16特征提取时是按照顺序,故而[0]表示cat,1表示dog

validation_data = np.load('D:/bottleneck_features_val.npy')
validation_labels = np.array(
    [0] * int((val_samples_num / 2)) + [1] * int((val_samples_num / 2)))
#label是400个cat,400个dog,由于此处VGG16特征提取时是按照顺序,故而[0]表示cat,1表示dog
# 构建分类器模型
clf_model=my_model()
history_ft = clf_model.fit(train_data, train_labels,
              epochs=epochs,
              batch_size=batch_size,
              validation_data=(validation_data, validation_labels))

# 画图,将训练时的acc和loss都绘制到图上
import matplotlib.pyplot as plt
def plot_training(history):
    plt.figure(12)

    plt.subplot(121) #第一幅子图
    train_acc = history.history['acc']
    val_acc = history.history['val_acc']
    epochs = range(len(train_acc))
    plt.plot(epochs, train_acc, 'b', label='train_acc')
    plt.plot(epochs, val_acc, 'r', label='test_acc')
    plt.title('Train and Test accuracy')
    plt.legend()

    plt.subplot(122)  #第二幅子图
    train_loss = history.history['loss']
    val_loss = history.history['val_loss']
    epochs = range(len(train_loss))
    plt.plot(epochs, train_loss, 'b', label='train_loss')
    plt.plot(epochs, val_loss, 'r', label='test_loss')
    plt.title('Train and Test loss')
    plt.legend()

    plt.show()


plot_training(history_ft)
# 将本模型保存一下
clf_model.save_weights('D:/top_FC_model')

# Found 2000 images belonging to 2 classes.
# bottleneck features of train set is saved.
# Found 1000 images belonging to 2 classes.
# bottleneck features of test set is saved.
# Train on 2000 samples, validate on 800 samples
#loss: 0.0014 - acc: 0.9990 - val_loss: 3.0336 - val_acc: 0.7825    300代结果



"""""

loss: 0.0058 - acc: 0.9985 - val_loss: 2.7147 - val_acc: 0.7863 训练180代结果
在这里插入图片描述

#深度学习007-Keras微调进一步提升性能  https://www.jianshu.com/p/5c766be9a9d7
import numpy as np
import os,random,shutil
np.random.seed(7)

# 1, 准备数据集
#1,指定一些超参数:
train_data_dir='D:/Kaggle/dogsvscats/cats_and_dogs_small/train'
val_data_dir='D:/Kaggle/dogsvscats/cats_and_dogs_small/test' # keras中将测试集称为validation set
train_samples_num=2000 # train set中全部照片数
val_samples_num=800
IMG_W,IMG_H,IMG_CH=150,150,3 # 单张图片的大小
batch_size=16 # 不能是32,因为2000/32不能整除,后面会有影响。
epochs=20  # 用比较少的epochs数目做演示,节约训练时间

# 2,准备图片数据流
from keras.preprocessing.image import ImageDataGenerator
train_datagen = ImageDataGenerator( # 单张图片的处理方式,train时一般都会进行图片增强
        rescale=1. / 255, # 图片像素值为0-255,此处都乘以1/255,调整到0-1之间
        shear_range=0.2, # 斜切
        zoom_range=0.2, # 放大缩小范围
        horizontal_flip=True) # 水平翻转

train_generator = train_datagen.flow_from_directory(# 从文件夹中产生数据流
    train_data_dir, # 训练集图片的文件夹
    target_size=(IMG_W, IMG_H), # 调整后每张图片的大小
    batch_size=batch_size,
    class_mode='binary') # 此处是二分类问题,故而mode是binary

# 3,同样的方式准备测试集
val_datagen = ImageDataGenerator(rescale=1. / 255) # 只需要和trainset同样的scale即可,不需增强
val_generator = val_datagen.flow_from_directory(
        val_data_dir,
        target_size=(IMG_W, IMG_H),
        batch_size=batch_size,
        class_mode='binary')

# 4,构建模型
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Dropout, Flatten, Dense
from keras import applications
from keras import optimizers
from keras.models import Model


def build_model():
    base_model = applications.VGG16(weights='imagenet', include_top=False, input_shape=(IMG_W, IMG_H, IMG_CH))
    # 此处我们只需要卷积层不需要全连接层,故而inclue_top=False,一定要设置input_shape,否则后面会报错
    # 这一步使用applications模块自带的VGG16函数直接加载了模型和参数,作为我们自己模型的“身子”

    # 下面定义我们自己的分类器,作为我们自己模型的“头”
    top_model = Sequential()
    top_model.add(Flatten(input_shape=base_model.output_shape[1:]))  # 如果没有设置input_shape,这个地方报错,显示output_shape有很多None
    top_model.add(Dense(256, activation='relu'))
    top_model.add(Dropout(0.5))
    top_model.add(Dense(1, activation='sigmoid'))  # 二分类问题

    top_model.load_weights('D:/top_FC_model')
    # 上面定义了模型结构,此处要把训练好的参数加载进来,

    my_model = Model(inputs=base_model.input, outputs=top_model(base_model.output))  # 将“身子”和“头”组装到一起
    # my_model就是我们组装好的完整的模型,也已经加载了各自的weights

    # 普通的模型需要对所有层的weights进行训练调整,但是此处我们只调整VGG16的后面几个卷积层,所以前面的卷积层要冻结起来
    for layer in my_model.layers[:17]:  # 25层之前都是不需训练的
        layer.trainable = False

    # 模型的配置 fine-Tune的核心是对原始的骨架网络(此处为VGG16)进行参数的微调,所以需要用非常小的学习率,而且要用SGD优化器。
    my_model.compile(loss='binary_crossentropy',
                     optimizer=optimizers.SGD(lr=1e-4, momentum=0.9),  # 使用一个非常小的lr来微调
                     metrics=['accuracy'])
    return my_model
# 开始用train set来微调模型的参数
print('start to fine-tune my model')
my_model=build_model()
history_ft = my_model.fit_generator(
        train_generator,
        steps_per_epoch=train_samples_num // batch_size,  #2000/16=125批量
        epochs=epochs,
        validation_data=val_generator,
        validation_steps=val_samples_num // batch_size)

# 画图,将训练时的acc和loss都绘制到图上
import matplotlib.pyplot as plt

def plot_training(history):
    plt.figure(12)

    plt.subplot(121)
    train_acc = history.history['acc']
    val_acc = history.history['val_acc']
    epochs = range(len(train_acc))
    plt.plot(epochs, train_acc, 'b', label='train_acc')
    plt.plot(epochs, val_acc, 'r', label='test_acc')
    plt.title('Train and Test accuracy')
    plt.legend()

    plt.subplot(122)
    train_loss = history.history['loss']
    val_loss = history.history['val_loss']
    epochs = range(len(train_loss))
    plt.plot(epochs, train_loss, 'b', label='train_loss')
    plt.plot(epochs, val_loss, 'r', label='test_loss')
    plt.title('Train and Test loss')
    plt.legend()

    plt.show()


plot_training(history_ft)

print(len(my_model.layers)) #查看模型的整个层数    20层
#loss: 0.0912 - acc: 0.9700 - val_loss: 1.1004 - val_acc: 0.8675     等 于17层时候 15太乱  25全冻结了

0.1747 - acc: 0.9385 - val_loss: 0.3562 - val_acc: 0.8988 17层 20代 图看着凌乱了,,,再来试试100
在这里插入图片描述
loss: 0.0116 - acc: 0.9960 - val_loss: 0.7997 - val_acc: 0.8939 100代 结果
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/zqx951102/article/details/107706877
今日推荐