python深度学习--dogs_vs_cat小量数据集训练

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/SunChao3555/article/details/88370052

 首先注册kaggle并下载dogs-vs-cats数据集大概800M【最好使用科学上网,速度较快】,将其解压到指定路径(注意要逐层解压至文件夹形式)

#os.mkdir()为创建文件夹【执行一次后应当将其注释掉只留后面可能用到的一些路径配置】

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import pylab
from pandas import DataFrame, Series
from keras import models, layers, optimizers, losses, metrics
from keras.utils.np_utils import to_categorical
import os,shutil

#-------------数据划分-------------------------------
# 原始数据集解压目录的路径
original_dataset_dir = 'F:/dogs-vs-cats/train/'
#保存较小数据集的目录
base_dir = 'F:/dogs-vs-cats/cats_and_dogs_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_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_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_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_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_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_dir, fname)
#     dst = os.path.join(test_dogs_dir, fname)
#     shutil.copyfile(src, dst)

#------------------------------------------------------

#每个分组中两个类别的样本数相同,这是一个平衡的二分类问题,分类精度可作为衡量成功的指标

#构建网络
#---------------------------------------
#卷积神经网络由 Conv2D 层(使用 relu 激活)和 MaxPooling2D 层交替堆叠构成
#由于这里要处理的是更大的图像和更复杂的问题,需要相应地增大网络,即再增
# 加一个 Conv2D+MaxPooling2D 的组合。
# 这既可以增大网络容量,也可以进一步减小特征图的尺寸, 使其在连接 Flatten
# 层时尺寸不会太大。本例中初始输入的尺寸为 150×150(有些随意的选择),所
# 以最后在 Flatten 层之前的特征图大小为 7×7
#-----------------------------------------
#注意 网络中特征图的深度在逐渐增大(从 32 增大到 128),而特征图的尺寸在逐渐减小(从 150×150 减小到 7×7)。这几乎是所有卷积神经网络的模式。
def model_build():
    model = models.Sequential()
    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'))
    #二分类问题,你面对的是一个二分类问题,所以网络最后一层是使用 sigmoid 激活的单一单元(大小为 1 的 Dense 层)。输出为某一类的概率
    print(model.summary())

    #编译模型
    model.compile(optimizer=optimizers.RMSprop(lr=1e-4),
                  loss='binary_crossentropy',
                  metrics=['acc'])
    return model

#数据预处理
#---------------------------------------------------------
#将数据输入神经网络之前,应该将数据格式化为经过预处理的浮点数张量
#现在,数据以 JPEG 文件的形式保存在硬盘中,所以数据预处理步骤大致如下。
    # (1) 读取图像文件。
    # (2) 将 JPEG 文件解码为RGB像素网格。
    # (3) 将这些像素网格转换为浮点数张量。
    # (4) 将像素值(0~255 范围内)缩放到 [0, 1] 区间(正如你所知,神经网络喜欢处理较小的输入值)。

#Keras 拥有自动完成这些步骤的工具。
# Keras 有一个图像处理辅助工具的模块,位于 keras.preprocessing.image
#特别地,它包含 ImageDataGenerator 类,可以快速创建 Python生成器,
# 能够将硬盘上的图像文件自动转换 为预处理好的张量批量。

#---------------------------------------------------------

from keras.preprocessing.image import ImageDataGenerator

def model_fit1(model):
    train_datagen=ImageDataGenerator(rescale=1./255)#将所有图像乘以 1/255 缩放至[0,1]
    test_datagen=ImageDataGenerator(rescale=1./255)

    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),
        batch_size=20,
        class_mode='binary'
    )
    '''
    Python 生成器(Python generator)是一个类似于迭代器的对象,一个可以和 for ...
    in 运算符一起使用的对象。生成器是用 yield 运算符来构造的。 下面一个生成器的例子,可以生成整数。
    def generator(): 
        i=0
        while True: 
            i += 1 yield i
    for item in generator(): 
        print(item) 
        if item > 4: 
        break
    输出结果如下。 1/n  2/n 3/n 4/n 5/n    
    '''

    for data_batch,labels_batch in train_generator:
        print(data_batch.shape)
        print(labels_batch.shape)
        break#生成器会不停地生成这些批量,它会不断循环目标文件夹中的图像
        #它生成了 150×150 的 RGB 图像[形状为 (20, 150, 150,3)]与二进制标签[形状为 (20,)]组成的批量。每个批量中包含 20 个样本(批量大小)

    #下面我们利用生成器,我们让模型对数据进行拟合
    #fit_generator 方法来拟合,它在数据生成器上的效果和 fit 相同

    history=model.fit_generator(
        train_generator,
        steps_per_epoch=100,
        #从生成器中抽取 steps_per_epoch个批量后(即运行了 steps_per_epoch
        #次梯度下降),拟合过程将进入下一个轮次[本例共有1000+1000个样本,
        # 前面训练数据生成器的batch=20,故这里为100]
        epochs=30,
        validation_data=validation_generator,
        #参数可以是一个数据生成器,但也可以是 Numpy 数组组成的元组
        #如果参数为生成器,那么这个生成器会不停地生成验证数据批量,
        # 因此还需要指定 validation_steps 参数与前面类似500+500 /20
        validation_steps=50
    )
    #保存模型
    model.save('cats_and_dogs_small_1.h5')
    return history

