在深度学习的项目实践中,往往会遇到两个非常难以克服的难题,一是算力,要得到精确的结果,你需要设计几千层,规模庞大的神经网络,然后使用几千个GPU,把神经网络布置到这些GPU上进行运算;第二个难以克服的困难就是数据量,要想得到足够精确的结果,必须依赖于足够量的数据来训练网络模型。本节我们先看看第二个问题如何解决。

我们将开放一个神经网络,用于识别猫狗照片,用于训练模型的照片数量不多,大概4000张左右,猫狗各有2000张,我们将用2000张图片训练模型,1000张用来校验模型,最后1000张对模型进行测试。基于这些有限的数据,我们从零开始构造一个卷积网络模型,在没有使用任何优化手段的情况下,先使得模型的识别准确率达到70%左右,这时如果继续加大模型的训练强度会引起过度拟合,此时我们引入数据扩展法,一种能有效应对视觉识别过程中出现过度拟合的技巧,使用该方法我们可以把网络的准确度提升到80%左右,接着我们再使用其他方法,例如特征抽取,模型预训练,再加上一些具备参数调优,最后让模型的准确率达到97%。

首先我们的训练数据来自于kaggle网站,我已经下载并上传到下面链接的对应课程页面里:
更详细的讲解和代码调试演示过程,请点击链接
把图片下载到本地解压后,我们再使用下面代码,将相关图片拷贝到不同的路径下:

import os, shutil
#数据包被解压的路径
original_dataset_dir = '/Users/chenyi/Documents/人工智能/all/train'
#构造一个专门用于存储图片的路径
base_dir = '/Users/chenyi/Documents/人工智能/all/cats_and_dogs_small'
os.makedirs(base_dir, exist_ok=True)
#构造路径存储训练数据,校验数据以及测试数据
train_dir = os.path.join(base_dir, 'train')
os.makedirs(train_dir, exist_ok = True)
test_dir = os.path.join(base_dir, 'test')
os.makedirs(test_dir, exist_ok = True)
validation_dir = os.path.join(base_dir, 'validation')
os.makedirs(validation_dir, exist_ok = True)#构造专门存储猫图片的路径,用于训练网络
train_cats_dir = os.path.join(train_dir, 'cats')
os.makedirs(train_cats_dir, exist_ok = True)
#构造存储狗图片路径,用于训练网络
train_dogs_dir = os.path.join(train_dir, 'dogs')
os.makedirs(train_dogs_dir, exist_ok = True)#构造存储猫图片的路径,用于校验网络
validation_cats_dir = os.path.join(validation_dir, 'cats')
os.makedirs(validation_cats_dir, exist_ok = True)
#构造存储狗图片的路径,用于校验网络
validation_dogs_dir = os.path.join(validation_dir, 'dogs')
os.makedirs(validation_dogs_dir, exist_ok = True)#构造存储猫图片路径,用于测试网络
test_cats_dir = os.path.join(test_dir, 'cats')
os.makedirs(test_cats_dir, exist_ok = True)
#构造存储狗图片路径,用于测试网络
test_dogs_dir = os.path.join(test_dir, 'dogs')
os.makedirs(test_dogs_dir, exist_ok = True)#把前1000张猫图片复制到训练路径
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张猫图片复制到校验路径
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张猫图片复制到测试路径
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张狗图片复制到训练路径
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张狗图片复制到校验路径
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张狗图片复制到测试路径
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)print('total trainning cat images: ', len(os.listdir(train_cats_dir)))print('total training dog images', len(os.listdir(train_dogs_dir)))print('total validation cat images', len(os.listdir(validation_cats_dir)))print('total validation dogs images', len(os.listdir(validation_dogs_dir)))print('total test cat images:', len(os.listdir(test_cats_dir)))print('total test dog images:', len(os.listdir(test_dogs_dir)))

上面代码把图片分别放置到不同文件夹下,训练用的图片在一个文件夹,校验用的图片在一个文件夹,最后测试用的图片在一个文件夹,上面代码运行后,结果如下:

我们将向上一节例子那样,构造一个Conv2D和MaxPooling2D相互交替的卷积网络。由于我们现在读取的图片比上一节的手写数字图片要到,而且图片的颜色深度比上一节的灰度图要大,因此我们这次构造的网络规模也要相应变大。卷积网络模型的构建代码如下:

