24、卷积神经网络(CNN)详解:从基础操作到网络构建

卷积神经网络(CNN)详解:从基础操作到网络构建

1. 池化操作(Pooling)

池化是卷积神经网络(CNN)中的第二个基本操作,相较于卷积操作,它更容易理解。下面以最大池化(Max Pooling)为例进行详细说明。

假设我们有一个 4×4 的矩阵 A:
[
A =
\begin{pmatrix}
1 & 2 & 3 & 4 \
5 & 6 & 7 & 8 \
9 & 10 & 11 & 12 \
13 & 14 & 15 & 16
\end{pmatrix}
]

要进行最大池化,我们需要定义一个大小为 (n_K×n_K) 的区域,这里假设 (n_K = 2)。具体步骤如下:
1. 从矩阵 A 的左上角开始,选取一个 2×2 的区域,即:
[
\begin{pmatrix}
1 & 2 \
5 & 6
\end{pmatrix}
]
对这个区域内的元素应用最大函数,得到最大值,记为 (B_1),即 (B_1 = \max{1, 2, 5, 6} = 6)。
2. 将 2×2 的窗口向右移动两列(步长 (s = 2)),选取新的区域:
[
\begin{pmatrix}
3 & 4 \
7 & 8
\end{pmatrix}
]
同样应用最大函数,得到 (B_2 = \max{3, 4, 7, 8} = 8)。
3. 当无法再向右移动窗口时,将窗口向下移动两行,从矩阵 A 的左侧重新开始选取区域:
[
\begin{pmatrix}
9 & 10 \
13 & 14
\end{pmatrix}
]
得到 (B_3 = \max{9, 10, 13, 14} = 14)。
4. 最后,选取矩阵 A 右下角的 2×2 区域:
[
\begin{pmatrix}
11 & 12 \
15 & 16
\end{pmatrix}
]
得到 (B_4 = \max{11, 12, 15, 16} = 16)。

最终,我们将得到的四个值 (B_1)、(B_2)、(B_3) 和 (B_4) 构建成一个输出张量 B:
[
B =
\begin{pmatrix}
6 & 8 \
14 & 16
\end{pmatrix}
]

最大池化操作的输入是矩阵 A、步长 (s) 和核大小 (n_K),输出是一个新的矩阵 B,其维度计算公式与卷积操作相同:
[
n_B = \frac{n_A - n_K}{s} + 1
]

除了最大池化,还有平均池化(Average Pooling),它返回所选区域内元素的平均值。不过,最大池化是最常用的池化操作,平均池化虽然使用频率较低,但在特定的网络架构中也会出现。

2. 填充操作(Padding)

在处理图像时,有时卷积操作的结果矩阵维度与原始图像不同,这可能不是最优的。此时,可以使用填充操作。填充的基本思想是在最终图像的顶部、底部添加像素行,在右侧和左侧添加像素列,并填充一些值,使结果矩阵的大小与原始矩阵相同。常见的填充策略包括用零填充、用最近像素的值填充等。

假设我们有一个矩阵,经过零填充后可能如下所示:

import numpy as np
ex_out_padded = np.array([[ 0., 0., 0., 0., 0., 0., 0., 0.],
                          [ 0., 0., 0., 30., 30., 0., 0., 0.],
                          [ 0., 0., 0., 30., 30., 0., 0., 0.],
                          [ 0., 0., 0., 30., 30., 0., 0., 0.],
                          [ 0., 0., 0., 30., 30., 0., 0., 0.],
                          [ 0., 0., 0., 30., 30., 0., 0., 0.],
                          [ 0., 0., 0., 30., 30., 0., 0., 0.],
                          [ 0., 0., 0., 0., 0., 0., 0., 0.]])

如果使用填充 (p)(填充行和列的宽度),卷积和池化操作后矩阵 B 的最终维度计算公式为:
[
n_B = \left\lfloor\frac{n_A + 2p - n_K}{s} + 1\right\rfloor
]

需要注意的是,在处理真实图像时,彩色图像通常编码为三个通道(RGB),这意味着卷积和池化操作需要在三个维度(宽度、高度和颜色通道)上执行,会增加算法的复杂度。

3. CNN 的构建模块

