基于keras的猫狗分类(小型卷积神经网络)

2023-10-27

背景:

        本文主要介绍猫狗分类问题,原型取自2013年的kaggle计算机竞赛,你可以从https://www.kaggle.com/c/dogs_vs_cats/data获取必要的数据集,或者寻找其他的镜像文件。数据集包含25000张猫狗图像,这里我们选取2000张,其中,1000张训练集,500张验证集合500张测试集。

        本文将采用2种方法;

        (1)使用普通的CNN来训练模型;

        (2)使用预训练的VGG16来训练模型。

一、使用普通的CNN来训练模型

代码清单1.1 分配路径

import os, shutil

original_dataset_dir = '原始数据集解压路径'
base_dir = '保存较小数据集路径'
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)

validation_cats_dir = os.path.join(validation_dir, 'cats')
os.mkdir(validation_cats_dir)

test_cats_dir = os.path.join(test_dir, 'cats')
os.mkdir(test_cats_dir)

train_dogs_dir = os.path.join(train_dir, 'dogs')
os.mkdir(train_dogs_dir)

validation_dir = os.path.join(validation_dir, 'dogs')
os.mkdir(validation_dogs_dir)

test_dogs_dir = os.path.join(test_dir, 'dogs')
os.mkdir(test_dogs_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)

fnames = ['cat.{}.jpg'.format(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)

fnames = ['cat.{}.jpg'.format(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)

fnames = ['dog.{}.jpg'.format(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)

fnames = ['dog.{}.jpg'.format(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)

fnames = ['dog.{}.jpg'.format(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)

        需要注意的是,在目录创建完毕后,我们应该注释掉创建的命令,以免下次运行程序时报错。

        此时我们可以用下面的代码来看看每个分组分别包含多少张图像:

print('The number of training cat images is:', len(os.listdir(train_cats_dir)))
下面代码不再赘述

        结果应该是猫和狗训练集 / 验证集 / 测试集各分别有1000 / 500 / 500张图像。

代码清单1.2 构建小型神经网络

from keras import models
from keras import layers
from keras import optimizers

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

        卷积神经网络由Conv2D层和MaxPoolingD层交替堆叠而成,因为这里的问题较为复杂,所以适当增大网络,这样既能增加网络容量,还可以进一步缩小特征图的尺寸,使其在连接Flatten层时尺寸不会太大。本节中设置图形大小为150x150(随意),所以最后图形在Flatten层之前的尺寸为7x7。

        编译网络时,我们使用RMSprop优化器。因为网络最后一层是单一sigmoid单元,所以我们将使用二元交叉熵作为损失函数。基本规则如下表所示:

 

为模型选择正确的最后一层激活和损失函数
问题类型 最后一层激活 损失函数
二分类问题 sigmoid binary_crossentropy
多分类、单标签问题 softmax categorical_crossentropy
多分类、多标签问题 sigmoid binary_crossentropy
回归到任意值 mse
回归到0~1范围内的值 sigmoid mse或binary_crossentropy

       

我们来看看特征图的维度如何随着每层变化:

model.summary()
_________________________________________________________________
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         
_________________________________________________________________
conv2d_5 (Conv2D)            (None, 5, 5, 128)         147584    
_________________________________________________________________
max_pooling2d_5 (MaxPooling2 (None, 2, 2, 128)         0         
_________________________________________________________________
flatten_1 (Flatten)          (None, 512)               0         
_________________________________________________________________
dropout_1 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_1 (Dense)              (None, 512)               262656    
_________________________________________________________________
dense_2 (Dense)              (None, 1)                 513       
=================================================================
Total params: 651,585
Trainable params: 651,585
Non-trainable params: 0
_________________________________________________________________

代码清单1.3 数据预处理

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

        (1)读取图像文件

        (2)将JEPG文件解码为RGB像素风格

        (3)将这些像素网格转化为浮点型张量

        (4)将像素值从(0~255)缩小到[0-1]区间(神经网络喜欢处理较小的输入值)

        Keras自带图像处理辅助工具的模块,位于keras.preprocessing.image。它包含ImageDataGenerator类,可以快速创建Python生成器,能够将硬盘上的图像文件自动转换为预处理好的张量批量。

#使用ImageDataGenerator从目录中读取图像
from keras.preprocessing.image import ImageGenerator

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),
                                                    batch_size=20,
                                                    class_mode='binary')

validation_datagen = test_datagen.flow_from_directory(test_dir,
                                                      target_size=(150, 150),
                                                      batch_size=20,
                                                      class_mode='binary')

#看一下生成器的输出
for data_batch, labels_batch in train_generator:
    print('Data batch shape:', data_batch.shape)
    print('Labels batch shape:', labels_batch.shape)
    break

        生成器输出如下:

('data batch shape:', (20, 150, 150, 3))
('labels batch shape:', (20,))

        生成器生成了150x150的RGB图像 [ 形状为(20, 150, 150, 3) ] 与二进制标签 [ 形状为(20,) ] 组成的批量。每个批量包含20个样本(批量大小)。因为生成器会不停地生成批量,因此我们要在某个时刻让它停下来(break)。

#利用批量生成器拟合模型
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')

        此时,我们使用fit_generator的方法来拟合,它在数据生成器上的效果和fit一样。第一个参数应该为Python生成器:train_generator。因为数据不断生成,所以要知道每轮需要从生成器中抽取多少样本,这就是steps_per_epoch的作用:从生成器中抽取steps_per_epoch个批量后(即运行了steps_per_epochs次梯度下降),拟合过程将进入下一个轮次。本例中,每个批量包含20个样本,所以2000个样本需要100个批量。

绘图(代码略)

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

代码清单1.4 数据增强(data augementation)

        过拟合原因是因为学习样本太少,无法训练出能够泛化到新数据的模型。我们已经知道dropout和权重衰减(L2正则化),这里我们采用数据增强,它的原理是将图像随机变换一产生新的数据(对于模型而言)。

        首先我们定义一个包含dropout的新卷积神经网络:

model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(150, 150, 3)))
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D(2, 2))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D(2, 2))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D())
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D(2, 2))
model.add(layers.BatchNormalization())
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(optimizer=optimizersRMSprop(lr=1e-4),
              loss='binary_crossentropy',
              metrics=['acc'])

        其中,我们在每个卷积层之间和Flatten层之前增加了BatchNormalization层,最后的准确率大概会提示%1左右。

        下面,利用数据增强生成器来训练卷积神经网络:

