《Python深度学习》第五章-2(Cats_vs_Dogs)读书笔记

5.2 在小型数据集上从头开始训练一个卷积神经网络

当数据不够时,有以下方法进行处理,本节主要是介绍数据增强。

5.2.1 深度学习与小数据问题的相关性

  1. 深度学习的一个基本特性就是能够独立地在训练数据中找到有趣的特征,无须人为的特征工程,而这只在拥有大量训练样本时才能实现。对于输入样本的维度非常高(比如图像)的问题尤其如此。
  2. 但如果模型很小,并做了很好的正则化,同时任务非常简单,那么几百个样本可能就足够了。
  3. 由于卷积神经网络学到的是 局 部 的 、 平 移 不 变 的 特 征 \color{red}局部的、平移不变的特征 ,它对于感知问题可以高效地利用数据。
  4. 深度学习模型本质上具有高度的 可 复 用 性 \color{red}可复用性

  1. 本节将重点讨论猫狗图像分类,数据集中包含 8000 张猫和狗的图像(4000 张猫的图像,4000 张狗的图像)。我们将 4000 张图像用于训练,2000 张用于验证,2000张用于测试。
  2. 大概流程:
    • 首先,在 2000 个训练样本上训练一个简单的小型卷积神经网络,不做任何正则化,为模型目标设定一个基准。
    • 然 后,使用 数 据 增 强 ( d a t a a u g m e n t a t i o n ) \color{red}数据增强(data augmentation) (dataaugmentation),它在计算机视觉领域是一种非常强大的降低过拟合的技术。
    • 最后,用 预 训 练 的 网 络 做 特 征 提 取 \color{red}预训练的网络做特征提取 对 预 训 练 的 网 络 进 行 微 调 \color{red}对预训练的网络进行微调

5.2.2 下载数据

Cat_vs_Dog数据下载:

  • 链接:https://pan.baidu.com/s/1KGfmMjVxgDbcIM_aUvhocw 提取码:ai6u
import os, shutil
# 原始数据集解压目录的路径
original_dataset_dir = 'C:\\Users\\Administrator\\Python_learning\\kaggle_original_data'
# 保存较小数据集的目录
base_dir = 'C:\\Users\\Administrator\\Python_learning\\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)