def acc_loss_plot(history):
    fig=plt.figure()
    ax1=fig.add_subplot(2,1,1)
    acc = history.history['acc']
    val_acc = history.history['val_acc']
    loss = history.history['loss']
    val_loss = history.history['val_loss']
    epochs = range(1, len(acc) + 1)
    ax1.plot(epochs, acc, 'bo', label='Training acc')
    ax1.plot(epochs, val_acc, 'b', label='Validation acc')
    ax1.set_title('Training and validation accuracy')

    ax2=fig.add_subplot(2,1,2)
    ax2.plot(epochs, loss, 'bo', label='Training loss')
    ax2.plot(epochs, val_loss, 'b', label='Validation loss')
    ax2.set_title('Training and validation loss')
    plt.legend()
    plt.tight_layout()
    plt.show()

#训练精度随着时间线性增加,直到接近 100%,而验 证精度则停留在 70%~72%。验证损失仅在 5 轮后就达到最小值,然后保持不变,而训练损失则 一直线性下降,直到接近于 0
#因为训练样本相对较少(2000个),所以最关心的问题是过拟合。这里不使用dropout 和权重衰减(L2 正则化等,而是针对计算机视觉领域的新方法-----数据增强


#利用ImageDataGenerator设置数据增强
#数据增强

#------------------------------------------------------
# 是从现有的训练样本中生成更多的训练数据,其方法是利用多种能够生成可信图像的随机变换来增加(augment)样本。其目标是,模型在训练时不会两次查看完全相同的图像。这让模型能够观察到数据的更多内容,从而具有更好的泛化能力。
'''
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'#填充新创建像素的方法,这些新像素可能来自于旋转或宽度/高度平移
)

from keras.preprocessing import image

fnames=[os.path.join(train_cats_dir,fname) for fname in os.listdir(train_cats_dir)]#获取猫的训练集数据路径
img_path=fnames[3]
img=image.load_img(img_path,target_size=(150,150))#读取图像并调整大小
x=image.img_to_array(img)#将其转换为形状 (150, 150, 3) 的 Numpy 数组
x=x.reshape((1,)+x.shape)#将其形状改变为(1, 150, 150, 3)
i=0
fig2=plt.figure()
for batch in datagen.flow(x, batch_size=1):#生成随机变换后的图像批量。4个
    ax=fig2.add_subplot(2,2,i+1)
    imgplot = ax.imshow(image.array_to_img(batch[0]))
    i += 1
    if i % 4 == 0:
        break
plt.show()
'''
#如果你使用这种数据增强来训练一个新网络,那么网络将不会
# 两次看到同样的输入。但网络看到的输入仍然是高度相关的
# ,因为这些输入都来自于少量的原始图像。你无法生成新信
# 息, 而只能混合现有信息。因此,这种方法可能不足以完全
# 消除过拟合。
# 为了进一步降低过拟合,你还需要向模型中添加一个
# Dropout 层,添加到密集连接分类器之前
#--------------------------------------------------------

#利用数据增强生成器训练卷积神经网络
def model_fit2(model,train_dir,validation_dir):
    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),#将所有图像的大小调整为 150×150
        batch_size=32,
        class_mode='binary'#因为使用了 binary_crossentropy,损失,所以需要用二进制标签
    )

    validation_generator=test_datagen.flow_from_directory(
        validation_dir,
        target_size=(150,150),
        batch_size=32,
        class_mode='binary'
    )
    history = model.fit_generator(
        train_generator,
        steps_per_epoch=100,
        epochs=100,
        validation_data=validation_generator,
        validation_steps=50
    )
    model.save('cats_and_dogs_small_2.h5')
    return history

# model=model_build()
# history=model_fit2(model,train_dir,validation_dir)
# acc_loss_plot(history)#得到了82%的精度,并且模型不再过拟合:训练曲线紧紧跟随着验证曲线




 ###未使用数据增强的卷积神经网络的训练和验证集精确度及损失图像

 

使用数据增强及dropout正则化后的图像

猜你喜欢

转载自blog.csdn.net/SunChao3555/article/details/88370052