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

可以直接在它这个目录下创建一个python文件,就叫猫和狗(cats_and_dogs.py),然后配置好训练集、验证集的目录。

base_dir = '../cats_and_dogs_filtered'
train_dir = os.path.join(base_dir, 'train')
validation_dir = os.path.join(base_dir, 'validation')# 用于训练的猫图目录。
train_cats_dir = os.path.join(train_dir, 'cats')
# 用于训练的狗图目录。
train_dogs_dir = os.path.join(train_dir, 'dogs')# 用于验证的猫图目录。
validation_cats_dir = os.path.join(validation_dir, 'cats')
# 用于验证的狗图目录。
validation_dogs_dir = os.path.join(validation_dir, 'dogs')

现在可以看看猫(cats)和狗(dogs)在训练(train)和验证(validation)目录中的文件命名是怎么约定的。

train_cat_fnames = os.listdir(train_cats_dir)
print('猫的训练、验证目录中的文件命名约定:\n%s' % (train_cat_fnames[:10]))train_dog_fnames = os.listdir(train_dogs_dir)
train_dog_fnames.sort()
print('狗的训练、验证目录中的文件命名约定:\n%s' % (train_dog_fnames[:10]))'''
猫的训练、验证目录中的文件命名约定:
['cat.952.jpg', 'cat.946.jpg', 'cat.6.jpg', 'cat.749.jpg', 'cat.991.jpg', 'cat.985.jpg', 'cat.775.jpg', 'cat.761.jpg', 'cat.588.jpg', 'cat.239.jpg']
狗的训练、验证目录中的文件命名约定:
['dog.0.jpg', 'dog.1.jpg', 'dog.10.jpg', 'dog.100.jpg', 'dog.101.jpg', 'dog.102.jpg', 'dog.103.jpg', 'dog.104.jpg', 'dog.105.jpg', 'dog.106.jpg']
'''

还可以找出训练和验证目录中猫和狗图像的总数。

print('总共用于训练的猫图像:', len(os.listdir(train_cats_dir)))
print('总共用于训练的狗图像:', len(os.listdir(train_dogs_dir)))
print('总共用于验证的猫图像:', len(os.listdir(validation_cats_dir)))
print('总共用于验证的狗图像:', len(os.listdir(validation_dogs_dir)))'''
总共用于训练的猫图像: 1000
总共用于训练的狗图像: 1000
总共用于验证的猫图像: 500
总共用于验证的狗图像: 500
'''

现在知道了,有1,000个猫狗的训练图片,还有500个猫狗的验证图片。现在可以再看几张图片,以便提前地了解一下猫狗数据集里都是些什么图片。

import matplotlib.pyplot as plt
import matplotlib.image as mpimg# 输出图表的参数,将以4x4的配置输出猫狗数据集的部分图片。
nrows = 4
ncols = 4# 迭代图像的当前索引。
pic_index = 0# 设置matplotlib(Python的2D绘图库)图,并将其设置为适合4x4图片大小。
fig = plt.gcf()
fig.set_size_inches(ncols * 4, nrows * 4)pic_index += 8
next_cat_pix = [os.path.join(train_cats_dir, fname)for fname in train_cat_fnames[pic_index-8:pic_index]]
next_dog_pix = [os.path.join(train_dogs_dir, fname)for fname in train_dog_fnames[pic_index-8:pic_index]]for i, img_path in enumerate(next_cat_pix+next_dog_pix):# 设置子图,子图的索引从1开始。sp = plt.subplot(nrows, ncols, i + 1)# 不显示轴(或者说网格线)。sp.axis('Off')img = mpimg.imread(img_path)plt.imshow(img)plt.show()

输出的实际效果如下图所示,每次重新运行都会查看到新的一批图片。

现在准备从头开始构建小型Convnet(开源的卷积神经网络代码),并期望能达到72%的准确率。我们要处理的图片是150x150的彩色图片,编写代码,将堆叠3个卷积层(convolution)、修正线性激活函数(relu)和最大池化层(maxpooling)模块。

