卷积神经网络理论详解: Keras 代码实现

导 读

计算机视觉每天都在快速发展。原因之一就是深度学习。当我们谈论计算机视觉时,我们脑海中会浮现一个术语“卷积神经网络”(简称CNN),因为CNN在这里被大量使用。

CNN在计算机视觉中的例子有人脸识别、图像分类等。它与基本的神经网络类似。CNN 还具有可学习的参数,如神经网络,即权重、偏差等。

有需要的朋友关注公众号【小Z的科研日常】,获取更多内容

01、为什么要使用CNN?

1.1 前馈神经网络的问题

假设正在使用 MNIST 数据集,知道 MNIST 中的每个图像都是 28 x 28 x 1(黑白图像仅包含 1 个通道)。输入层的神经元总数为 28 x 28 = 784,这是可以修改的。如果图像的大小是 1000 x 1000,这意味着输入层需要 10⁶ 个神经元,该怎么办?

这就需要大量的神经元才能运作。它在计算上是无效的。因此,卷积神经网络(CNN)应运而生。简单来说,CNN 的作用就是提取图像的特征并将其转换为较低维度,而不丢失其特征。

在下面的示例中,可以看到图像的初始大小为 224 x 224 x 3。如果不进行卷积,则需要 224 x 224 x 3 = 100,输入层中需要 352 个神经元,但在应用卷积后,输入张量维度减少到 1 x 1 x 1000。这意味着前馈神经网络的第一层只需要 1000 个神经元。

简单来说,CNN 的作用就是提取图像的特征并将其转换为较低的维度,而不会丢失其特征。在下面的示例中,可以看到图像的初始大小为 224 x 224 x 3。如果不进行卷积,则需要 224 x 224 x 3 = 100,输入层中需要 352 个神经元,但在应用卷积后,您输入张量维度减少到 1 x 1 x 1000。这意味着前馈神经网络的第一层只需要 1000 个神经元。

02、定义表示

2.1 图像表示

想到图像,很容易理解它具有高度和宽度,因此用二维结构(矩阵)表示其中包含的信息是有意义的,直到记住图像有颜色,并添加有关的信息颜色,我们需要另一个维度,这就是张量变得特别有用的时候。

图像被编码为颜色通道,图像数据被表示为给定点的颜色通道中的每种颜色强度,最常见的是 RGB,即红色、蓝色和绿色。图像中包含的信息是每个通道颜色在图像宽度和高度上的强度,就像下面这样。

因此,红色通道在每个点的宽度和高度的强度可以表示为一个矩阵,蓝色和绿色通道也是如此,所以我们最终得到三个矩阵,当它们组合起来时,它们形成一个张量。

2.2 边缘检测

每个图像都有垂直和水平边缘,它们实际上组合形成图像。卷积运算与某些滤波器一起使用来检测边缘。

假设有尺寸为 6 x 6 的灰度图像和尺寸为 3 x 3 的滤波器(比如说)。当 6 x 6 灰度图像与 3 x 3 滤波器卷积时,我们得到 4 x 4 图像。

首先,3 x 3 滤波器矩阵与灰度图像的第一个 3 x 3 大小相乘,然后我们将一列向右移动到 end ,之后我们移动一行,依此类推。

卷积运算可以通过以下方式可视化。这里我们的图像尺寸是 4 x 4,滤波器是 3 x 3,因此我们在卷积后得到的输出是 2 x 2。

如果我们有 N x N 图像大小和 F x F 滤波器大小,那么卷积后的结果将是

(N x N) * (F x F) = (N-F+1)x(N-F+1)

2.3 步幅和填充

步幅表示卷积中每个步骤移动的步数。默认为 1。

我们可以观察到输出的大小小于输入的大小。为了保持输出的维度与输入相同,我们使用填充。填充是向输入矩阵对称添加零的过程。在下面的示例中,额外的灰色块表示填充。它用于使输出的维度与输入相同。

假设“p”是填充

最初(无填充)

(N×N)*(F×F)=(N-F+1)×(N-F+1)---(1)

