Tensorflow(二)神经网络之CNN

1,553 阅读9分钟

以下内容是在学习过程中的一些笔记,难免会有错误和纰漏的地方。如果造成任何困扰,很抱歉。

前言

卷积神经网络(Convolutional Neural Networks,CNN)是一类包含卷积计算且具有深度结构的前馈神经网络,强项是计算机视觉

卷积神经网络是一种带有卷积结构的深度神经网络,卷积结构可以减少深层网络占用的内存量,其三个关键的操作,其一是局部感受野,其二是权值共享,其三是 pooling 层,有效的减少了网络的参数个数,缓解了模型的过拟合问题。

其本质是一个多层感知机,成功的原因在于其所采用的局部连接和权值共享的方式:

  • 一方面减少了权值的数量使得网络易于优化
  • 另一方面降低了模型的复杂度,也就是减小了过拟合的风险

一、结构解析

描述:这里可以添加本文要记录的大概内容

CNN的最重要的构建块是卷积层:第一卷积层的神经元不会连接到输入图像中的每个像素,而只与其接受野内的像素相连接。反过来,第二卷积层的每个神经元仅连接到位于第一层中的一个小矩阵内的神经元。

卷积神经网络整体架构分为:

  • 输入层
  • 卷积层
  • 池化层
  • 全连接层
  • 输出层

2.1 卷积运算

卷积层在卷积神经网络整体架构中,做了什么事情?

通过选择区域方框(卷积核尺寸)配合权重参数矩阵,并移动方框(滑动窗口步长)去计算得到3x3的特征值,上述移动一个方块,即步长为1;至于说图像为什么是5x5x3,这个5是像素长宽,3是RGB三通道。

最开始的图只是把单个通道给计算了特征,而最终图像计算一般是上面这种,RGB三个通道计算完后汇聚成一个特征值。不同的权重及其他参数,也会早就多个不同的卷积核。

如果按照上面这种移动的法子,就会造成,有的区域块重叠的次数很多,有的区域块根本不会发生重叠例如边缘区域,导致计算最终结果的时候会不准确,如何解决这个问题?

可以在边缘进行填充,可以填充长度为1或者2都行,根据实际情况来,这样的区域移动就不会发生重叠次数差距过大问题。

而同样的,不只是图像,文本也可以做卷积处理,例如:

我觉得今天天气真好啊
[我觉得][觉得今][今天天][天气真][真好啊]
[我觉得今天][天天气真好][天气真好啊]

同时也可以做边缘填充处理,例如

我觉得今天天气真好啊
000我觉得今天天气真好啊000

总结卷积层涉及的参数:

  • 滑动窗口步长:移动多少格
  • 卷积核尺寸:区域的正方形大小,也可以叫做滤波器(Filters)的大小
  • 边缘填充:最外层加一圈0
  • 卷积核个数:多少个特征图

卷积结果的计算公式为:

W1、H1代表输入的宽和长,W2、H2代表输出特征图的宽和长;F是卷积核长和宽的大小;S是滑动窗口的步长;P是填充边界。

输入数据是 32x32x3 的图像,用 10个 5x5x3 的filter来进行卷积操作指定步长为1,边界填充为2,最终输入的规模为:(32-5+2x2)/1+1=32,最终输出规模是32x32x10,最后的10是因为卷积核数为10,经过卷积操作后也可以保持特征图长度、宽度不变。

2.2 池化运算

并不是所有的特征都是有用的,所以池化层的作用:压缩和保留特征图的重要信息。最常用的是最大池化法(Max Pooling),即取窗格中各个元素的最大值(降采样)。

执行池化运算的数据数不足的时候,为了方便计算,会对周围补充填0,同时也可以看出,池化运算会压缩特征图的尺寸,得到更强的特征。

池化完成后,我们会对特征图进行平坦化,即将全部特征图转换为向量,输入到全连接层的神经网络进行分类。

最后通过反向传播算法更新卷积神经网络的权重,卷积层的权重就是卷积核(滤波器)

注意:特征图容易因为池化运算而压缩变小,解决办法是我们可以通过调整步幅或者补零的方式,让池化运算后的特征图不会压缩的太小。

除了最大池化法以外,还有:

  • 最小池化法
  • 平均池化法

2.3 Dropout层