卷积和池化操作是构建 CNN 层的基础。典型的 CNN 中通常包含以下几种层:
- 卷积层(Convolutional Layers) :输入一个张量(可能是三维的,因为有三个颜色通道),例如具有特定维度的图像。应用一定数量的核(通常为 10、16 或更多),添加偏置,应用激活函数(如 ReLU)以引入非线性,最终产生输出矩阵 B。
- 池化层(Pooling Layers) :通常用 POOL 和一个数字表示,如 POOL1。输入一个张量,对其应用池化操作后输出另一个张量。池化层没有需要学习的参数,但引入了额外的超参数 (n_K) 和步长 (s)。通常在池化层中不使用填充,因为池化的一个常见目的是减少张量的维度。
- 全连接层(Fully Connected Layers) :与之前章节中介绍的层相同,即神经元与前一层和后一层的所有神经元都相连。

4. 卷积层详解

卷积层的输入是一个张量,例如图像。它会应用多个核(通常用 (n_c) 表示核的数量,也称为通道数),每个核独立地对输入进行卷积操作,然后将结果堆叠起来。最终的输出不再是一个简单的二维矩阵,而是一个三维张量 (\hat{B}),其维度为 (n_B×n_B×n_c)。

卷积层中网络在训练阶段学习的参数是核的元素。假设我们有 (n_c) 个维度为 (n_K×n_K) 的核,那么卷积层中的参数数量为 (n_K^2n_c)。这个数量与输入图像的大小无关,有助于减少过拟合,特别是在处理大尺寸输入图像时。

以下是一个简单的 mermaid 流程图,展示了卷积层的工作流程:

graph TD;
    A[输入图像] --> B[应用多个核];
    B --> C[每个核独立卷积];
    C --> D[结果堆叠];
    D --> E[输出三维张量];
5. 池化层详解

池化层通常跟在卷积层之后,用于减少特征图的维度。常见的池化操作有最大池化和平均池化,其中最大池化更为常用。

池化层的主要作用包括:
- 减少计算量 :通过降低特征图的维度,减少后续层的计算负担。
- 增强特征的鲁棒性 :对局部区域进行池化操作,使得特征对输入的小变化具有更强的鲁棒性。

以下是池化层的操作步骤总结:
| 步骤 | 操作 |
| ---- | ---- |
| 1 | 定义池化窗口大小 (n_K) 和步长 (s) |
| 2 | 从输入特征图的左上角开始,选取 (n_K×n_K) 的区域 |
| 3 | 对选取的区域应用池化函数(如最大函数或平均函数) |
| 4 | 将池化窗口按照步长 (s) 移动,重复步骤 2 和 3,直到覆盖整个输入特征图 |

6. 层的堆叠

在 CNN 中,通常会将卷积层和池化层依次堆叠在一起。卷积层后面通常跟着池化层,有时这两层一起被称为一个层,因为池化层没有可学习的权重,被视为与卷积层相关的简单操作。

以下是一个简单的 mermaid 流程图,展示了卷积层和池化层的堆叠方式:

graph TD;
    A[输入] --> B[卷积层];
    B --> C[池化层];
    C --> D[卷积层];
    D --> E[池化层];
    E --> F[全连接层];
    F --> G[输出层];
7. 构建 CNN 示例

下面我们将构建一个简单的 CNN 网络,以帮助你了解整个过程和代码实现。我们将使用 Zalando 数据集进行训练,网络架构如下:
1. 卷积层 1 :使用 6 个 5×5 的滤波器,步长 (s = 1)。
2. 最大池化层 1 :窗口大小为 2×2,步长 (s = 2)。
3. 对前一层的输出应用 ReLU 激活函数。
4. 卷积层 2 :使用 16 个 5×5 的滤波器,步长 (s = 1)。
5. 最大池化层 2 :窗口大小为 2×2,步长 (s = 2)。
6. 对前一层的输出应用 ReLU 激活函数。
7. 全连接层 1 :包含 128 个神经元,激活函数为 ReLU。
8. 全连接层 2 :包含 10 个神经元,用于对 Zalando 数据集进行分类。
9. Softmax 输出神经元

以下是具体的代码实现:

import pandas as pd
import numpy as np
import tensorflow as tf

# 导入数据集
data_train = pd.read_csv('fashion-mnist_train.csv', header = 0)
data_test = pd.read_csv('fashion-mnist_test.csv', header = 0)

# 准备数据
labels = data_train['label'].values.reshape(1, 60000)
labels_ = np.zeros((60000, 10))
labels_[np.arange(60000), labels] = 1
labels_ = labels_.transpose()
train = data_train.drop('label', axis=1)

labels_dev = data_test['label'].values.reshape(1, 10000)
labels_dev_ = np.zeros((10000, 10))
labels_dev_[np.arange(10000), labels_dev] = 1
test = data_test.drop('label', axis=1)

# 归一化数据
train = np.array(train / 255.0)
dev = np.array(test / 255.0)
labels_ = np.array(labels_)
labels_test_ = np.array(labels_dev_)

# 定义占位符
x = tf.placeholder(tf.float32, shape=[None, 28*28])
x_image = tf.reshape(x, [-1, 28, 28, 1])
y_true = tf.placeholder(tf.float32, shape=[None, 10])
y_true_scalar = tf.argmax(y_true, axis=1)

# 定义构建卷积层的函数
def new_conv_layer(input, num_input_channels, filter_size, num_filters):
    shape = [filter_size, filter_size, num_input_channels, num_filters]
    weights = tf.Variable(tf.truncated_normal(shape, stddev=0.05))
    biases = tf.Variable(tf.constant(0.05, shape=[num_filters]))
    layer = tf.nn.conv2d(input=input, filter=weights, strides=[1, 1, 1, 1], padding='SAME')
    layer += biases
    return layer, weights

# 定义构建池化层的函数
def new_pool_layer(input):
    layer = tf.nn.max_pool(value=input, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
    return layer

# 定义应用 ReLU 激活函数的函数
def new_relu_layer(input_layer):
    layer = tf.nn.relu(input_layer)
    return layer

# 定义构建全连接层的函数
def new_fc_layer(input, num_inputs, num_outputs):
    weights = tf.Variable(tf.truncated_normal([num_inputs, num_outputs], stddev=0.05))
    biases = tf.Variable(tf.constant(0.05, shape=[num_outputs]))
    layer = tf.matmul(input, weights) + biases
    return layer

# 构建网络
layer_conv1, weights_conv1 = new_conv_layer(input=x_image, num_input_channels=1, filter_size=5, num_filters=6)
layer_pool1 = new_pool_layer(layer_conv1)
layer_relu1 = new_relu_layer(layer_pool1)
layer_conv2, weights_conv2 = new_conv_layer(input=layer_relu1, num_input_channels=6, filter_size=5, num_filters=16)
layer_pool2 = new_pool_layer(layer_conv2)
layer_relu2 = new_relu_layer(layer_pool2)

# 扁平化特征图
num_features = layer_relu2.get_shape()[1:4].num_elements()
layer_flat = tf.reshape(layer_relu2, [-1, num_features])

# 构建全连接层
layer_fc1 = new_fc_layer(layer_flat, num_inputs=num_features, num_outputs=128)
layer_relu3 = new_relu_layer(layer_fc1)
layer_fc2 = new_fc_layer(input=layer_relu3, num_inputs=128, num_outputs=10)

# 评估预测结果
y_pred = tf.nn.softmax(layer_fc2)
y_pred_scalar = tf.argmax(y_pred, axis=1)

# 定义损失函数
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=layer_fc2, labels=y_true))

# 定义优化器
optimizer = tf.train.AdamOptimizer(learning_rate=1e-4).minimize(cost)

# 定义评估准确率的操作
correct_prediction = tf.equal(y_pred_scalar, y_true_scalar)
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

# 训练网络
num_epochs = 10
batch_size = 100

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    for epoch in range(num_epochs):
        train_accuracy = 0
        # 这里省略了具体的批次训练代码,可根据实际情况补充

通过以上步骤,我们完成了一个简单 CNN 网络的构建和训练。在实际应用中,还可以进行超参数调优和模型优化,以提高模型的性能。

卷积神经网络(CNN)详解:从基础操作到网络构建

