Kaggle dogs vs. cats 数据集及CNN 代码

1. 数据集下载

Dogs vs. Cats | Kaggle

需要科学注册,才能下载。

如果实在下载不了,可以从百度云下载(2023年4月将过期,可私信):

链接: https://pan.baidu.com/s/13YOjcAhGo10vij6-hTe29A

提取码: fn5r

这个数据集包含 25 000 张猫狗图像(每个类别都有 12 500 张),大小为 543MB(压缩后)。

2. 将图像向复制到训练、验证和测试的目录

import os, shutil
# The path to the directory where the original
# dataset was uncompressed(原始数据集解压目录的路径)
original_dataset_dir = 'D:\\Kaggle\\dogs-vs-cats\\train'

# The directory where we will
# store our smaller dataset(保存较小数据集的目录)
base_dir = 'D:\\Kaggle\\dogs-vs-cats-small'
if not os.path.exists(base_dir):
    os.mkdir(base_dir) #os.mkdir() 方法用于以数字权限模式创建目录。如果目录有多级,则创建最后一级,

# Directories for our training, validation and test splits
#(分别对应划分后的训练、 验证和测试的目录)
train_dir = os.path.join(base_dir, 'train')
if not os.path.exists(train_dir):
    os.mkdir(train_dir)
# Directory with our training cat pictures(猫的训练图像目录)
train_cats_dir = os.path.join(train_dir, 'cats')
if not os.path.exists(train_cats_dir):
    os.mkdir(train_cats_dir)

# Directory with our training dog pictures(狗的训练图像目录)
train_dogs_dir = os.path.join(train_dir, 'dogs')
if not os.path.exists(train_dogs_dir):
    os.mkdir(train_dogs_dir)
validation_dir = os.path.join(base_dir, 'validation')
if not os.path.exists(validation_dir):
    os.mkdir(validation_dir)
    # Directory with our validation cat pictures(猫的验证图像目录)
validation_cats_dir = os.path.join(validation_dir, 'cats')
if not os.path.exists(validation_cats_dir):
    os.mkdir(validation_cats_dir)

# Directory with our validation dog pictures(狗的验证图像目录)
validation_dogs_dir = os.path.join(validation_dir, 'dogs')
if not os.path.exists(validation_dogs_dir):
    os.mkdir(validation_dogs_dir)
    
test_dir = os.path.join(base_dir, 'test')
if not os.path.exists(test_dir):
    os.mkdir(test_dir)
    # Directory with our test cat pictures(猫的测试图像目录)
test_cats_dir = os.path.join(test_dir, 'cats')
if not os.path.exists(test_cats_dir):
    os.mkdir(test_cats_dir)

# Directory with our test dog pictures(狗的测试图像目录)
test_dogs_dir = os.path.join(test_dir, 'dogs')
if not os.path.exists(test_dogs_dir):
    os.mkdir(test_dogs_dir)
# The path to the directory where the original
# dataset was uncompressed(原始数据集解压目录的路径)
original_dataset_dir = 'D:\\Kaggle\\dogs-vs-cats\\train'

# The directory where we will
# store our smaller dataset(保存较小数据集的目录)
base_dir = 'D:\\Kaggle\\dogs-vs-cats-small'
if not os.path.exists(base_dir):
    os.mkdir(base_dir) #os.mkdir() 方法用于以数字权限模式创建目录。如果目录有多级,则创建最后一级,

# Directories for our training, validation and test splits
#(分别对应划分后的训练、 验证和测试的目录)
train_dir = os.path.join(base_dir, 'train')
if not os.path.exists(train_dir):
    os.mkdir(train_dir)
# Directory with our training cat pictures(猫的训练图像目录)
train_cats_dir = os.path.join(train_dir, 'cats')
if not os.path.exists(train_cats_dir):
    os.mkdir(train_cats_dir)