train_datagen = ImageDataGenerator(rescale=1./255,
                                   rotation=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),
                                                    batch_size=32,
                                                    class_mode='binary')

validation_generator = test_datagen.flow_from_directory(test_dir,
                                                        target_size=(150, 150),
                                                        batch_size=32,
                                                        class_mode='binary')

history = model.fit_generator(train_generator,
                              step_per_epoch=63,
                              epochs=100,
                              validation_data=validation_generator,
                              validation_steps=50)

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

        其中,rotation_range是角度值(0~180),表示图像随机旋转的角度;

        width_shift和hieght_shift是图像在水平或垂直方向上平移的范围(相对于总的宽度或高度的比例);

        shear_range是随机错切变换的角度;

        zoom_range是图像随机缩放的范围;

        horizontal_flip是随机讲一半图像水平翻转;

        fill_mode是用于填充新创建像素的方法,这些新像素可能来自于旋转或宽度 / 高度平移。

        随机选择图片,显示数据增强的结果:

from keras.preprocessing import image

fnames = [os.path.join(train_cats_dir, fname) for fname in os.listdir(train_cats_dir)]

#选一张图像
img_path = fnames[10]

#读取图像并调整大小
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()

        得到的结果如下:

 

绘图

        在使用了数据增强、dropout和normalization之后,模型不再过拟合,训练曲线紧紧跟着验证曲线,精度可达%83。如果再增加网络层数,精度将再提高差不多3个百分点。

        目标达成

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

基于keras的猫狗分类(小型卷积神经网络) 的相关文章

  • Python中学习器流水线Pipeline

    sklean提供的pipeline来将多个学习器组成流水线 通常流水线的形式为 将数据标准化的学习器 特征提取的学习器 执行预测的学习器 除了最后一个学习器之外 前面的所有学习器必须提供transform方法 该方法用于数据转化 例如 归一