from keras import layers
from keras import models
from keras import optimizersmodel = models.Sequential()
#输入图片大小是150*150 3表示图片像素用(R,G,B)表示
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'])model.summary()

上面代码运行后结果如下:

我们看到网络在第六层时,已经有了三百万个参数!这是由于我们反复做卷积,对输入的矩阵做切片造成的。由于网络需要对数据进行二分,所以最后一层只有一个神经元。

接下来我们看看数据预处理,由于机器学习需要读取大量数据,因此keras框架提供了一些辅助机制,让我们能快速将数据以批量的方式读入内存,我们看相应代码:

from keras.preprocessing.image import ImageDataGeneratortrain_datagen = ImageDataGenerator(rescale = 1./ 255) #把像素点的值除以255,使之在0到1之间
test_datagen = ImageDataGenerator(rescale = 1. / 255)#generator 实际上是将数据批量读入内存,使得代码能以for in 的方式去方便的访问
train_generator = train_datagen.flow_from_directory(train_dir, target_size=(150, 150),batch_size=20,class_mode = 'binary')
validation_generator = test_datagen.flow_from_directory(validation_dir, target_size = (150, 150),batch_size = 20,class_mode = 'binary')
#calss_mode 让每张读入的图片对应一个标签值,我们上面一下子读入20张图片,因此还附带着一个数组(20, )
#标签数组的具体值没有设定,由我们后面去使用
for data_batch, labels_batch in train_generator:print('data batch shape: ', data_batch.shape)print('labels batch shape: ', labels_batch.shape)break

Generator 是一种数据批量读取类,而且他们是可循环的,也就是可以对它们使用for in ,在上面我们构造了两个Generator用于读取训练图片和校验图片,同时把图片大小设置为150*150,同时它还能让我们在图片后面附带一个标签值,这就是参数class_mode的作用,由于我们只有猫狗两种图片,因此该标签值不是0就是1,由于train_dir路径下只有两个文件夹,它会为从这两个文件夹中读取的图片分别赋值0和1。

在用for in 遍历generator时,每次遍历都能读取20张图片,而且这个过程是无止境的,当所有图片读取完后,generator又会重头再次读入图片,因此我们必须自己使用break把循环中断掉。上面代码运行后结果如下:

我们看看如何通过generator把数据高效的传递给网络,代码如下:

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

网络模型支持直接将generator作为参数输入,由于我们构造的generator一次批量读入20张图片,总共有2000张图片,所以我们将参数steps_per_epoch = 100,这样每次训练时,模型会用for…in… 在train_generator上循环100次,将所有2000张图片全部读取,后面设置校验数据参数时,逻辑也类似,我们指定循环训练模型30次,上面代码执行后,在普通单CPU机器上运行将会非常缓慢,在我的电脑上,大概执行了十几分钟。

训练结束后,我们模型的训练准确率和校验准确率绘制出来,看看模型对数据的处理情况,代码如下:

model.save('cats_and_dogs_small_1.h5')
import matplotlib.pyplot as pltacc = 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 = 'trainning acc')
plt.plot(epochs, val_acc, 'b', label = 'validation acc')
plt.title('Trainning and validation accuary')
plt.legend()plt.show()
plt.figure()#绘制模型对训练数据和校验数据判断的错误率
plt.plot(epochs, loss, 'bo', label = 'Trainning loss')
plt.plot(epochs, val_loss, 'b', label = 'Validation loss')
plt.title('Trainning and validation loss')
plt.legend()plt.show()

上面代码运行后,绘制的图形如下:

从第一个图可以看出,模型对训练数据的识别率不断提升,但是对校验数据的识别率基本停滞在一个水平,从第二个图看出,模型对训练数据识别的错误率极具下降,但对校验数据的识别错误率反而快速上升了,这表明模型出现了过度拟合的现象,这是在任何机器学习项目中都会遇到的问题。

