关于深度学习系列笔记十二(关于猫狗判断实验)

2022-03-11 13:36:18 浏览数 (1)

首先小小的庆祝一下自己的微信公众号订阅用户已达到100人了,是小小的虚荣心也是收获也是鞭策,希望自己砥砺前行,努力进步,做到更好。

其实写文章,比较难的是对知识的沉淀,这个沉淀有多种,代码的沉淀,一件事物理解上的沉淀,甚至整合代码和文章的沉淀,要考虑他人的感受,也要在自己的能力范围内进行控制等等不一而足。

本笔记是经典的猫狗识别问题,大概25000的训练集图片加上12500的测试集图片,猫狗图片各一半,共约700多M,而之前的手写数字也不过10M多一些,对计算机处理而言都是比较耗资源的,何况这个实验的目的本来就是从小样本提升准确率。从实质上这些模型和前面的模型没有本质区别,唯一的区别是ImageDataGenerator的应用,能够把图片直接转换为浮点数向量。

可视化训练集和验证集的损失率和准确率

可以看出随着训练轮次的增加,

训练集的准确率呈对数级上升,而验证集的准确率则在第十轮左右维持在72%的准确率

训练集的损失度呈对数级下降,而验证集的损失度则在第十轮左右最低,此后不断上升

因此本例子主要还是过度拟合导致,根本原因是样本数量不足,只有2000训练集样本

在增加dropout层以后,训练准确率较之前有所下降,但验证准确率较之前有所提升达到75%。

在利用数据增强生成器训练卷积神经网络后,训练集和验证集的准确率基本是同步的,最高上升到78%

代码示例

代码语言:javascript复制
import os, shutil
def initdata():
    #原始数据集解压目录的两个路径
    original_dataset_train_dir = 'D:/Python36/Coding/PycharmProjects/ttt/dataset/train'
    original_dataset_test_dir = 'D:/Python36/Coding/PycharmProjects/ttt/dataset/test'

    #保存较小数据集的目录
    base_dir = 'D:/Python36/Coding/PycharmProjects/ttt/dataset/small'
    os.mkdir(base_dir)

    #分别对应划分后的训练、验证和测试的目录
    train_dir = os.path.join(base_dir, 'train')
    os.mkdir(train_dir)
    validation_dir = os.path.join(base_dir, 'validation')
    os.mkdir(validation_dir)
    test_dir = os.path.join(base_dir, 'test')
    os.mkdir(test_dir)

    #猫的训练图像目录
    train_cats_dir = os.path.join(train_dir, 'cats')
    os.mkdir(train_cats_dir)
    #狗的训练图像目录
    train_dogs_dir = os.path.join(train_dir, 'dogs')
    os.mkdir(train_dogs_dir)
    #猫的验证图像目录
    validation_cats_dir = os.path.join(validation_dir, 'cats')
    os.mkdir(validation_cats_dir)
    #狗的验证图像目录
    validation_dogs_dir = os.path.join(validation_dir, 'dogs')
    os.mkdir(validation_dogs_dir)
    #猫的测试图像目录
    test_cats_dir = os.path.join(test_dir, 'cats')
    os.mkdir(test_cats_dir)
    #狗的测试图像目录
    test_dogs_dir = os.path.join(test_dir, 'dogs')
    os.mkdir(test_dogs_dir)

    #将前1000 张猫的图像复制到train_cats_dir
    fnames = ['cat.{}.jpg'.format(i) for i in range(1000)]
    for fname in fnames:
        src = os.path.join(original_dataset_train_dir, fname)
        dst = os.path.join(train_cats_dir, fname)
        shutil.copyfile(src, dst)
    #将接下来500张猫的图像复制到validation_cats_dir
    fnames = ['cat.{}.jpg'.format(i) for i in range(1000, 1500)]
    for fname in fnames:
        src = os.path.join(original_dataset_train_dir, fname)
        dst = os.path.join(validation_cats_dir, fname)
        shutil.copyfile(src, dst)
    #将接下来的500 张猫的图像复制到test_cats_dir
    fnames = ['cat.{}.jpg'.format(i) for i in range(1500, 2000)]
    for fname in fnames:
        src = os.path.join(original_dataset_train_dir, fname)
        dst = os.path.join(test_cats_dir, fname)
        shutil.copyfile(src, dst)
    #将前1000 张狗的图像复制到train_dogs_dir
    fnames = ['dog.{}.jpg'.format(i) for i in range(1000)]
    for fname in fnames:
        src = os.path.join(original_dataset_train_dir, fname)
        dst = os.path.join(train_dogs_dir, fname)
        shutil.copyfile(src, dst)
    #将接下来500张狗的图像复制到validation_dogs_dir
    fnames = ['dog.{}.jpg'.format(i) for i in range(1000, 1500)]
    for fname in fnames:
        src = os.path.join(original_dataset_train_dir, fname)
        dst = os.path.join(validation_dogs_dir, fname)
        shutil.copyfile(src, dst)
    #将接下来的500 张狗的图像复制到test_dogs_dir
    fnames = ['dog.{}.jpg'.format(i) for i in range(1500, 2000)]
    for fname in fnames:
        src = os.path.join(original_dataset_train_dir, fname)
        dst = os.path.join(test_dogs_dir, fname)
        shutil.copyfile(src, dst)
    #检查一下,看看每个分组(训练/ 验证/ 测试)中分别包含多少张图像。
    print('total training cat images:', len(os.listdir(train_cats_dir)))
    # total training cat images: 1000
    print('total training dog images:', len(os.listdir(train_dogs_dir)))
    # total training dog images: 1000
    print('total validation cat images:', len(os.listdir(validation_cats_dir)))
    # total validation cat images: 500
    print('total validation dog images:', len(os.listdir(validation_dogs_dir)))
    # total validation dog images: 500
    print('total test cat images:', len(os.listdir(test_cats_dir)))
    # total test cat images: 500
    print('total test dog images:', len(os.listdir(test_dogs_dir)))
    # total test dog images: 500

    #即整体目录和图片数量如下
    # small
    #    test
    #        cats         500
    #        dogs         500
    #    train
    #        cats         1000
    #        dogs         1000
    #    validation
    #        cats         1000
    #        dogs         1000
    return train_dir,validation_dir