随机推荐

  • 网络面试-ox09 http是如何维持用户的状态?

    http是无状态 无连接的特性 无连接 使用了keep alive 来处理 无状态 使用了Cookie Session 来维持 解决无状态的方案 2 种 基于Session实现的会话保持 lt 1 gt 在客户端第一次向服务器发送 HTTP
  • vbs整人小病毒2

    海螺 大家好 这是我第二次发文章 点个赞再走吧 Thanks 还有关注 好 直接上代码 Dim AutoRunProgram Set AutoRunProgram WScript CreateObject WScript shell Reg
  • 构建高并发高可用的电商平台架构大纲

    构建高并发高可用的电商平台架构大纲 参考 http blog csdn net yangbutao article details 12242441
  • 人工智能的常用十种算法

    1 决策树 根据一些 feature 进行分类 每个节点提一个问题 通过判断 将数据分为两类 再继续提问 这些问题是根据已有数据学习出来的 再投入新数据的时候 就可以根据这棵树上的问题 将数据划分到合适的叶子上 2 随机森林 视频 在源数据
  • SpringBoot 整合websocket 测试出错

    问题描述 javax websocket server ServerContainer not available 问题起因 一般情况下 使用 SpringBootTest后 Spring将加载所有被管理的bean 基本等同于启动了整个服务
  • 实现Callable接口

  • 【HarmonyOS】【DevEco Studio】ohpm安装失败该如何解决?

    关键词 HarmonyOS DevEco Studio ohpm安装失败 问题背景及解决方案 最近遇到很多DevEco Studio安装ohpm失败的问题 下面给大家介绍几种出现的问题以及解决方案 1 ohpm not set up 报错截
  • 测试用例--测试大纲(提纲)法

    测试大纲 提纲 法 一 应用场合 程序包含多个窗口 每个窗口有多个操作 这些操作之间有一定的关系 为了弄清楚窗口之间不同操作的联系 可以使用测试大纲法 常用于测1 软件的安装 删除程序2 界面之间的跳转关系等 二 测试大纲法的测试步骤 步骤
  • pread,pwrite,read,write区别

    pread pwrite include
  • tc命令——Linux基于IP进行流量限速

    https blog csdn net zhongbeida xue article details 54613750
  • NumPy学习笔记

    NumPy author 段浩 Created on Thu Nov 10 10 21 20 2022 Numpy的介绍 在数据分析和科学计算的领域 Numpy占据十分重要的地位 Numpy使得python具备了操作多维数组的功能 并且效率
  • GET和POST有什么区别?

    简要概括 1 GET提交的数据会放在URL之后 以 分割URL和传输数据 参数之间以 相连 参数值通常以 号赋值 而POST方法是把提交的数据放在HTTP包的Body中 2 GET提交的数据大小有限制 最多只能有1024字节 因为浏览器对U
  • objc.h

    文章目录 objc h 头文件阅读 Class objc object SEL IMP 一些函数定义 方法总结 objc h 头文件阅读 Class Class在Runtime中的定义 An opaque type that represe
  • java程序中的long_java中Long和long的区别

    之前做项目的时候多少接触了这个问题 只是一直没用到这个类型 写的项目都是一些很小的 数据量很少 今天给项目大框架的时候发现 数据库 Oracle 里面Number类型的长度为10以下的时候 如果使用myEclipse自动生成实体类的 这时候
  • CUDA Samples: ripple

    以下CUDA sample是分别用C 和CUDA实现的生成的波纹图像 并对其中使用到的CUDA函数进行了解说 code参考了 GPU高性能编程CUDA实战 一书的第五章 各个文件内容如下 funset cpp include funset
  • 液晶大小

    1 3 5寸液晶 像素点阵 320 240null 2 4 3寸 像素点阵 480 272null
  • 浅谈Spring框架中的IOC(控制反转)和DI(依赖注入)

    浅谈Spring框架中的IOC 控制反转 和DI 依赖注入 控制反转和依赖注入文字表述 1 什么是控制反转 在使用Spring框架之后 对象的实例不再由调用者来创建 而是由Spring容器来创建 Spring容器会控制程序之间的关系 而不是
  • Flink Table API读取Kafka数据,并将结果sink到Kafka中

    最近在小破站上学Flink 跟着做 Flink Table API读取Kafka数据 并将结果sink到Kafka中 这样一个小练习 感觉平平无奇 但是一直运行报错 内心一阵抓狂 WTF 废话少叙 切入正题 使用的是 Flink 1 13
  • 31~50

    Less 31 双引号闭合 直接查询就可 Less 32 在这一关中 单双引号 反斜杠都在addslashes 函数的作用下转义了 所以他们前面都会加上反斜杠 但是我们可以用加上 df的方法来进行注入 查询数据 id 0 df union
  • 基于keras的猫狗分类(小型卷积神经网络)

    背景 本文主要介绍猫狗分类问题 原型取自2013年的kaggle计算机竞赛 你可以从https www kaggle com c dogs vs cats data获取必要的数据集 或者寻找其他的镜像文件 数据集包含25000张猫狗图像 这