Dropout层是神经网络的一种优化方法,不增加训练数据的情况下帮助我们对抗过度拟合,例如在神经网络中新增一层50%的Dropout层。

效果是Dropout层会随机选择50%的数据变成0。

目的是为了破坏各层神经元之间的共适性。

2.4 局部连接与权值分享

首先对比全连接层与局部连接的差异

实际上,如果全连接的方式,全连接的神经网络会学习整张图片上所有的像素,学习到的是全域样式;而卷积层使用滤波器的小区域来提取特征,学习到的是局部样式,即使用小区域的局部链接。

另外局部链接还有一种叫重叠。

权重共享是指,每一个局部连接的神经元都是使用相同的权重

上面3个神经元使用相同的权重(不含偏移量),这个权重指的是卷积层的滤波器。

二、案例集

描述:这里可以添加本文要记录的大概内容

3.1 猫狗图像分类识别

数据集在阿里云上找

import os
import datetime
from operator import le

import IPython
import IPython.display
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
import tensorflow as tf

# 数据集路径
from keras.optimizers import Adam
from keras.preprocessing.image import ImageDataGenerator

base_dir = 'E:/27_Python_Protect/数据集/猫狗识别数据集'
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')

# 构建神经网络模型
model = tf.keras.models.Sequential(
    [
        # Conv2D 对图像卷积
        tf.keras.layers.Conv2D(32, (3, 3), activation='relu', input_shape=(64, 64, 3)),
        tf.keras.layers.MaxPooling2D(2, 2),

        tf.keras.layers.Conv2D(64, (3, 3), activation='relu'),
        tf.keras.layers.MaxPooling2D(2, 2),

        tf.keras.layers.Conv2D(128, (3, 3), activation='relu'),
        tf.keras.layers.MaxPooling2D(2, 2),

        # 为全连接层准备-平坦层
        tf.keras.layers.Flatten(),

        tf.keras.layers.Dense(512, activation='relu'),
        # 二分类sigmoid就够了
        tf.keras.layers.Dense(1, activation='sigmoid')
    ]
)

# 查看模型
# model.summary()

model.compile(
    loss='binary_crossentropy',
    # optimizer=Adam(lr=le - 4),
    optimizer='Adam',
    metrics=['acc']
)

# 数据预处理
train_datagen = ImageDataGenerator(rescale=1. / 255)
test_datagen = ImageDataGenerator(rescale=1. / 255)

train_generator = train_datagen.flow_from_directory(
    train_dir,  # 文件夹路径
    target_size=(64, 64),  # 指定resize成的大小
    batch_size=20,
    # 如果one-hot就是categorical,二分类用binary就可以
    class_mode='binary'
)

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

# 开始训练网络模型
history = model.fit_generator(
    train_generator,
    steps_per_epoch=100,
    epochs=20,
    validation_data=validation_generator,
    validation_steps=50,
    verbose=2
)

# 效果展示
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 accuracy')
plt.plot(epochs, val_acc, 'b', label='Validation accuracy')
plt.title('Training and validation accuracy')

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

查看过拟合情况

查看损失情况,效果还行,可以依据实际情况把图像大小调整大一些

3.2 MLP实现手写识别

步骤划分

  1. 数据预处理
    • 载入数据集
    • 特征转换
    • 图像归一化:灰度图像
    • 多元分类问题:One-hot编码
  2. 构建神经网络
    • 神经元及层数确认
    • 编译模型
    • 训练模型
    • 评估模型
  3. 解决过拟合问题
    • 方案一:扩大神经元
    • 方案二:添加隐藏层
    • 方案三:添加Dropout层
  4. 投放使用

先上第一阶段代码

import numpy as np
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense
from keras.utils import to_categorical

# 指定乱数种子
seed = 7
np.random.seed(seed)

#  载入数据集
(X_train, Y_train), (X_test, Y_test) = mnist.load_data()

# 将 28*28 图片转换成 784 的向量
X_train = X_train.reshape(X_train.shape[0], 28 * 28).astype("float32")
X_test = X_test.reshape(X_test.shape[0], 28 * 28).astype("float32")

# 因为是固定范围, 所以执行正规化, 从 0-255 至 0-1, 灰度图片归一化
X_train = X_train / 255
X_test = X_test / 255

# 多元分类问题 One-hot编码
Y_train = to_categorical(Y_train)
Y_test = to_categorical(Y_test)