8. 代码解释与关键要点

为了更好地理解上述构建 CNN 网络的代码,下面对关键部分进行详细解释。

8.1 数据准备
data_train = pd.read_csv('fashion-mnist_train.csv', header = 0)
data_test = pd.read_csv('fashion-mnist_test.csv', header = 0)

这两行代码使用 pandas 库读取训练集和测试集数据,文件格式为 CSV。

labels = data_train['label'].values.reshape(1, 60000)
labels_ = np.zeros((60000, 10))
labels_[np.arange(60000), labels] = 1
labels_ = labels_.transpose()
train = data_train.drop('label', axis=1)

此部分代码处理训练集的标签,将标签转换为独热编码(One-Hot Encoding)形式,同时分离出训练数据。

labels_dev = data_test['label'].values.reshape(1, 10000)
labels_dev_ = np.zeros((10000, 10))
labels_dev_[np.arange(10000), labels_dev] = 1
test = data_test.drop('label', axis=1)

类似地,对测试集的标签进行处理并分离出测试数据。

train = np.array(train / 255.0)
dev = np.array(test / 255.0)
labels_ = np.array(labels_)
labels_test_ = np.array(labels_dev_)

将训练数据和测试数据进行归一化处理,将像素值缩放到 0 到 1 之间,有助于模型的训练和收敛。

8.2 占位符定义
x = tf.placeholder(tf.float32, shape=[None, 28*28])
x_image = tf.reshape(x, [-1, 28, 28, 1])
y_true = tf.placeholder(tf.float32, shape=[None, 10])
y_true_scalar = tf.argmax(y_true, axis=1)
  • x 是输入数据的占位符,形状为 [None, 28*28] None 表示可以接受任意数量的样本。
  • x_image 将输入数据从一维向量重新调整为二维图像的形式,形状为 [-1, 28, 28, 1] ,其中 -1 表示自动计算该维度的大小, 1 表示单通道(灰度图像)。
  • y_true 是真实标签的占位符,形状为 [None, 10] ,表示 10 个类别。
  • y_true_scalar 是将真实标签从独热编码转换为标量形式。
8.3 层构建函数
  • 卷积层函数 new_conv_layer
def new_conv_layer(input, num_input_channels, filter_size, num_filters):
    shape = [filter_size, filter_size, num_input_channels, num_filters]
    weights = tf.Variable(tf.truncated_normal(shape, stddev=0.05))
    biases = tf.Variable(tf.constant(0.05, shape=[num_filters]))
    layer = tf.nn.conv2d(input=input, filter=weights, strides=[1, 1, 1, 1], padding='SAME')
    layer += biases
    return layer, weights

该函数用于构建卷积层,首先定义卷积核的形状,然后初始化权重和偏置,使用 tf.nn.conv2d 函数进行卷积操作,最后加上偏置并返回卷积层和权重。

  • 池化层函数 new_pool_layer
def new_pool_layer(input):
    layer = tf.nn.max_pool(value=input, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
    return layer

此函数使用 tf.nn.max_pool 函数进行最大池化操作,池化窗口大小为 2x2 ,步长为 2。

  • ReLU 激活函数层 new_relu_layer
def new_relu_layer(input_layer):
    layer = tf.nn.relu(input_layer)
    return layer

该函数使用 tf.nn.relu 函数对输入层应用 ReLU 激活函数。

  • 全连接层函数 new_fc_layer
def new_fc_layer(input, num_inputs, num_outputs):
    weights = tf.Variable(tf.truncated_normal([num_inputs, num_outputs], stddev=0.05))
    biases = tf.Variable(tf.constant(0.05, shape=[num_outputs]))
    layer = tf.matmul(input, weights) + biases
    return layer

该函数用于构建全连接层,初始化权重和偏置,使用 tf.matmul 函数进行矩阵乘法,然后加上偏置并返回全连接层。

8.4 网络构建与训练
layer_conv1, weights_conv1 = new_conv_layer(input=x_image, num_input_channels=1, filter_size=5, num_filters=6)
layer_pool1 = new_pool_layer(layer_conv1)
layer_relu1 = new_relu_layer(layer_pool1)
layer_conv2, weights_conv2 = new_conv_layer(input=layer_relu1, num_input_channels=6, filter_size=5, num_filters=16)
layer_pool2 = new_pool_layer(layer_conv2)
layer_relu2 = new_relu_layer(layer_pool2)

按照之前定义的网络架构,依次构建卷积层、池化层和 ReLU 激活函数层。

num_features = layer_relu2.get_shape()[1:4].num_elements()
layer_flat = tf.reshape(layer_relu2, [-1, num_features])

将卷积层和池化层的输出进行扁平化处理,以便输入到全连接层。

layer_fc1 = new_fc_layer(layer_flat, num_inputs=num_features, num_outputs=128)
layer_relu3 = new_relu_layer(layer_fc1)
layer_fc2 = new_fc_layer(input=layer_relu3, num_inputs=128, num_outputs=10)

构建全连接层并应用 ReLU 激活函数。

y_pred = tf.nn.softmax(layer_fc2)
y_pred_scalar = tf.argmax(y_pred, axis=1)

使用 tf.nn.softmax 函数将全连接层的输出转换为概率分布,然后将预测结果转换为标量形式。

cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=layer_fc2, labels=y_true))
optimizer = tf.train.AdamOptimizer(learning_rate=1e-4).minimize(cost)

