前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Deep learning with Python 学习笔记(2)

Deep learning with Python 学习笔记(2)

作者头像
范中豪
发布2019-09-10 18:25:46
6300
发布2019-09-10 18:25:46
举报
文章被收录于专栏:CV学习史CV学习史CV学习史

本节介绍基于Keras的CNN

卷积神经网络接收形状为 (image_height, image_width, image_channels)的输入张量(不包括批量维度),宽度和高度两个维度的尺寸通常会随着网络加深而变小。通道数量由传入 Conv2D 层的第一个参数所控制

用卷积神经网络对 MNIST 数字进行分类Demo

from keras import layers
from keras import models
from keras.datasets import mnist
from keras.utils import to_categorical


def set_model():
    model = models.Sequential()
    model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)))
    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(64, (3, 3), activation='relu'))
    # 需要将 3D 输出展平为 1D,将(3, 3, 64)输出展平为(576, )
    model.add(layers.Flatten())
    model.add(layers.Dense(64, activation='relu'))
    model.add(layers.Dense(10, activation='softmax'))
    # 查看模型各层状态
    model.summary()
    return model


(train_images, train_labels), (test_images, test_labels) = mnist.load_data(path='/home/fan/dataset/mnist.npz')
train_images = train_images.reshape((60000, 28, 28, 1))
train_images = train_images.astype('float32') / 255
test_images = test_images.reshape((10000, 28, 28, 1))
test_images = test_images.astype('float32') / 255
train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels)

model = set_model()
model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])
model.fit(train_images, train_labels, epochs=5, batch_size=64)
test_loss, test_acc = model.evaluate(test_images, test_labels)
print(test_acc)

运行之后,显示正确率为0.9921,而之前使用的密集连接网络的正确率为0.9794,提高了0.0127

密集连接层和卷积层的根本区别在于, Dense 层从输入特征空间中学到的是全局模式,如果模式出现在新的位置,它只能重新学习这个模式,而卷积层学到的是局部模式,可以在任何位置进行匹配 学习局部模式使得CNN具有以下性质:

  1. 卷积神经网络学到的模式具有平移不变性(translation invariant) 卷积神经网络在图像右下角学到某个模式之后,它可以在任何地方识别这个模式,比如左上角 对于密集连接网络来说,如果模式出现在新的位置,它只能重新学习这个模式
  2. 卷积神经网络可以学到模式的空间层次结构(spatial hierarchies of patterns) 第一个卷积层将学习较小的局部模式(比如边缘),第二个卷积层将学习由第一层特征组成的更大的模式,以此类推。这使得卷积神经网络可以有效地学习越来越复杂、越来越抽象的视觉概念(因为视觉世界从根本上具有空间层次结构)

对于包含两个空间轴(高度和宽度)和一个深度轴(也叫通道轴)的 3D 张量,其卷积也叫特征图(feature map)。卷积运算从输入特征图中提取图块,并对所有这些图块应用相同的变换,生成输出特征图(output feature map)。该输出特征图仍是一个 3D 张量,具有宽度和高度,其深度可以任意取值,因为输出深度是层的参数,深度轴的不同通道不再像 RGB 输入那样代表特定颜色,而是代表过滤器(filter)。过滤器对输入数据的某一方面进行编码

上例中,模型定义了

model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)))

该卷积层接收一个大小为 28 * 28 * 1 的特征图,输出一个 26 * 26 * 32 的特征图(26 = (28 -3) / 1 + 1),该26 * 26 * 32是过滤器对输入的响应图(response map),表示这个过滤器模式在输入中不同位置的响应。这也是特征图这一术语的含义: 深度轴的每个维度都是一个特征(或过滤器),而 2D 张量 output[:, :, n]是这个过滤器在输入上的响应的二维空间图(map)

卷积由以下两个关键参数所定义

  1. 从输入中提取的图块尺寸: 这些图块的大小通常是 3×3 或 5×5
  2. 输出特征图的深度:卷积所计算的过滤器的数量 对于 Keras 的 Conv2D 层,这些参数都是向层传入的前几个参数: Conv2D(output_depth, (window_height, window_width))

卷积的工作原理 在 3D 输入特征图上滑动(slide)这些 3×3 或 5×5 的窗口,在每个可能的位置停止并提取周围特征的 3D 图块[形状为 (window_height, window_width, input_depth) ]。然后每个 3D 图块与学到的同一个权重矩阵[叫作卷积核(convolution kernel)]做张量积,转换成形状为 (output_depth,) 的 1D 向量。然后对所有这些向量进行空间重组,使其转换为形状为 (height, width, output_depth) 的 3D 输出特征图。输出特征图中的每个空间位置都对应于输入特征图中的相同位置

卷积计算

可见,当特征图通过卷积核之后,特征图的尺寸变小,具体变化为 \[ outputSize = \frac {(inputSize - ConvSize + 2*padding)}{stride} \] 其中,outputSize 为输出尺寸,inputSize 为输入尺寸,ConvSize为卷积核尺寸,padding 为填充,stride 为步幅

对于 Conv2D 层,可以通过 padding 参数来设置填充,这个参数有两个取值: "valid" 表示不使用填充(只使用有效的窗口位置);"same" 表示“填充后输出的宽度和高度与输入相同”。padding 参数的默认值为 "valid"