我们的卷积层(convolution)过滤器大小为3x3,我们的最大池化层(maxpooling)过滤器为2x2。

  • 第一个卷积层(convolution):设置16个3x3大小的过滤器,即识别16种轮廓及边缘,最后通过修正线性激活函数(relu)形成一个3x3x16的立方体。
  • 第二个卷积层(convolution):设置32个3x3大小的过滤器,即识别32种轮廓及边缘,最后通过修正线性激活函数(relu)形成一个3x3x32的立方体。
  • 第三个卷积层(convolution):设置64个3x3大小的过滤器,即识别64种轮廓及边缘,最后通过修正线性激活函数(relu)形成一个3x3x64的立方体。

每一个卷积层(convolution)成形之后,都会追加一个最大池化层(maxpooling),通过池化操作来降低卷积层输出的特征向量,同时改善结果,使其不易出现过拟合。

注意:这是一种广泛使用的配置,并且已知可以很好地用于图像分类。

此外,由于我们只有相对较少的训练样本(1,000张),仅使用三个卷积模块就可以保持模型较小,从而降低过度拟合(overfitting)的风险。过拟合(overfitting)的意思是:对见过的数据,分类效果极好;而对没见过的数据,表现很糟糕。

from tensorflow import keras# 输入特征图是150x150x3,其中150x150用于图像像素,3用于三个颜色通道(R、G和B)。
img_input = keras.layers.Input(shape=(150, 150, 3))# 第一个卷积层提取3x3x16的特征,使用修正线性激活函数(`relu`),然后是具有2x2大小的最大池化层。
x = keras.layers.Conv2D(16, 3, activation='relu')(img_input)
x = keras.layers.MaxPooling2D(2)(x)# 第二个卷积层提取3x3x32的特征,使用修正线性激活函数(`relu`),然后是具有2x2大小的最大池化层。
x = keras.layers.Conv2D(32, 3, activation='relu')(x)
x = keras.layers.MaxPooling2D(2)(x)# 第三个卷积层提取3x3x64的特征,使用修正线性激活函数(`relu`),然后是具有2x2大小的最大池化层。
x = keras.layers.Conv2D(64, 3, activation='relu')(x)
x = keras.layers.MaxPooling2D(2)(x)

然后是最重要一步,创建两个全连接层(dense),因为我们正面临着两个分类问题,即二元分类问题,我们将以sigmoid函数(一个常用的神经网络激励函数)激活我们的网络,以便我们的网络输出将是0~1之间的单个标量,当前编码的概率图像是一维(而不是零维)。

# 将特征图展平为一维数据(`1-dim`)张量,以便添加全连接层。
x = keras.layers.Flatten()(x)# 使用修正线性激活函数(`relu`)和512个隐藏单元(或神经元)创建全连接层。
x = keras.layers.Dense(512, activation='relu')(x)# 使用单个节点(或神经元)和`sigmoid`激活函数创建输出层。
output = keras.layers.Dense(1, activation='sigmoid')(x)# 创建模型:
# input = 输入特征映射
# output = 输入特征映射 + 堆叠卷积层/最大池化层数 + 全连接层
# 全连接层 + `sigmoid`输出层
model = keras.Model(img_input, output)

最后,可以总结一下整个模型的框架,欣赏一下我们创建的卷积神经网络模型。

model.summary()'''
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
input_1 (InputLayer)         (None, 150, 150, 3)       0
_________________________________________________________________
conv2d (Conv2D)              (None, 148, 148, 16)      448
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 74, 74, 16)        0
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 72, 72, 32)        4640
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 36, 36, 32)        0
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 34, 34, 64)        18496
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 17, 17, 64)        0
_________________________________________________________________
flatten (Flatten)            (None, 18496)             0
_________________________________________________________________
dense (Dense)                (None, 512)               9470464
_________________________________________________________________
dense_1 (Dense)              (None, 1)                 513
=================================================================
Total params: 9,494,561
Trainable params: 9,494,561
Non-trainable params: 0
_________________________________________________________________
'''

上面输出中的“Output Shape”列显示特征图的大小如何在每个连续图层中演变。很明显,由于没有设置填充(padding),卷积层(conv)会将特征映射的大小减少一点,并且每个最大池化层(max_pooling)将特征映射减半。