在计算机视觉识别中,有一种技巧叫数据扩展,专门用于图像识别过程中出现的过度拟合现象。过度拟合出现的一个原因在于数据量太小,我们遇到的情况正是如此。数据扩展本质上是通过利用现有数据创造出新数据,从而增加数据量,我们通过对原有图片随机进行一些修改,在不改变图片本质的情况下,将一张图片修改成一张新的图片,当然这种修改不能将一只猫改成一只狗,只能将一只黑猫变成一只灰猫,我们要保证在训练中,模型不用多次运算同一张图片,在keras框架内,数据扩展很容易实现,例如下面代码:

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表示对图片进行旋转变化, width_shift 和 height_shift对图片的宽和高进行拉伸,shear_range指定裁剪变化的程度,zoom_range是对图片进行放大缩小,horizaontal_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[3]
#加载图片并把它设置为150*150
img = image.load_img(img_path, target_size=(150, 150))
x = image.img_to_array(img)
x = x.reshape((1, ) + x.shape)i = 0
'''
下面的flow函数能自动帮我们进行指定的各种图形变换,例如拉伸,缩放,裁剪等,它是一个死循环,我们需要自己通过break命令才能跳出来
'''
f, ax = plt.subplots(1,4)
for batch in datagen.flow(x, batch_size = 1):imgplot = ax[i].imshow(image.array_to_img(batch[0]))ax[i].axis('off')i += 1if i % 4 == 0:breakplt.show()

我们拿出一张猫的图片,然后使用上面代码对图片进行各种变换,上面代码运行后得到结果如下:

从上面可以看到,我们从一张图片就可以通过变换生成好几张不同图片,我们在把图片传入网络前,先通过上面办法扩张图片,那么网络一下子就能获得成倍增长的训练数据,然而这种做法使得新生成的图片与原有图片存在很强的关联性,因此它对改善过度拟合的作用比较有限,因此我们还得运用前面说过的对网络层输出结果随机清零的办法,我们把这两方法结合起来重新训练网络,代码如下:

model = models.Sequential()
...
model.add(layers.Flatten())
#把上层网络的输出结果中的一半数据随机清零
model.add(layers.Dropout(0.5))
...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) #把像素点的值除以255,使之在0到1之间,增加图片变换
test_datagen = ImageDataGenerator(rescale = 1. / 255)#generator 实际上是将数据批量读入内存,使得代码能以for in 的方式去方便的访问
train_generator = train_datagen.flow_from_directory(train_dir, target_size=(150, 150),batch_size=20,class_mode = 'binary')
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 = 100,epochs = 30, validation_data = validation_generator,validation_steps = 50)
model.save('cats_and_dogs_small_2.h5')

完了,我们再次运行前面的绘图代码,看看网络对训练数据和校验数据的识别度有何变化,运行后得到的绘制结果如下:

经过数据扩展和输出结果随机清零后,对过度拟合的处理非常有效,从上图看,网络对训练数据和校验数据的识别正确率最终完全一致,对两类数据的识别错误率都在有序下降。此时我们达到了80%左右的准确率。如果进一步使用数据正规化以及参数调优等手段,网络的识别率还能进一步提升,但是就如车没油跑不远一样,如果数据不足,无论我们使用什么深度去优化,识别率都很难再有明显的提升,进一步提升识别率的方法,我们将在下一节详细阐述。

更详细的讲解和代码调试演示过程,请点击链接

更多技术信息,包括操作系统,编译器,面试算法,机器学习,人工智能,请关照我的公众号:

深度学习:从零开始构造一个识别猫狗图片的卷积网络相关推荐

  1. Python深度学习(4):猫狗分类

    这个项目使用卷积神经网络,<Python深度学习>中使用了两个方法实现.一个是自己搭建卷积网络,另一个是直接使用VGG16.其中直接使用VGG16又可以分为抽取特征和微调模型两种方法. 1 ...

  2. [深度之眼]TensorFlow2.0项目班-猫狗图片分类

    猫狗数据集官网 猫示例: 狗示例: 训练集:猫狗各11500张图片 验证集:猫狗各1000张图片 难点:图片大小不统一,标签未配对 首先加载需要的包: import tensorflow as tf ...

  3. 【深度学习】总有些哪些大胆又新奇的卷积网络结构设计

    作者&编辑 | 言有三 你大概已经见惯了基本的卷积结构,对残差网络也了如指掌,对分组卷积也烂熟于心,也知道模型压缩的一些技巧,不过今天这里要说的,大部分同学可未必知道噢. 大众的模型结构咱们不 ...

  4. 深度学习 从零开始 —— 神经网络(七又二分之一)卷积优化结构,阿猫阿狗识别,优化步骤结果

    直接四组卷积层和最大池话,一层分类一层结果 加上数据增强之后: 不使用数据增强的快速特征提取 在VG16之后添加自己的分类器(两个Dense,一个dropout正则化) VGG16冻结,使用数据增强的 ...

  5. 基于深度学习的命名实体识别研究综述——论文研读

    基于深度学习的命名实体识别研究综述 摘要: 0引言 1基于深度学习的命名实体识别方法 1.1基于卷积神经网络的命名实体识别方法 1.2基于循环神经网络的命名实体识别方法 1.3基于Transforme ...

  6. opencv交通标志识别_教你从零开始做一个基于深度学习的交通标志识别系统

    教你从零开始做一个基于深度学习的交通标志识别系统 基于Yolo v3的交通标志识别系统及源码 自动驾驶之--交通标志识别 在本文章你可以学习到如何训练自己采集的数据集,生成模型,并用yolo v3算法 ...

  7. 使用深度学习分类猫狗图片

    使用深度学习分类猫狗图片 前言 一.下载数据 二.构建网络 三.数据预处理 四.使用数据增强 总结 前言 本文将介绍如何使用较少的数据从头开始训练一个新的深度学习模型.首先在一个2000个训练样本上训 ...

  8. DeepEye:一个基于深度学习的程序化交易识别与分类方法

    DeepEye:一个基于深度学习的程序化交易识别与分类方法 徐广斌,张伟 上海证券交易所资本市场研究所,上海 200120  上海证券交易所产品创新中心,上海 200120    摘要:基于沪市A股交 ...

  9. 《Python深度学习从零开始学》简介

    #好书推荐##好书奇遇季#深度学习入门书<Python深度学习从零开始学>,京东当当天猫都有发售.从模型和实验入手,快速掌握深度学习技术. 业内大咖强力推荐!!!武汉大学信息管理学院教授 ...

  10. 简单的识别猫狗的模型

    从Google下载猫狗训练集与验证集的zip压缩包,提取到项目目录下.这个文件夹里面包含训练(train)和验证(validation)数据集的子目录,而且每个子目录都包含猫和狗的子目录. 可以直接在 ...

最新文章

  1. Web API 简单示例
  2. python 冒泡排序_Python中的冒泡排序
  3. 启明云端分享 | Sigmastar SSD201\SSD202D核心板在智能料理机方案的应用(4.3寸彩屏应用)
  4. wxWidgets:wxCommandEvent类用法
  5. 阿里云天池 Python训练营Task5:Python训练营测试 学习笔记
  6. java怎么让1的数据2可以拥有,【如何让代码变“高级”(二)】-这样操作值得一波666(Java Stream)(这么有趣)...
  7. 20190904:(leetcode习题)合并两个有序数组
  8. Tornado之异步authenticated
  9. 【转】windows下GSL的配置
  10. [经典力学]牛顿自然哲学的数学原理论文解读
  11. 测试摇一摇次数的软件,iOS通过加速计计算摇一摇次数
  12. 【无标题】互联网广告投放优势和前景
  13. 超链接打开qq对话框
  14. 虚拟机创作ubuntu18的ISO镜像
  15. 用excel/WPS制作酷炫数据可视化大屏(附模板)
  16. 奢华酒店品牌美高梅将入驻上海西岸;ClinChoice昆翎完成1.5亿美元融资 | 美通企业日报...
  17. 将 s1 和 r1 上的启动配置文件上传到服务器进行备份,packettracer综合技能练习261...
  18. cocos creator休闲游戏甜品幻想H5+安卓+IOS三端源码开发脚本为javaScript
  19. 把自己从一个疯狂下载者变成一个真正的学习者
  20. input 金额格式校验

热门文章

  1. 将python文件转换exe可执行程序
  2. 计算机基础进制转换(二进制、八进制、十进制、十六进制)
  3. 【Unity】雷达+Unity +TUIO 介绍一
  4. 常见的搜索引擎蜘蛛有哪些
  5. 增值税下调对财务软件的影响
  6. Office 检测到一个与此文件相关的问题为帮助保护您的计算机,无法打开此文件。
  7. 2023年北京航空航天大学材料与化工(金属方向)考研成功经验
  8. 软件测试(四)——正交实验法、功能图法、其他测试用例设计方法等
  9. mysql 计算信度_解析组合信度CR、AVE值如何计算_组合信度cr
  10. Django菜鸟教程学习记录(一)