# 将前 2000 张猫的图像复制到 train_cats_dir
fnames = ['cat.{}.jpg'.format(i) for i in range(2000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(train_cats_dir, fname)
    shutil.copyfile(src, dst)
# 将接下来 1000 张猫的图像复制到 validation_cats_dir
fnames = ['cat.{}.jpg'.format(i) for i in range(2000, 3000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(validation_cats_dir, fname)
    shutil.copyfile(src, dst)
# 将接下来的 1000 张猫的图像复制到 test_cats_dir
fnames = ['cat.{}.jpg'.format(i) for i in range(3000, 4000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(test_cats_dir, fname)
    shutil.copyfile(src, dst)

# 将前 2000 张狗的图像复制到 train_dogs_dir
fnames = ['dog.{}.jpg'.format(i) for i in range(2000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(train_dogs_dir, fname)
    shutil.copyfile(src, dst)
# 将接下来 1000 张狗的图像复制到 validation_dogs_dir
fnames = ['dog.{}.jpg'.format(i) for i in range(2000, 3000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(validation_dogs_dir, fname)
    shutil.copyfile(src, dst)
# 将接下来 1000 张狗的图像复制到 test_dogs_dir
fnames = ['dog.{}.jpg'.format(i) for i in range(3000, 3000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(test_dogs_dir, fname)
    shutil.copyfile(src, dst)

5.2.3 构建网络

  1. 建立网络
    面对的是一个二分类问题,所以网络最后一层是使用 sigmoid 激活的单一单元(大小为1 的 Dense 层)。

    from tensorflow.keras import layers
    from tensorflow.keras import models
    
    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.Dense(512, activation='relu'))
    model.add(layers.Dense(1, activation='sigmoid'))
    
    model.summary()
    

    《Python深度学习》第五章-2(Cats_vs_Dogs)读书笔记_第1张图片

  2. 配置模型用于训练
    因为网络最后一层是单一 sigmoid单元,所以我们将使用 二 元 交 叉 熵 作 \color{red}二元交叉熵作 为损失函数。

    《Python深度学习》第五章-2(Cats_vs_Dogs)读书笔记_第2张图片

    from tensorflow.keras import optimizers
    model.compile(loss='binary_crossentropy',
    			  optimizer=optimizers.RMSprop(lr=1e-4),
    			  metrics=['acc'])
    

5.2.4 数据预处理

  1. 数据预处理基本步骤

    • 读取图像文件。
    • 将 JPEG 文件解码为 RGB 像素网格。
    • 将这些像素网格转换为浮点数张量。
    • 将像素值(0~255 范围内)缩放到 [0, 1] 区间(正如你所知,神经网络喜欢处理较小的输入值)。
    1. Keras 拥有自动完成这些步骤的工具。Keras有一个图像处理辅助工具的模块,位于 keras.preprocessing.image
    2. keras.preprocessing.image它包含ImageDataGenerator 类,可以快速创建 P y t h o n 生 成 器 \color{red}Python 生成器 Python,能够将硬盘上的图像文件自动转换为 预 处 理 好 的 张 量 批 量 \color{red}预处理好的张量批量
    3. P y t h o n 生 成 器 ( P y t h o n g e n e r a t o r ) \color{red}Python 生成器(Python generator) Python(Pythongenerator)是一个类似于迭代器的对象,一个可以和 for ...in 运算符一起使用的对象。生成器是用 yield 运算符来构造的。
    from tensorflow.keras.preprocessing.image import ImageDataGenerator
    # 将所有图像乘以 1/255 缩放
    train_datagen = ImageDataGenerator(rescale=1./255)
    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')
    

    在这里插入图片描述

  2. Python批量生成器

    for data_batch, labels_batch in train_generator:
        print('data batch shape:', data_batch.shape)
        print('labels batch shape:', labels_batch.shape)
        break
    

    在这里插入图片描述

    1. Python生成器 在此处生成了150×150的 RGB 图像[形状为 (20,150, 150, 3) ]与二进制标签[形状为 (20,) ]组成的批量。每个批量中包含 20 个样本(批量大小)
    2. 生 成 器 会 不 停 地 生 成 这 些 批 量 \color{red}生成器会不停地生成这些批量 ,它会不断循环目标文件夹中的图像。因此,需要在某个时刻终止( break )迭代循环。
  3. 利用批量生成器拟合模型

    history = model.fit_generator(
    	train_generator,
    	steps_per_epoch=200, # steps_per_epoch * batch_size = 4000(训练数据为4000)
    	epochs=30,
    	validation_data=validation_generator,
    	validation_steps=50)
    
    1. fit_generator 方法来拟合,它在数据生成器上的效果和 fit相同。
    2. fit_generator参数:
      - 第一个参数是 Python 生成器:可以不停地生成输入和目标组成的批量,比如 train_generator
      - 第二个参数是steps_per_epoch :从生成器中抽取 steps_per_epoch个批量后(即运行了 steps_per_epoch 次梯度下降),拟合过程将进入下一个轮次。本例中,每个批量包含 20 个样本,所以读取完所有 4000 个样本需要 200个批量。
      - 第三个参数是 validation_data:这个参数可以是一个数据生成器,但也可以是 Numpy 数组组成的元
      。 如果向 validation_data 传入一个生成器,那么这个生成器应该能够不停地生成验证数据批量,因此你还需要指定 validation_steps 参数,说明需要从验证生成器中抽取多少个批次用于评估。
  4. 保存模型

    model.save('cats_and_dogs_small_1.h5')
    
  5. 绘制训练过程中的损失曲线和精度曲线

    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']
    
    epochs = range(1, len(acc) + 1)
    
    plt.plot(epochs, acc, 'bo', label='Training acc')
    plt.plot(epochs, val_acc, 'b', label='Validation acc')
    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.title('Training and validation loss')
    plt.legend()
    
    plt.show()
    

    《Python深度学习》第五章-2(Cats_vs_Dogs)读书笔记_第3张图片
    《Python深度学习》第五章-2(Cats_vs_Dogs)读书笔记_第4张图片

    这些图像中都能看出过拟合的特征。训练精度随着时间线性增加,直到接近 100%,而验证精度则停留在 70%~72%。验证损失仅在 5 轮后就达到最小值,然后保持不变,而训练损失则一直线性下降,直到接近于 0。

5.2.5 初识数据增强

  1. What ? 什 么 是 数 据 增 强 \color{red}什么是数据增强 ?(书111页)
    • “图生图”:基于现有训练样本集按照一定规则变换出更
  2. Why? 为 什 么 数 据 增 强 在 训 练 样 本 受 限 下 能 提 高 模 型 泛 化 能 力 \color{red}为什么数据增强在训练样本受限下能提高模型泛化能力 ?
    • 视觉世界的“平移不变性”
    • 核心特征的模式信息不会随着图像的特定变换操作而改变、或者改变非常小
  3. How? 如 何 使 用 数 据 增 强 \color{red}如何使用数据增强 使?
    • 利用多种能够生成可信图像的随机变换
    • 随机变换包括图像的旋转、移动、放大/缩小等变换
  4. 验 证 集 不 适 用 数 据 增 强 。 \color{red}验证集不适用数据增强。

5.2.6 使用数据增强

  1. 数据增强的定义
    数据增强是从现有的训练样本中生成更多的训练数据,其方法是利用多种能够生成可信图像的随机变换来增加
    (augment)样本。

  2. 数据增强的目标 模 型 在 训 练 时 不 会 两 次 查 看 完 全 相 同 的 图 像 。 \color{red}模型在训练时不会两次查看完全相同的图像。 让模型能够观察到数据的更多内容,从而具有更好的泛化能力。

  3. 在 Keras 中,通过对 ImageDataGenerator 读取的图像执行多次随机变换来实现。例如:

    datagen = ImageDataGenerator(
    		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')
    
    • rotation_range角度值(在 0~180 范围内),表示图像随机旋转的角度范围。
    • width_shiftheight_shift 是图像在水平或垂直方向上平移的范围(相对于总宽度或总高度的比例)。
    • shear_range随机错切变换的角度
    • zoom_range图像随机缩放的范围
    • horizontal_flip随机将一半图像水平翻转。如果没有水平不对称的假设(比如真实世界的图像),这种做法是有意义的。
    • fill_mode 是用于填充新创建像素的方法,这些新像素可能来自于旋转或宽度/高度平移。
  4. 显示几个随机增强后的训练图像

    # 图像预处理工具的模块
    from tensorflow.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))
    # 将其转换为形状 (150, 150, 3) 的 Numpy 数组
    x = image.img_to_array(img)
    # 将其形状改变为 (1, 150, 150, 3)
    x = x.reshape((1,) + x.shape)
    #生成随机变换后的图像批量。
    #循环是无限的,因此你需要在某个时刻终止循环
    i = 0
    for batch in datagen.flow(x, batch_size=1):
    	plt.figure(i)
    	imgplot = plt.imshow(image.array_to_img(batch[0]))
    	i += 1
    	if i % 4 == 0:
    		break
    plt.show()
    

    《Python深度学习》第五章-2(Cats_vs_Dogs)读书笔记_第5张图片

  5. 增加过dropout层,降低过拟合
    为了进一步降低过拟合,你还需要向模型中添加一个 Dropout 层,添加到密集连接分类器之前。
    定义一个包含dropout的新卷积神经网络:

    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'))
    
    model.compile(loss='binary_crossentropy',
    			 optimizer=optimizers.RMSprop(lr=1e-4),
    			 metrics=['acc'])
    

    《Python深度学习》第五章-2(Cats_vs_Dogs)读书笔记_第6张图片

  6. 利用数据增强生成器训练卷积神经网络

    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),  # 将所有图像的大小调整为 150×150
    		batch_size=20,
    		# 书上是32,但是32*200(steps_per_epoch)大于了4000,运行会报错,所以改为20.
    		class_mode='binary')  
    # 因为使用了 binary_crossentropy损失,所以需要用二进制标签
    
    validation_generator = test_datagen.flow_from_directory(
    		validation_dir,
    		target_size=(150, 150),
    		batch_size=20, 
    		# 书上是32,但是32*200(steps_per_epoch)大于了4000,运行会报错,所以改为20.
    		class_mode='binary'#查看更改后的图片
    		save_to_dir=r'C:\\Users\\cats_and_dogs_small\\Sample',
        	save_prefix='trans_',
        	save_format='jpg')
    		
    history = model.fit_generator(
    		train_generator,
    		steps_per_epoch=200, # steps_per_epoch * batch_size = 200 * 32 = 4000
    		epochs=100,
    		validation_data=validation_generator,
    		validation_steps=50)
    
  7. 保存模型并画出验证曲线

    model.save('cats_and_dogs_small_2.h5')
    
    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']
    
    epochs = range(1, len(acc) + 1)
    
    plt.plot(epochs, acc, 'bo', label='Training acc')
    plt.plot(epochs, val_acc, 'b', label='Validation acc')
    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.title('Training and validation loss')
    plt.legend()
    
    plt.show()
    

    《Python深度学习》第五章-2(Cats_vs_Dogs)读书笔记_第7张图片
    《Python深度学习》第五章-2(Cats_vs_Dogs)读书笔记_第8张图片

通过进一步使用正则化方法以及调节网络参数以得到更高的精度。但只靠从头开始训练自己的卷积神经网络,再想提高精度就十分困难,因为可用的数据太少。下一步需要使用预训练的模型,这是接下来两节的重点。


完整代码

基础模型

from tensorflow.keras import layers
from tensorflow.keras import models
from tensorflow.keras import optimizers
from tensorflow.keras.preprocessing.image import ImageDataGenerator
import matplotlib.pyplot as plt
import os, shutil

# 原始数据集解压目录的路径
original_dataset_dir = 'C:\\Users\\Administrator\\Python_learning\\kaggle_original_data'
# 保存较小数据集的目录
base_dir = 'C:\\Users\\Administrator\\Python_learning\\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)

# 将前 2000 张猫的图像复制到 train_cats_dir
fnames = ['cat.{}.jpg'.format(i) for i in range(2000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(train_cats_dir, fname)
    shutil.copyfile(src, dst)
# 将接下来 1000 张猫的图像复制到 validation_cats_dir
fnames = ['cat.{}.jpg'.format(i) for i in range(2000, 3000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(validation_cats_dir, fname)
    shutil.copyfile(src, dst)
# 将接下来的 1000 张猫的图像复制到 test_cats_dir
fnames = ['cat.{}.jpg'.format(i) for i in range(3000, 4000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(test_cats_dir, fname)
    shutil.copyfile(src, dst)

# 将前 2000 张狗的图像复制到 train_dogs_dir
fnames = ['dog.{}.jpg'.format(i) for i in range(2000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(train_dogs_dir, fname)
    shutil.copyfile(src, dst)
# 将接下来 1000 张狗的图像复制到 validation_dogs_dir
fnames = ['dog.{}.jpg'.format(i) for i in range(2000, 3000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(validation_dogs_dir, fname)
    shutil.copyfile(src, dst)
# 将接下来 1000 张狗的图像复制到 test_dogs_dir
fnames = ['dog.{}.jpg'.format(i) for i in range(3000, 3000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(test_dogs_dir, fname)
    shutil.copyfile(src, dst)

# 构建网络
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.Dense(512, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))

# 配置模型

model.compile(loss='binary_crossentropy',
			  optimizer=optimizers.RMSprop(lr=1e-4),
			  metrics=['acc'])

# 图像预处理
# 将所有图像乘以 1/255 缩放
train_datagen = ImageDataGenerator(rescale=1./255)
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')

# 利用批量生成器拟合模型
history = model.fit_generator(
	train_generator,
	steps_per_epoch=200, # steps_per_epoch * batch_size = 4000
	epochs=30,
	validation_data=validation_generator,
	validation_steps=50)

# 保存模型
model.save('cats_and_dogs_small_1.h5')

# 绘制训练过程中的损失曲线和精度曲线
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)

plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
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.title('Training and validation loss')
plt.legend()

plt.show()

数据增强模型

from tensorflow.keras import layers
from tensorflow.keras import models
from tensorflow.keras import optimizers
from tensorflow.keras.preprocessing.image import ImageDataGenerator
import matplotlib.pyplot as plt
import os, shutil

# 原始数据集解压目录的路径
original_dataset_dir = 'C:\\Users\\Administrator\\Python_learning\\kaggle_original_data'
# 保存较小数据集的目录
base_dir = 'C:\\Users\\Administrator\\Python_learning\\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)

# 将前 2000 张猫的图像复制到 train_cats_dir
fnames = ['cat.{}.jpg'.format(i) for i in range(2000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(train_cats_dir, fname)
    shutil.copyfile(src, dst)
# 将接下来 1000 张猫的图像复制到 validation_cats_dir
fnames = ['cat.{}.jpg'.format(i) for i in range(2000, 3000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(validation_cats_dir, fname)
    shutil.copyfile(src, dst)
# 将接下来的 1000 张猫的图像复制到 test_cats_dir
fnames = ['cat.{}.jpg'.format(i) for i in range(3000, 4000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(test_cats_dir, fname)
    shutil.copyfile(src, dst)

# 将前 2000 张狗的图像复制到 train_dogs_dir
fnames = ['dog.{}.jpg'.format(i) for i in range(2000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(train_dogs_dir, fname)
    shutil.copyfile(src, dst)
# 将接下来 1000 张狗的图像复制到 validation_dogs_dir
fnames = ['dog.{}.jpg'.format(i) for i in range(2000, 3000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(validation_dogs_dir, fname)
    shutil.copyfile(src, dst)
# 将接下来 1000 张狗的图像复制到 test_dogs_dir
fnames = ['dog.{}.jpg'.format(i) for i in range(3000, 3000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(test_dogs_dir, fname)
    shutil.copyfile(src, dst)

# 增加过dropout层,降低过拟合
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'))

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

# 图像预处理
# 训练数据增强
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),  # 将所有图像的大小调整为 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')
		
# 利用批量生成器拟合模型		
history = model.fit_generator(
		train_generator,
		steps_per_epoch=200,	# steps_per_epoch * batch_size = 6400
		epochs=100,
		validation_data=validation_generator,
		validation_steps=50)

# 保存模型
model.save('cats_and_dogs_small_1.h5')

# 绘制训练过程中的损失曲线和精度曲线
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)

plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
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.title('Training and validation loss')
plt.legend()

plt.show()

你可能感兴趣的:(Python学习,python,计算机视觉,tensorflow,神经网络,深度学习)