应用填充后

如果我们在带有填充的 (N+2p) x (N+2p) 输入矩阵中应用滤波器 F x F,那么我们将得到输出矩阵维度 (N+2p-F+1) x (N+2p-F+1) 。

我们知道,应用填充后,我们将获得与原始输入尺寸 (N x N) 相同的尺寸。因此我们有

(N+2p-F+1)x(N+2p-F+1) 相当于 NxN 
N+2p-F+1 = N ---(2) 
p = (F-1)/2 ---( 3)

等式(3)清楚地表明 Padding 取决于滤波器的尺寸。

03、CNN中的层

CNN 有五个不同的层:

  • 输入层

  • 卷积层(Convo + ReLU)

  • 池化层

  • 全连接(FC)层

  • Softmax/逻辑层

  • 输出层

3.1 输入层

CNN 的输入层应包含图像数据。正如我们之前看到的,图像数据由三维矩阵表示。需要将其重新调整为单列。假设有尺寸为 28 x 28 =784 的图像,需要在输入之前将其转换为 784 x 1。如果有“m”个训练样本,那么输入的维度将为(784,m)。

3.2 卷积层

卷积层有时被称为特征提取器层,因为图像的特征是在该层中提取的。首先,图像的一部分连接到Convo层以执行我们之前看到的卷积运算,并计算感受野(它是输入图像的局部区域,与滤波器的大小相同)和过滤器。运算结果是输出量的单个整数。

然后我们将过滤器滑动到同一输入图像的下一个感受野上,并再次执行相同的操作。我们将一次又一次地重复相同的过程,直到我们浏览完整个图像。输出将作为下一层的输入。

Convo 层还包含 ReLU 激活,使所有负值为零。

3.3 池化层

池化层用于减少卷积后输入图像的空间体积。它用在两个卷积层之间。如果我们在 Convo 层之后应用 FC,而不应用池化或最大池化,那么计算成本会很高,我们不想要它。

因此,最大池化是减少输入图像空间体积的唯一方法。在上面的示例中,我们在步幅为 2 的单个深度切片中应用了最大池化。您可以观察到 4 x 4 维度输入减少到 2 x 2 维度。

池化层没有参数,但有两个超参数——Filter(F)和Stride(S)。一般来说,如果我们输入尺寸 W1 x H1 x D1,那么

W2 = (W1−F)/S+1

H2 = (H1−F)/S+1

D2 = D1

其中W2、H2和D2是输出的宽度、高度和深度。

3.4 全连接层(FC)

全连接层涉及权重、偏差和神经元。它将一层中的神经元连接到另一层中的神经元。它用于通过训练对不同类别的图像进行分类。

3.5 Softmax/逻辑层

Softmax 或 Logistic 层是 CNN 的最后一层。它位于FC层的末端。Logistic 用于二分类,softmax 用于多分类。

3.6 输出层

输出层包含one-hot编码形式的标签。现在对CNN已经有了很好的了解。让我们在 Keras 中实现一个 CNN。

04、Keras代码实现

我们将使用 CIFAR-10 数据集构建 CNN 图像分类器。CIFAR-10数据集有10个不同的标签

  • 飞机

  • 汽车

  • 鹿

  • 青蛙

  • 卡车

它有50,000个训练数据和10,000个测试图像数据。CIFAR-10 中的图像大小为 32 x 32 x 3。它附带 Keras 库。

首先,导入所有必需的模块和库。

import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
from __future__ import print_function
import keras
from keras.models import Sequential
from keras.utils import to_categorical
from keras.layers import Dense, Conv2D, MaxPooling2D, Dropout, Flatten

然后加载数据集并将其分成训练集和测试集。

from keras.datasets import cifar10
(train_images, train_labels), (test_images, test_labels) = cifar10.load_data()

我们将打印 CIFAR-10 中的训练样本形状、测试样本形状和类总数。正如我们之前看到的,有 10 个类。为了举例,我们将从训练集和测试集打印两个示例图像。