接下来,我们要配置模型训练的规范,我们将使用交叉熵损失函数(binary_crossentropy)训练我们的模型,因为这是一个二进制分类问题,我们的最终激活函数是一个sigmoid函数。

我们将使用学习率为0.001的rmsprop(一种常用的深度学习优化算法)优化器。同时,在训练期间,我们想监控分类准确性。

注意:在当前情况下,使用rmsprop优化算法优于随机梯度下降(SGD)算法,因为rmsprop会自动调整学习速率。

model.compile(loss='binary_crossentropy',optimizer=keras.optimizers.RMSprop(lr=0.001),metrics=['acc'])

现在,让我们设置数据生成器,它将读取源文件夹中的图片,将它们转换为float32的张量,并将它们附带上标签提供给我们的网络。这样的话,我们将有一个用于训练图像的生成器和一个用于验证图像的生成器,生成器将生产20个尺寸为150x150的图像及其标签(二进制)。

通常进入神经网络的数据应该以某种方式进行标准化,以使其更适合网络处理。(将原始像素直接输入网络的情况并不常见)在这里,我们通过将像素值归一化为0~1范围来预处理图像(最初所有值都在0~255范围内)。

在Keras(用Python编写的高级神经网络API)中,可以使用rescale参数通过keras.preprocessing.image.ImageDataGenerator类完成此操作。此类允许我们通过.flow(data, labels).flow_from_directory(directory)实例化增强图像批次(及其标签)的生成器。然后,这些生成器可以与接受数据生成器作为输入的Keras模型方法一起使用:fit_generatorevaluate_generatorpredict_generator