from keras.preprocessing.image import ImageDataGenerator

#数据预处理
def previewprocess(train_dir,validation_dir):
    #(1) 读取图像文件。
    #(2) 将JPEG 文件解码为RGB 像素网格。
    #(3) 将这些像素网格转换为浮点数张量。
    #(4) 将像素值(0~255 范围内)缩放到[0, 1] 区间(正如你所知,神经网络喜欢处理较小的输入值)。
    train_datagen = ImageDataGenerator(rescale=1./255)   #将所有图像乘以1/255 缩放
    test_datagen = ImageDataGenerator(rescale=1./255)    #将所有图像乘以1/255 缩放
    # Found 2000 images belonging to 2 classes.
    # Found 1000 images belonging to 2 classes.
    # 它生成了150×150的RGB图像[形状为(20, 150, 150, 3)]与二进制标签[形状为(20, )]组成的批量。
    # 每个批量中包含20个样本(批量大小)
    train_generator = train_datagen.flow_from_directory(
        train_dir,
        target_size=(150, 150), #将所有图像的大小调整为150×150
        batch_size=20,
        class_mode='binary')  #因为使用了binary_crossentropy损失,所以需要用二进制标签
    validation_generator = test_datagen.flow_from_directory(
        validation_dir,
        target_size=(150, 150), #将所有图像的大小调整为150×150
        batch_size=20,
        class_mode='binary')  #因为使用了binary_crossentropy损失,所以需要用二进制标签
    #train_generator和validation_generator数据格式如下:即4维数组和一个目标标签数组
    #array([[[[0.26666668, 0.27058825, 0.2509804],
    #         [0.26666668, 0.27058825, 0.2509804],
    #         [0.26666668, 0.27058825, 0.2509804],
    #         ...,
    #         [0.6745098, 0.67058825, 0.6627451],
    #         [0.65882355, 0.654902, 0.64705884],
    #         [0.64705884, 0.6431373, 0.63529414]]]],
    #      dtype=float32),
    #array([0., 1., 0., 0., 1., 1., 1., 0., 0., 1., 1., 1., 1., 0., 1., 0., 1., 1., 1., 0.],
    #      type=float32))
    #begin time= Fri May 17 14:43:46 2019
    for data_batch, labels_batch in train_generator:
        print('data batch shape:', data_batch.shape)
        #data batch shape: (20, 150, 150, 3)
        print('labels batch shape:', labels_batch.shape)
        break
    return  train_generator,validation_generator