最大池化通常使用 2×2 的窗口和步幅 2,其目的是将特征图下采样 2 倍。与此相对的是,卷积通常使用 3×3 窗口和步幅 1 通过池化,我们可以减少参数数量,防止过拟合,同时可以使得之后的卷积相对于之前的获得更大的视野,从而更好地学习特征的空间层级结构

卷积神经网络主要由 Conv2D 层(使用 relu 激活)和MaxPooling2D 层交替堆叠构成,当要处理更大的图像和更复杂的问题时,需要相应的增大网络,即可以再增加一个 Conv2D + MaxPooling2D 的组合。这既可以增大网络容量,也可以进一步减小特征图的尺寸,使其在连接 Flatten 层时尺寸不会太大

在向网络中输入数据时,我们首先需要将数据进行预处理,将其格式化为浮点数张量,JPEG数据处理步骤如下

  1. 读取图像
  2. 将JPEG文件解码为RGB像素网络
  3. 将像素网络转换为浮点数张量
  4. 将像素值缩放到[0, 1]区间

当数据量较大时,我们可以采用生成器的方式将数据依次喂给网络来进行拟合 Keras包含ImageDataGenerator 类,可以快速创建 Python 生成器,能够将硬盘上的图像文件自动转换为预处理好的张量批量 让模型对数据拟合

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

第一个参数为数据生成器,第二个参数表示从生成器中抽取 steps_per_epoch 个批量后(即运行了steps_per_epoch 次梯度下降),拟合过程将进入下一个轮次,第三个参数为验证数据,如果其为一个数据生成器的话,需要指定validation_steps参数,来说明需要从验证生成器中抽取多少个批次用于评估

Keras保存模型

model.save('\*\*\*.h5')

一个使用CNN的猫狗分类Demo 数据集下载 此处为了快速得到结果,使用猫狗各1000个图像训练,各500个验证,各500个测试

from keras import layers
from keras import models
from keras import optimizers
import os
import matplotlib.pyplot as plt


def get_model():
    # 猫狗二分类
    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()
    model.compile(loss='binary_crossentropy', optimizer=optimizers.RMSprop(lr=1e-4), metrics=['acc'])
    return model


from keras.preprocessing.image import ImageDataGenerator


def data_preprocess(train_dir, validation_dir):
    # Python生成器会不断循环目标文件夹中的图像,从而会不停地生成批量
    # 将图像乘1/255缩放
    train_datagen = ImageDataGenerator(rescale=1. / 255)
    test_datagen = ImageDataGenerator(rescale=1. / 255)
    # 将所有文件调整为150 * 150
    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')
    return train_generator, validation_generator


base_dir = '/home/fan/dataset/dogVScat/testDogVSCat'
train_dir = os.path.join(base_dir, 'train')
validation_dir = os.path.join(base_dir, 'validation')
train_generator, validation_generator = data_preprocess(train_dir, validation_dir)
model = get_model()
history = model.fit_generator(train_generator, steps_per_epoch=100, epochs=30, validation_data=validation_generator, validation_steps=50)


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

实验结果 loss: 0.0304 - acc: 0.9925 - val_loss: 1.2209 - val_acc: 0.7010

从如上结果可以看出,我们的网络过拟合了,可以使用数据增强的方式来防止过拟合

数据增强是从现有的训练样本中生成更多的训练数据,其方法是利用多种能够生成可信图像的随机变换来增加(augment)样本。其目标是,模型在训练时不会两次查看完全相同的图像。这让模型能够观察到数据的更多内容,从而具有更好的泛化能力 在 Keras 中,这可以通过对 ImageDataGenerator 实例读取的图像执行多次随机变换来实现 Demo

from keras.preprocessing.image import 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_shift 和 height_shift 是图像在水平或垂直方向上平移的范围(相对于总宽度或总高度的比例) shear_range 是随机错切变换的角度 zoom_range 是图像随机缩放的范围 horizontal_flip 是随机将一半图像水平翻转 fill_mode 是用于填充新创建像素的方法,这些新像素可能来自于旋转或宽度 / 高度平移

使用数据增强的方法增加数据

from keras.preprocessing import image
import matplotlib.pyplot as plt
from keras.preprocessing.image import ImageDataGenerator
import numpy as np


img_path = '/home/fan/dataset/dogVScat/testDogVSCat/train/dogs/dog.77.jpg'
# 加载图片并调整尺寸
img = np.asarray(image.load_img(img_path, target_size=(150, 150)))
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')
plt.imshow(img)
plt.title('original img')
plt.show()


img = img.reshape((1, ) + img.shape)
i = 0
for item in datagen.flow(img, batch_size=1):
    item = image.array_to_img(item[0])
    plt.subplot(2, 2, i+1)
    plt.imshow(item)
    i += 1
    plt.title('generated img ' + str(i))
    if i % 4 == 0:
        break
plt.show()

结果如下

为了继续降低过拟合,可以再向网络中添加dropout。Keras向网络中添加dropout

model.add(layers.Dropout(0.5))

通过使用数据增强,正则化以及调节网络参数可以在一定程度上提高精度,但是因为数据较少,想要进一步提高精度就需要使用预训练的模型

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-11-15 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
图像处理
图像处理基于腾讯云深度学习等人工智能技术,提供综合性的图像优化处理服务,包括图像质量评估、图像清晰度增强、图像智能裁剪等。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档