# 对所有图像按照指定的尺度因子,进行放大或缩小,设置值在0~1之间,通常为1./255。
train_datagen = keras.preprocessing.image.ImageDataGenerator(rescale=1./255)
test_datagen = keras.preprocessing.image.ImageDataGenerator(rescale=1./255)# 使用train_datagen生成器分批轮流训练20张图像。
train_generator = train_datagen.flow_from_directory(# 这是训练图像的源目录。train_dir,# 所有图像将调整为150x150大小。target_size=(150, 150),batch_size=20,# 由于我们使用binary_crossentropy损失算法,我们需要二进制标签。class_mode='binary')# 使用test_datagen生成器批量生成20个流程验证图像。
validation_generator = test_datagen.flow_from_directory(validation_dir,target_size=(150, 150),batch_size=20,class_mode='binary')

现在让我们训练所有的图像,一共2000个,分15个时期,并验证所有验证图像,一个1000个。

# 开始训练。
history = model.fit_generator(train_generator,# 2000张图片 = 批量大小 * 步进。steps_per_epoch=100,epochs=15,validation_data=validation_generator,# 1000张图片 = 批量大小 * 步进。validation_steps=50,verbose=2)

我们可以做一个有意思的事情,就是可视化在训练时如何模型是怎么变化的,让我们从训练集中选择一个随机的猫或狗图像,然后生成一个图形,其中每一行是图层的输出,并且该行中的每个图像都是该输出特征图中的特定滤镜。

import numpy as np
import random# 定义一个新的模型,它将图像作为输入,并在第一个模型之后输出前一个模型中所有图层的中间表示。
successive_outputs = [layer.output for layer in model.layers[1:]]
visualization_model = keras.Model(img_input, successive_outputs)# 从训练集中准备一只猫或狗的随机输入图像。
cat_img_files = [os.path.join(train_cats_dir, f) for f in train_cat_fnames]
dog_img_files = [os.path.join(train_dogs_dir, f) for f in train_dog_fnames]
img_path = random.choice(cat_img_files + dog_img_files)# 这是PIL图像。
img = keras.preprocessing.image.load_img(img_path, target_size=(150, 150))
# Numpy数组形状(150,150,3)。
x = keras.preprocessing.image.img_to_array(img)
# Numpy数组形状(1,150,150,3)。
x = x.reshape((1,) + x.shape)# 重新缩放1/255。
x /= 255# 通过神经网络运行我们的图像,从而获得该图像的所有中间表示。
successive_feature_maps = visualization_model.predict(x)# 这些是图层的名称,因此可以将它们作为我们图表的一部分。
layer_names = [layer.name for layer in model.layers]# 现在展示一下。
for layer_name, feature_map in zip(layer_names, successive_feature_maps):if len(feature_map.shape) == 4:# 只需对conv/maxpool(卷积/最大化池)图层执行此操作,而不是全连接层。# 特征图中的要素数量。n_features = feature_map.shape[-1]# 特征图具有形状(1,size,size,n_features)。size = feature_map.shape[1]# 我们将在此矩阵中平铺图像。display_grid = np.zeros((size, size * n_features))for i in range(n_features):# 对特征该进行后处理,使其在视觉上更加美观。x = feature_map[0, :, :, I]x -= x.mean()x /= x.std()x *= 64x += 128x = np.clip(x, 0, 255).astype('uint8')# 我们将每个过滤器平铺到这个大的水平网格中。display_grid[:, i * size : (i + 1) * size] = x# 显示网格。scale = 20. / n_featuresplt.figure(figsize=(scale * n_features, scale))plt.title(layer_name)plt.grid(False)plt.imshow(display_grid, aspect='auto', cmap='viridis')# 显示图像。plt.show()

当上面的代码运行完之前,会依次显示下面的几张图片。

正如上面所显示的,我们看到了图像从原始像素变为越来越抽象和紧凑的展示,下游的展示开始突出显示神经网络关注的内容,并且它们显示越来越少的特征被“激活”;大多数都设置为零,这被称为“稀疏性”,稀疏性是深度学习的关键特征。

这些展示关于图像的原始像素的信息越来越少,但是关于图像类别的信息越来越精细,我们可以将convnet(卷积神经或一般的深层网络)视为信息蒸馏管道。

接下来我们可以评估一下模型的准确性和损失,绘制训练期间收集的训练/验证准确性和损失。

# 检索每个训练时期的训练和验证数据集的准确度结果列表。
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)
plt.plot(epochs, val_acc)
plt.title('Training and validation accuracy')plt.figure()# 绘制每个时期的训练和验证损失。
plt.plot(epochs, loss)
plt.plot(epochs, val_loss)
plt.title('Training and validation loss')plt.show()

上面的代码运行结束前,会显示下面图片。

正如上面图片所展示的,我们的模型明显过度拟合了,我们的训练准确度(蓝线)接近100%,而我们的验证准确度(橙线)停滞在70%上下。我们的验证损失在仅仅5个时期后达到最小值,这是因为我们的训练样本数量相对较少(2000个)。

因此,过度拟合应成为我们的首要关注点,当太少特征样本的模型,不推广到新数据的模式时,即当模型开始使用不相关的特征进行预测时,就会发生过度拟合。例如,如果我们作为人类,只能看到三个伐木工人的图像,以及三个水手的图像,其中只有一个戴帽子的人是伐木工人,你可能会开始认为戴着帽子是一名伐木工人而不是水手的标志。

然后我们就会做出一个非常糟糕的伐木工人/水手分类器,过度拟合是机器学习中的核心问题:假设我们将模型的参数拟合到给定的数据集,我们如何确保模型适用于以前从未见过的数据?我们如何避免学习特定于训练数据的内容?

最后清理一下数据,运行以下代码以终止内核并释放内存资源。

os.kill(os.getpid(), signal.SIGKILL)

通过以上内容,就建立了一个简单的识别猫狗的模型。

简单的识别猫狗的模型相关推荐

  1. 用Tensorflow实现AlexNet识别猫狗数据集(猫狗大战)【附代码】

    AlexNet识别猫狗数据集 一.下载猫狗数据集 二.AlexNet实现 1.划分训练集和测试集 2.将训练集和测试集图片放缩为224x224 3.AlexNet实现 4.训练过程 5.模型测试 三. ...

  2. python 卷积神经网络猫狗大战_卷积神经网络入门(1) 识别猫狗

    按照我的理解,CNN的核心其实就是卷积核的作用,只要明白了这个问题,其余的就都是数学坑了(当然,相比较而言之后的数学坑更难). 如果学过数字图像处理,对于卷积核的作用应该不陌生,比如你做一个最简单的方 ...

  3. 卷积神经网络入门(1) 识别猫狗

    一下来自知乎 按照我的理解,CNN的核心其实就是卷积核的作用,只要明白了这个问题,其余的就都是数学坑了(当然,相比较而言之后的数学坑更难). 如果学过数字图像处理,对于卷积核的作用应该不陌生,比如你做 ...

  4. tensorflow.js在nodejs训练猫狗分类模型在浏览器上使用

    目录 本人系统环境 注意事项 前言 数据集准备 处理数据集 数据集初步处理 将每一张图片数据转换成张量数据(tensor) 将图片转换成张量数组的代码和运行效果 将图片的标注转换成张量数据(tenso ...

  5. 基于VGGnet识别猫狗数据集(猫狗大战)【附代码】

    文章目录 一.下载kaggle猫狗大战数据集 二.VGGnet实现 1.划分数据集 2.将训练集和测试集图片放缩为224x224 2.实现VGGnet 3.测试模型 三.总结 四.致歉 一.下载kag ...

  6. 通过简单神经网络识别猫图片

    代码位置:https://github.com/lilihongjava/deep_learning/tree/master/%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C% ...

  7. python使用卷积神经网络(CNN,VGGNET)识别猫狗

    权重文件请点赞关注收藏后私信博主要 卷积神经网络的知识此处不再赘述 如不有疑问可以参考这篇文章CNN卷积神经网络 接下来的代码使用VGGNET模型去识别不同图片的特征,我们将使用ImageNet数据集 ...

  8. tensorflow kaggle猫狗大战识别猫狗

    一,Kaggle猫狗大战数据集: 下载地址:https://www.kaggle.com/c/dogs-vs-cats 下载解压后会有两个文件目录,一个测试数据,一个训练数据: 训练数据: 二,训练代 ...

  9. 动物识别 羊群识别 牛识别 马识别 yolo动物识别 鸟类识别 狗识别 猫狗分类

最新文章

  1. 腾讯技术工程 | 腾讯数据平台部总监刘煜宏:这5大产品平台,展示了腾讯大数据的核心能力...
  2. 以太坊是什么 - 以太坊开发入门指南
  3. 高光谱图像pca降维_高光谱图像的数据特性之探讨
  4. SpringBoot和监控管理
  5. 使用组策略限制设备使用
  6. mysql 转int_MySQL索引凭什么能让查询效率提高这么多?
  7. 20190813 On Java8 第一章 对象的概念
  8. c语言 获取硬盘序列号,获取硬盘序列号的C++代码
  9. python Pystaller 将python文件打包成exe
  10. 机械制造作业考研题目答案分享——定位误差及其计算
  11. thinkadmin搜索功能/下拉选项
  12. C#上位机系列(4)—示波器一新窗口的建立
  13. 会议室预约系统 会议预约 会议预约触摸屏 会议预约管理系统
  14. IKAnalyzer 分词工具的使用与问题
  15. 据说只有高端机器才配运行K8S,网友:1G内存的渣渣跑起来了!
  16. 工作五年,我该如何选择?
  17. 阿里巴巴戛纳首秀,带去了天猫全域营销
  18. 基于Qt的车载GPS监控系统(2)需求分析
  19. 汉白玉产地在哪里_汉白玉的产地 汉白玉是产自哪里的
  20. 谷粒学院项目对应知识点

热门文章

  1. 机器学习入门笔记(二):线性模型
  2. Java 1.2.1 读取输入
  3. Python中判断两个字符串的内容是否相同
  4. 给你出道题---最佳组合问题
  5. Java中更换Map中的主键key的名称
  6. 【Spring Cloud】注册中心-Euraka
  7. MySQL备份/还原 Unknown storage engine 'InnoDB'
  8. 父组件向子组件传递数据
  9. Spring Cloud 服务消费者 rest+ribbon (二)
  10. 自己写的Weblogic的poc