# 定义模型
model = Sequential()
model.add(Dense(784, input_dim=784, activation="relu"))
model.add(Dense(10, activation="softmax"))

# model.summary()  ##显示模型摘要资讯

# 编译模型
model.compile(loss="categorical_crossentropy", optimizer="adam",
              metrics=["accuracy"])

# 训练模型
history = model.fit(X_train, Y_train, validation_split=0.2,
                    epochs=1000, batch_size=1280, verbose=2)
#  评估模型
print("\nTesting ...")
loss, accuracy = model.evaluate(X_train, Y_train)
print("训练数据集的准确度 = {:.2f}".format(accuracy))
loss, accuracy = model.evaluate(X_test, Y_test)
print("测试数据集的准确度 = {:.2f}".format(accuracy))

添加50% Dropout层解决过拟合问题

model = Sequential()
model.add(Dense(256, input_dim=28*28, activation="relu"))
model.add(Dropout(0.5))
model.add(Dense(256, activation="relu"))
model.add(Dense(10, activation="softmax"))  

3.3 CNN实现手写识别

常用卷积层网络说明

卷积层说明
Conv1D创建一维卷积层,可以在时间维度的序列数据执行卷积运算,例如:语义分析
Conv2D创建二维卷积层,可以在空间维度的二维数组做卷积运算,例如:图片分类识别
UpSampling1D创建一维输入的上采样层,可以沿着时间轴来将数据重复指定次数
UpSampling2D创建二维输入的上采样层,可以沿着二维空间来将数据重复指定次数
MaxPooling1D创建序列数据的一维最大池化
MaxPooling2D创建空间数据的二维最大池化
AveragePooling1D创建序列数据的一维平均池化
AveragePooling2D创建空间数据的二维平均池化

数据预处理

import numpy as np
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers import Conv2D
from keras.layers import MaxPooling2D
from keras.layers import Dropout
from keras.utils import to_categorical

# 指定乱数种子
seed = 7
np.random.seed(seed)

# 载入数据集
(X_train, Y_train), (X_test, Y_test) = mnist.load_data()

# 将图片转换成 4D 张量
X_train = X_train.reshape(X_train.shape[0], 28, 28, 1).astype("float32")
X_test = X_test.reshape(X_test.shape[0], 28, 28, 1).astype("float32")

# 因为是固定范围 所以执行正规化 从 0-255 至 0-1
X_train = X_train / 255
X_test = X_test / 255

# One-hot编码
Y_train = to_categorical(Y_train)
Y_test = to_categorical(Y_test)

print("Y_train.shape = ", Y_train.shape)
print("图片是5 所以One-hot编码是 = ", Y_train[0])

重头戏核心:模型定义

# 定义模型
model = Sequential()

# 16为滤波器数
# kernel_size为窗格尺寸的元组 一般是正方形且为奇数
# padding为补零方式 默认参数valid为不补零 same为补零成相同尺寸
# input_shape为输入的形状大小
# strides指定步幅数
model.add(Conv2D(16, kernel_size=(5, 5), padding="same",
                 input_shape=(28, 28, 1), activation="relu", strides=(1, 1)))
model.add(MaxPooling2D(pool_size=(2, 2)))

model.add(Conv2D(32, kernel_size=(5, 5), padding="same",
                 activation="relu"))
model.add(MaxPooling2D(pool_size=(2, 2)))

model.add(Dropout(0.5))
model.add(Flatten())
model.add(Dense(128, activation="relu"))
model.add(Dropout(0.5))
model.add(Dense(10, activation="softmax"))

最后则开始编译训练并评估结果

# 编译模型
model.compile(loss="categorical_crossentropy", optimizer="adam",
              metrics=["accuracy"])

# 训练模型
history = model.fit(X_train, Y_train, validation_split=0.2,
                    epochs=10, batch_size=128, verbose=2)

# 评估模型
print("\nTesting ...")
loss, accuracy = model.evaluate(X_train, Y_train)
print("训练数据集的准确度 = {:.2f}".format(accuracy))
loss, accuracy = model.evaluate(X_test, Y_test)
print("测试数据集的准确度 = {:.2f}".format(accuracy))

ov

地址
主页 - Keras 中文文档 (keras-zh.readthedocs.io)
关于TensorFlow | TensorFlow中文官网 (google.cn)