print('Training data shape : ', train_images.shape, train_labels.shape)

print('Testing data shape : ', test_images.shape, test_labels.shape)

# 从训练标签中找出唯一的数字
classes = np.unique(train_labels)
nClasses = len(classes)
print('Total number of outputs : ', nClasses)
print('Output classes : ', classes)

plt.figure(figsize=[4,2])

# 显示训练数据中的第一幅图像
plt.subplot(121)
plt.imshow(train_images[0,:,:], cmap='gray')
plt.title("Ground Truth : {}".format(train_labels[0]))

# 显示测试数据中的第一个图像
plt.subplot(122)
plt.imshow(test_images[0,:,:], cmap='gray')
plt.title("Ground Truth : {}".format(test_labels[0]))

输出:

找到输入图像的形状,然后将其重塑为训练和测试集的输入格式。之后将所有数据类型更改为浮点数。

nRows,nCols,nDims = train_images.shape[1:]
train_data = train_images.reshape(train_images.shape[0], nRows, nCols, nDims)
test_data = test_images.reshape(test_images.shape[0], nRows, nCols, nDims)
input_shape = (nRows, nCols, nDims)

train_data = train_data.astype('float32')
test_data = test_data.astype('float32')

通过将训练数据和测试数据除以 255 将数据标准化在 0-1 之间,然后使用to_catagorical()函数将所有标签转换为 one-hot 向量。

train_data /= 255
test_data /= 255

train_labels_one_hot = to_categorical(train_labels)
test_labels_one_hot = to_categorical(test_labels)

使用 one-hot 编码显示类别标签的变化。

print('Original label 0 : ', train_labels[0])
print('After conversion to categorical ( one-hot ) : ', train_labels_one_hot[0])

现在创建我们的模型。我们将添加 Convo 层,然后添加池化层。然后我们将连接 Dense(FC) 层来预测类别。

输入数据馈送到第一个 Convo 层,该 Convo 层的输出充当下一个 Convo 层的输入,依此类推。最后,数据被馈送到 FC 层,该层尝试预测正确的标签。

def createModel():
    model = Sequential()
    # 前两层有 32 个滤波器,窗口大小为 3x3
    model.add(Conv2D(32, (3, 3), padding='same', activation='relu', input_shape=input_shape))
    model.add(Conv2D(32, (3, 3), activation='relu'))
    model.add(MaxPooling2D(pool_size=(2, 2)))
    model.add(Dropout(0.25))

    model.add(Conv2D(64, (3, 3), padding='same', activation='relu'))
    model.add(Conv2D(64, (3, 3), activation='relu'))
    model.add(MaxPooling2D(pool_size=(2, 2)))
    model.add(Dropout(0.25))

    model.add(Conv2D(64, (3, 3), padding='same', activation='relu'))
    model.add(Conv2D(64, (3, 3), activation='relu'))
    model.add(MaxPooling2D(pool_size=(2, 2)))
    model.add(Dropout(0.25))

    model.add(Flatten())
    model.add(Dense(512, activation='relu'))
    model.add(Dropout(0.5))
    model.add(Dense(nClasses, activation='softmax'))
    
    return model

初始化所有参数并使用 rmsprops 优化器编译我们的模型。有很多优化器,例如 adam、SGD、GradientDescent、Adagrad、Adadelta 和 Adamax,可以随意尝试。

model1 = createModel()
batch_size = 256
epochs = 50
model1.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])

model.summary()用于查看模型中每一层的所有参数和形状。您可以观察到总参数为 276、138,总可训练参数为 276、138。不可训练参数为 0。

编译模型后,我们将通过fit()方法训练模型,然后对其进行评估。

history = model1.fit(train_data, train_labels_one_hot, batch_size=batch_size, epochs=epochs, verbose=1, 
                   validation_data=(test_data, test_labels_one_hot))
model1.evaluate(test_data, test_labels_one_hot)

训练后,我们获得了 83.86% 的准确率和 75.48% 的验证准确率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小Z的科研日常

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值