# Directory with our training dog pictures(狗的训练图像目录)
train_dogs_dir = os.path.join(train_dir, 'dogs')
if not os.path.exists(train_dogs_dir):
    os.mkdir(train_dogs_dir)
validation_dir = os.path.join(base_dir, 'validation')
if not os.path.exists(validation_dir):
    os.mkdir(validation_dir)
    # Directory with our validation cat pictures(猫的验证图像目录)
validation_cats_dir = os.path.join(validation_dir, 'cats')
if not os.path.exists(validation_cats_dir):
    os.mkdir(validation_cats_dir)

# Directory with our validation dog pictures(狗的验证图像目录)
validation_dogs_dir = os.path.join(validation_dir, 'dogs')
if not os.path.exists(validation_dogs_dir):
    os.mkdir(validation_dogs_dir)
    
test_dir = os.path.join(base_dir, 'test')
if not os.path.exists(test_dir):
    os.mkdir(test_dir)
    # Directory with our test cat pictures(猫的测试图像目录)
test_cats_dir = os.path.join(test_dir, 'cats')
if not os.path.exists(test_cats_dir):
    os.mkdir(test_cats_dir)

# Directory with our test dog pictures(狗的测试图像目录)
test_dogs_dir = os.path.join(test_dir, 'dogs')
if not os.path.exists(test_dogs_dir):
    os.mkdir(test_dogs_dir)

3. 将猫狗分类的小型卷积神经网络实例化 

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() #观察特征图的维度如何随着每层变化

 4. 配置模型用于训练

from tensorflow.keras import optimizers


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

 5.数据预处理

将数据输入神经网络之前,应该将数据格式化为经过预处理的浮点数张量。 现在,数据以 JPEG 文件的形式保存在硬盘中,所以数据预处理步骤大致如下:

(1) 读取图像文件。

(2) 将 JPEG 文件解码为 RGB 像素网格。

(3) 将这些像素网格转换为浮点数张量。

(4) 将像素值(0~255 范围内)缩放到 [0, 1] 区间(正如你所知,神经网络喜欢处理较小的输入值)。

这些步骤可能看起来有点吓人,但幸运的是,Keras 拥有自动完成这些步骤的工具。Keras 有一个图像处理辅助工具的模块,位于keras.preprocessing.image。特别地,它包含 ImageDataGenerator 类,可以快速创建 Python 生成器,能够将硬盘上的图像文件自动转换 为预处理好的张量批量。下面我们将用到这个类。

from tensorflow.keras.preprocessing.image import ImageDataGenerator

# All images will be rescaled by 1./255(将所有图像乘以 1/255 缩放)
train_datagen = ImageDataGenerator(rescale=1./255)
test_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory(
        # This is the target directory(目标目录)
        train_dir,
        # All images will be resized to 150x150(将所有图像的大小调整为 150×150)
        target_size=(150, 150),
        batch_size=20,
        # Since we use binary_crossentropy loss, we need binary labels(因为使用了 binary_crossentropy损失,所以需要用二进制标签)
        class_mode='binary')

validation_generator = test_datagen.flow_from_directory(
        validation_dir,
        target_size=(150, 150),
        batch_size=20,
        class_mode='binary')

 我们来看一下其中一个生成器的输出:它生成了 150×150 的 RGB 图像[形状为 (20,150, 150, 3)]与二进制标签[形状为 (20,)]组成的批量。每个批量中包含20个样本(批量大小)。注意,生成器会不停地生成这些批量,它会不断循环目标文件夹中的图像。因此,你需要在某个时刻终止(break)迭代循环。

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

 6. 利用批量生成器拟合模型

history = model.fit_generator(
      train_generator,
      steps_per_epoch=100,
      epochs=30,
      validation_data=validation_generator,
      validation_steps=50)

7. 保存模型 

#始终在训练完成后保存模型,这是一种良好实践
model.save('cats_and_dogs_small_1.h5')

8. 绘制训练过程中模型在训练数据和验证数据上的损失和精度

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(len(acc))

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()