定义损失函数为交叉熵损失,使用 Adam 优化器进行模型训练,学习率设置为 1e-4

correct_prediction = tf.equal(y_pred_scalar, y_true_scalar)
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

定义准确率评估操作,通过比较预测结果和真实标签来计算准确率。

num_epochs = 10
batch_size = 100

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    for epoch in range(num_epochs):
        train_accuracy = 0
        # 这里省略了具体的批次训练代码,可根据实际情况补充

设置训练的轮数和批次大小,初始化所有变量,然后进行训练。在每一轮训练中,可根据实际情况补充具体的批次训练代码。

9. CNN 的优势与应用场景
9.1 优势
  • 利用二维信息 :CNN 能够充分利用输入图像的二维结构信息,而不是将图像展平为一维向量,这使得模型能够更好地捕捉图像中的空间特征。
  • 参数共享 :卷积层中的卷积核在整个图像上共享,大大减少了模型的参数数量,降低了计算复杂度,同时也有助于减少过拟合。
  • 特征提取能力 :通过卷积和池化操作,CNN 能够自动提取图像中的不同层次的特征,从低级的边缘、纹理特征到高级的物体形状、语义特征。
9.2 应用场景
  • 图像分类 :如识别图片中的物体类别,例如判断一张图片是猫还是狗。
  • 目标检测 :在图像中定位和识别多个目标物体,例如在一张街道照片中检测出汽车、行人等。
  • 语义分割 :将图像中的每个像素分配到不同的类别,例如将医学图像中的肿瘤区域分割出来。
10. 总结

本文详细介绍了卷积神经网络(CNN)的基本操作,包括池化、填充,以及 CNN 的构建模块,如卷积层、池化层和全连接层。通过一个具体的代码示例,展示了如何构建和训练一个简单的 CNN 网络。同时,对代码的关键部分进行了详细解释,并阐述了 CNN 的优势和应用场景。

在实际应用中,可以根据具体任务和数据集的特点,调整网络架构和超参数,进一步优化模型的性能。例如,可以尝试不同的卷积核大小、数量,调整池化窗口和步长,或者使用不同的激活函数和优化器。此外,还可以进行数据增强、正则化等操作,以提高模型的泛化能力。

希望本文能够帮助读者更好地理解 CNN 的原理和实现,为进一步深入学习和应用 CNN 奠定基础。

以下是一个简单的 mermaid 流程图,展示 CNN 整体的工作流程:

graph TD;
    A[输入图像] --> B[卷积层];
    B --> C[池化层];
    C --> D[ReLU激活];
    D --> E[卷积层];
    E --> F[池化层];
    F --> G[ReLU激活];
    G --> H[扁平化];
    H --> I[全连接层];
    I --> J[ReLU激活];
    J --> K[全连接层];
    K --> L[Softmax输出];
    L --> M[预测结果];

通过这个流程图,可以更直观地看到 CNN 从输入图像到最终预测结果的整个过程。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值