def enhenceimage(train_dir,validation_dir):
    #利用ImageDataGenerator来设置数据增强
    '''
    datagen = ImageDataGenerator(
        rotation_range=40,      #是角度值(在 0~180 范围内),表示图像随机旋转的角度范
        width_shift_range=0.2,  #是图像在水平或垂直方向上平移的范围(相对于总宽度或总高度的比例
        height_shift_range=0.2, #是图像在水平或垂直方向上平移的范围(相对于总宽度或总高度的比例
        shear_range=0.2,        #随机错切变换的角度
        zoom_range=0.2,         #是图像随机缩放的范围
        horizontal_flip=True,  #是随机将一半图像水平翻转
        fill_mode='nearest')  #是用于填充新创建像素的方法,这些新像素可能来自于旋转或宽度/高度平移
    '''
    #利用数据增强生成器训练卷积神经网络
    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,)
    test_datagen = ImageDataGenerator(rescale=1./255)
    train_generator = train_datagen.flow_from_directory(
        train_dir,
        target_size=(150, 150),
        batch_size=32,
        class_mode='binary')
    validation_generator = test_datagen.flow_from_directory(
        validation_dir,
        target_size=(150, 150),
        batch_size=32,
        class_mode='binary')
    return  train_generator,validation_generator

from keras import layers
from keras import models
def buildmodel():
    #构建模型
    model = models.Sequential()
    #4个卷积层和4个池化层以及2个全连接层
    model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(150, 150, 3)))
    model.add(layers.MaxPooling2D((2, 2)))
    model.add(layers.Conv2D(64, (3, 3), activation='relu'))
    model.add(layers.MaxPooling2D((2, 2)))
    model.add(layers.Conv2D(128, (3, 3), activation='relu'))
    model.add(layers.MaxPooling2D((2, 2)))
    model.add(layers.Conv2D(128, (3, 3), activation='relu'))
    model.add(layers.MaxPooling2D((2, 2)))
    model.add(layers.Flatten())
    model.add(layers.Dense(512, activation='relu'))
    model.add(layers.Dense(1, activation='sigmoid'))
    print(model.summary())
    return model

def buildmodelwithdropout():
    #构建模型
    model = models.Sequential()
    #4个卷积层和4个池化层以及2个全连接层
    model.add(layers.Conv2D(32, (3, 3), activation='relu',input_shape=(150, 150, 3)))
    model.add(layers.MaxPooling2D((2, 2)))
    model.add(layers.Conv2D(64, (3, 3), activation='relu'))
    model.add(layers.MaxPooling2D((2, 2)))
    model.add(layers.Conv2D(128, (3, 3), activation='relu'))
    model.add(layers.MaxPooling2D((2, 2)))
    model.add(layers.Conv2D(128, (3, 3), activation='relu'))
    model.add(layers.MaxPooling2D((2, 2)))
    model.add(layers.Flatten())
    model.add(layers.Dropout(0.5))
    model.add(layers.Dense(512, activation='relu'))
    model.add(layers.Dense(1, activation='sigmoid'))
    print(model.summary())
    return model

from keras import optimizers
# initdata()
train_generator = 'D:/Python36/Coding/PycharmProjects/ttt/dataset/small/train'
validation_generator = 'D:/Python36/Coding/PycharmProjects/ttt/dataset/small/validation'
train_generator,validation_generator=previewprocess(train_generator,validation_generator)

model=buildmodel()
modelwithdropout=buildmodelwithdropout()
#Layer (type)                 Output Shape              Param #
#=================================================================
#conv2d_1 (Conv2D)            (None, 148, 148, 32)      896
#_________________________________________________________________
#max_pooling2d_1 (MaxPooling2 (None, 74, 74, 32)        0
#_________________________________________________________________
#conv2d_2 (Conv2D)            (None, 72, 72, 64)        18496
#_________________________________________________________________
#max_pooling2d_2 (MaxPooling2 (None, 36, 36, 64)        0
#_________________________________________________________________
#conv2d_3 (Conv2D)            (None, 34, 34, 128)       73856
#_________________________________________________________________
#max_pooling2d_3 (MaxPooling2 (None, 17, 17, 128)       0
#_________________________________________________________________
#conv2d_4 (Conv2D)            (None, 15, 15, 128)       147584
#_________________________________________________________________
#max_pooling2d_4 (MaxPooling2 (None, 7, 7, 128)         0
#_________________________________________________________________
#flatten_1 (Flatten)          (None, 6272)              0
#_________________________________________________________________
#dense_1 (Dense)              (None, 512)               3211776
#_________________________________________________________________
#dense_2 (Dense)              (None, 1)                 513
#=================================================================
# 使用RMSprop 优化器。
# 因为网络最后一层是单一sigmoid单元,所以我们将使用二元交叉熵作为损失函数
#为模型选择正确的最后一层激活和损失函数
#问题类型   最后一层激活  损失函数
#二分类问题   sigmoid    binary_crossentropy
#多分类、单标签问题   softmax    categorical_crossentropy
#多分类、多标签问题   sigmoid    binary_crossentropy
#回归到任意值 无   mse
#回归到0~1 范围内的值    sigmoid    mse 或binary_crossentropy
model.compile(loss='binary_crossentropy',optimizer=optimizers.RMSprop(lr=1e-4),metrics=['acc'])
history = model.fit_generator(train_generator,steps_per_epoch=100,epochs=30,validation_data=validation_generator,validation_steps=50)
#保存模型
model.save('cats_and_dogs_small_1.h5')