2021-11-04 22:12 p.m. 新增内容如下: 

9. 使用数据增强

过拟合的原因是学习样本太少,导致无法训练出能够泛化到新数据的模型。如果拥有无限的数据,那么模型能够观察到数据分布的所有内容,这样就永远不会过拟合。数据增强是从现有的训练样本中生成更多的训练数据,其方法是利用多种能够生成可信图像的随机变换来增加(augment)样本。其目标是,模型在训练时不会两次查看完全相同的图像。这让模型能够观察到数据的更多内容,从而具有更好的泛化能力。

在 Keras中,这可以通过对 ImageDataGenerator实例读取的图像执行多次随机变换来实现。我们先来看一个例子。

datagen = ImageDataGenerator(
      rotation_range=40,#角度值(在 0~180 范围内),表示图像随机旋转的角度范围。
      width_shift_range=0.2,
      height_shift_range=0.2,#width_shift 和 height_shift 是图像在水平或垂直方向上平移的范围(相对于总宽度或总高度的比例)。
      shear_range=0.2,#随机错切变换的角度。
      zoom_range=0.2,#图像随机缩放的范围。
      horizontal_flip=True,#随机将一半图像水平翻转。
      fill_mode='nearest')#用于填充新创建像素的方法,这些新像素可能来自于旋转或宽度 / 高度平移。

显示几个随机增强后的训练图像: 

# This is module with image preprocessing utilities(图像预处理工具的模块)
from keras.preprocessing import image
​
fnames = [os.path.join(train_cats_dir, fname) for fname in os.listdir(train_cats_dir)]
​
# We pick one image to "augment"(选择一张图像进行增强)
img_path = fnames[3]
​
# Read the image and resize it(读取图像并调整大小)
img = image.load_img(img_path, target_size=(150, 150))
​
# Convert it to a Numpy array with shape (150, 150, 3)(将其转换为形状 (150, 150, 3) 的 Numpy 数组)
x = image.img_to_array(img)
​
# Reshape it to (1, 150, 150, 3)(将其形状改变为 (1, 150, 150, 3))
x = x.reshape((1,) + x.shape)
​
# The .flow() command below generates batches of randomly transformed images.
# It will loop indefinitely, so we need to `break` the loop at some point!
#生成随机变换后的图像批量。 循环是无限的,因此你需要在某个时刻终止循环
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()

 如果我们使用这种数据增强配置训练一个新的网络,我们的网络将永远不会看到两次相同的输入。然而,它所看到的输入仍然是高度相互关联的,因为它们来自少量的原始图像——我们不能产生新的信息,我们只能混合现有的信息。因此,这可能不足以完全消除过拟合。为了进一步防止过度拟合,我们还将在密集连接分类器前面的模型中添加一个Dropout层:

 10. 定义一个包含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)) #Dropout层
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'])

我们来训练这个使用了数据增强和 dropout 的网络。

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

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,)

# Note that the validation data should not be augmented!(注意,不能增强验证数据)
test_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory( #flow_from_directory(directory): 以文件夹路径为参数,生成经过数据提升/归一化后的数据,在一个无限循环中无限产生batch数据
        # This is the target directory(目标目录)
        train_dir,
        # All images will be resized to 150x150(将所有图像的大小调整为 150×150)
        target_size=(150, 150),
        batch_size=32,
        # Since we use binary_crossentropy loss, we need binary labels
        #(因为使用了 binary_crossentropy损失,所以需要用二进制标签)
        class_mode='binary')

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=63, #这里数字改了,因为有报错
      epochs=100,
      validation_data=validation_generator,
      validation_steps=32) #这里数字改了,因为有报错

12.保存模型

model.save('cats_and_dogs_small_2.h5')

13.绘制训练精度和验证精度,绘制训练损失和验证损失 

acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']

epochs = range(len(acc))

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()

  • 4
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值