#编译、训练、保存带dropout模式的模型
modelwithdropout.compile(loss='binary_crossentropy',optimizer=optimizers.RMSprop(lr=1e-4),metrics=['acc'])
historywithdropout = modelwithdropout.fit_generator(train_generator,steps_per_epoch=100,epochs=30,validation_data=validation_generator,validation_steps=50)
#保存模型
modelwithdropout.save('cats_and_dogs_small_1_withdropout.h5')

#和上面同样的模型,只是增强了图片样本
train_generator = 'D:/Python36/Coding/PycharmProjects/ttt/dataset/small/train'
validation_generator = 'D:/Python36/Coding/PycharmProjects/ttt/dataset/small/validation'

train_generator,validation_generator=enhenceimage(train_generator,validation_generator)
modelwithenhencedropout=buildmodelwithdropout()
modelwithenhencedropout.compile(loss='binary_crossentropy',optimizer=optimizers.RMSprop(lr=1e-4),metrics=['acc'])
historywithenhencedropouthistory = modelwithenhencedropout.fit_generator(train_generator,steps_per_epoch=100,epochs=30,validation_data=validation_generator,validation_steps=50)
modelwithenhencedropout.save('cats_and_dogs_small_1_withehencedropout.h5')

#可视化训练集和验证集的损失率和准确率
# 可以看出随着训练轮次的增加,
# 训练集的准确率呈对数级上升,而验证集的准确率则在第十轮左右维持在72%的准确率
# 训练集的损失度呈对数级下降,而验证集的损失度则在第十轮左右最低,此后不断上升
# 因此本例子主要还是过度拟合导致,根本原因是样本数量不足,只有2000训练集样本
# 在增加dropout层以后,训练准确率较之前有所下降,但验证准确率较之前有所提升达到75%。
# 在利用数据增强生成器训练卷积神经网络后,训练集和验证集的准确率基本是同步的,最高上升到78%
#150s 2s/step - loss: 0.0340 - acc: 0.9900 - val_loss: 1.1205 - val_acc: 0.7190
#153s 2s/step - loss: 0.2005 - acc: 0.9245 - val_loss: 0.6639 - val_acc: 0.7500
#244s 2s/step - loss: 0.4792 - acc: 0.7687 - val_loss: 0.4896 - val_acc: 0.7494
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']
accwithdropout = historywithdropout.history['acc']
val_accwithdropout = historywithdropout.history['val_acc']
losswithdropout = historywithdropout.history['loss']
val_losswithdropout = historywithdropout.history['val_loss']
accwithenhencedropout = historywithenhencedropouthistory.history['acc']
val_accwithenhencedropout = historywithenhencedropouthistory.history['val_acc']
losswithenhencedropout = historywithenhencedropouthistory.history['loss']
val_losswithenhencedropout = historywithenhencedropouthistory.history['val_loss']

epochs = range(1, len(acc)   1)
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.plot(epochs, accwithdropout, 'bo', label='Training acc withdropout',color='red')
plt.plot(epochs, val_accwithdropout, 'b', label='Validation acc withdropout',color='red')
plt.plot(epochs, accwithenhencedropout, 'bo', label='Training acc withenhencedropout',color='yellow')
plt.plot(epochs, val_accwithenhencedropout, 'b', label='Validation acc withenhencedropout',color='yellow')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.plot(epochs, losswithdropout, 'bo', label='Training loss withdropout',color='red')
plt.plot(epochs, val_losswithdropout, 'b', label='Validation loss withdropout',color='red')
plt.plot(epochs, losswithenhencedropout, 'bo', label='Training loss withenhencedropout',color='yellow')
plt.plot(epochs, val_losswithenhencedropout, 'b', label='Validation loss withenhencedropout',color='yellow')
plt.title('Training and validation loss')
plt.legend()
plt.show()

0 人点赞