【卷积运算】——深度学习.卷积神经网络

发布于:2025-07-22 ⋅ 阅读:(22) ⋅ 点赞:(0)

目录

1  从前馈神经网络说起

2 卷积

2.1 局部连接

2.2 权重共享

2.3 池化(Pooling)

3 二维卷积运算

3.1 权值共享

3.2 卷积之步长操作

3.3 卷积的Padding

3.4 Python计算输出特征图

3.5 卷积核为什么总是选取奇数


卷积神经网络(Convolutional Neural Network,CNN)是受生物学上感受野机制的启发而提出的。目前的卷积神经网络一般是由卷积层、汇聚层和全连接层交叉堆叠而成的前馈神经网络,有三个结构上的特性:局部连接、权重共享以及汇聚。这些特性使得卷积神经网络具有一定程度上的平移、缩放和旋转不变性。

1  从前馈神经网络说起

深度学习通常都会从学习卷积神经网络(Convolutional Neural Network, 简称CNN)开始。很大程度上,是由于CNN的基本组成部分与前馈神经网络有很紧密的关联,甚至可以说,CNN就是一种特殊的前馈神经网络。

这两者的主要区别在于,CNN在前馈神经网络的基础上加入了卷积层和池化层(后面会讲到),以便更好地处理图像等具有空间结构的数据。

现在画图说明一下。对于前馈神经网络,我们可以将简化后的网络结构如下图表示:

可以变换为:

当然,【全连接层-ReLU】可以有多个,此时网络结构可以表示为:

简单地说,CNN就是在此基础上,将全连接层换成卷积层,并在ReLU层之后加入池化层(非必须),那么一个基本的CNN结构就可以表示成这样:

好了,现在现在问题已经简化为理解卷积层和池化层了。

2 卷积

为什么卷积神经网络更适合处理图像呢?

考虑到使用全连接前馈网络来处理图像时,会出现如下问题:

  1. 模型参数过多,容易发生过拟合。 在全连接前馈网络中,隐藏层的每个神经元都要跟该层所有输入的神经元相连接。随着隐藏层神经元数量的增多,参数的规模也会急剧增加,导致整个神经网络的训练效率非常低,也很容易发生过拟合。

  2. 难以提取图像中的局部不变性特征。 自然图像中的物体都具有局部不变性特征,比如尺度缩放、平移、旋转等操作不影响其语义信息。而全连接前馈网络很难提取这些局部不变性特征。

卷积神经网络有三个结构上的特性:局部连接、权重共享和池化(有时候也成为汇聚)。这些特性使得卷积神经网络具有一定程度上的平移、缩放和旋转不变性。和前馈神经网络相比,卷积神经网络的参数也更少。因此,通常会使用卷积神经网络来处理图像信息。

卷积神经网络(CNN)在处理数据时,特别是图像数据时,利用了三个关键特性:局部连接、权重共享和池化(有时称为汇聚)。理解这三个概念有助于深入掌握CNN的工作原理及其优势。

2.1 局部连接

  • 定义:局部连接指的是每个神经元只与输入数据的一个局部区域相连,而不是全连接。例如,在图像处理中,这意味着每个神经元仅关注图像的一小部分(比如一个5x5像素的区域),而不是整个图像。

  • 作用:这种设计模仿了人类视觉系统中的感受野机制,即视觉皮层中的神经元对视野中特定位置的小区域敏感。局部连接极大地减少了模型参数的数量,从而降低了计算成本,并帮助网络专注于学习局部特征,如边缘或纹理。

2.2 权重共享

  • 定义:权重共享是指同一卷积层中的所有神经元使用相同的权重向量来检测输入数据的不同局部区域。换句话说,卷积核(滤波器)在整个输入上滑动时,其内部的权重保持不变

  • 作用:这一机制不仅进一步减少了模型参数的数量,提高了计算效率,还赋予了模型平移不变性(translation invariance),意味着无论某个特征出现在图像的哪个位置,模型都能识别它。这使得CNN非常适合处理具有平移不变性的任务,如图像分类。

2.3 池化(Pooling)

  • 定义:池化操作通常用于减少特征图的空间尺寸,从而降低计算复杂度并控制过拟合。最常见的形式是最大池化(Max Pooling),它通过取每个固定大小子区域的最大值来缩小特征图的尺寸。

  • 作用:除了减少维度外,池化还能提供某种程度的位置不变性,因为它减少了输出对输入中小变化的敏感性。这意味着即使输入有轻微的变化(如物体位置稍微移动),池化后的特征图也能保持相对稳定。此外,池化还有助于提取主要特征,去除冗余信息

综上所述,局部连接和权重共享共同作用,使得CNN能够高效地学习输入数据(尤其是图像)中的空间层次结构,而池化则有助于简化这些表示,使它们对变换更加鲁棒。这三个特性是CNN能够在众多任务中取得优异性能的关键因素。

卷积是分析数学中的一种重要运算,常用于信号处理或图像处理任务。本节以二维卷积为例来进行实践。

3 二维卷积运算

在机器学习和图像处理领域,卷积的主要功能是在一个图像(或特征图)上滑动一个卷积核,通过卷积操作得到一组新的特征。在计算卷积的过程中,需要进行卷积核的翻转,而这也会带来一些不必要的操作和开销。因此,在具体实现上,一般会以数学中的互相关(Cross-Correlatio)运算来代替卷积。 在神经网络中,卷积运算的主要作用是抽取特征,卷积核是否进行翻转并不会影响其特征抽取的能力。特别是当卷积核是可学习的参数时,卷积和互相关在能力上是等价的。因此,很多时候,为方便起见,会直接用互相关来代替卷积。

用一句话总结: 卷积操作是通过滑动卷积核,在每个局部区域上进行“先乘再和”的运算,最终生成一个特征图,每个输出值是一个标量。

卷积操作和线性代数中的内积(点乘),它们之间有着相似之处。

上面这个数学公式似乎很难理解。下面给出了卷积计算的示例。

实现一个简单的二维卷积算子,代码实现如下:

import torch
import torch.nn as nn

# 定义输入矩阵和卷积核
input_matrix = torch.tensor([
    [1., 2., 3.],
    [4., 5., 6.],
    [7., 8., 9.]
], dtype=torch.float32)  # shape: (3, 3)

kernel = torch.tensor([
    [0., 1.],
    [2., 3.]
], dtype=torch.float32)  # shape: (2, 2)


# 将输入扩展为四维张量(batch_size=1, channels=1, height=3, width=3)
input_tensor = input_matrix.view(1, 1, 3, 3)  # shape: [1, 1, 3, 3]

# 创建卷积层(in_channels=1, out_channels=1, kernel_size=2x2)
conv_layer = nn.Conv2d(
    in_channels=1,
    out_channels=1,
    kernel_size=2,
    bias=False  # 禁用偏置项
)

# 手动设置卷积核权重(权重形状:[out_channels, in_channels, height, width])
conv_layer.weight.data = kernel.view(1, 1, 2, 2)

# 执行卷积操作
output = conv_layer(input_tensor)

# 输出结果(四维张量 -> 二维矩阵)
result = output.squeeze().detach()  # shape: [2, 2]

print("卷积结果:\n", result)

运行结果:

卷积结果:
 tensor([[25., 31.],
        [43., 49.]])

当然你也可以手动给卷积层的bias赋值,代码修改如下:

import torch.nn as nn
import torch

input_matrix = torch.tensor([[1., 2., 3], [4., 5., 6.], [7., 8., 9.]])
kernel = torch.tensor([[0., 1.], [2., 3.]])

# 将输入扩展为四维的张量
input_tensor = input_matrix.view(1, 1, 3, 3)

# 创建卷积层
# conv_layer = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=(2, 2), bias=False)
conv_layer = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=(2, 2), bias=True)
conv_layer.weight.data = kernel.view(1, 1, 2, 2)
#手动给卷积层的bias赋值。
conv_layer.bias.data = torch.tensor([1.])
print(conv_layer.bias)
# 执行卷积操作
# output_tensor = conv_layer(input_tensor)
output_tensor = conv_layer(input_tensor)
output_tensor = output_tensor.squeeze().detach()

print(output_tensor)

运行效果:

Parameter containing:
tensor([1.], requires_grad=True)
tensor([[26., 32.],
        [44., 50.]])

3.1 权值共享

权值共享是卷积神经网络(CNN)中一个非常核心的概念,理解它可以帮助你更好地掌握CNN的精髓。权值共享指的是同一个卷积核(或滤波器)在输入数据不同位置上重复使用,而不仅仅是在一个位置上使用。这个卷积核包含了一组权重,用于从输入数据中提取特征。通过权值共享,网络能够在不同的输入位置之间共享计算和参数,从而大大减少模型参数的数量,提高网络的泛化能力。

权值共享有两个关键好处。首先,它显著减少了模型的参数数量,降低了过拟合的风险。其次,由于所有卷积核都使用相同的权重和偏置,CNN实现了平移不变性。这意味着,即使物体在图像中移动了位置,只要其特征保持不变,网络仍能准确识别它。

权值共享是卷积神经网络的一项重要特性。它使得模型更加简洁,提高了模型的泛化能力。理解权值共享不仅有助于深入理解CNN本身,还有助于更好地利用这个强大的机器学习工具。

举个简单的例子,把图像想象成一个有很多小格子(像素)的棋盘,卷积核就像是一个印章。印章在棋盘上移动,每次印章覆盖到的位置,就对这些位置的数值进行一些计算(通常是乘法和加法),得到一个新的数值,这些新数值组成一个新的矩阵,这个过程就是卷积操作。印章的形状(权值)在整个棋盘(图像)上盖章的时候是不会改变的。无论印章滑动到棋盘的哪个位置,印章的形状都是一样的。这样做的好处是大大减少了需要学习的参数数量。

3.2 卷积之步长操作

卷积的 “步长” 是指卷积核(或称为滤波器)在输入图像(或特征图)上移动时每次移动的步数,并且不能超过卷积核的大小,也就是在图像的空间维度上滑动的单位距离。

举例说明:

假设有一个大小为5×5的输入图像(为了简化问题,不考虑颜色通道等复杂情况),而卷积核的大小是3×3。

  • 如果步长设置为1,那么卷积核就从左上角开始,每次向右移动1个单位,当到达图像边缘后,向下移动1个单位,继续向右扫描。这样在横向和纵向都会有很多重叠的计算区域,最终得到的输出特征图尺寸较大,会得到一个3×3的矩阵,可以捕捉到更密集的空间细节

  • 若步长设置为2,则卷积核在每次移动时会跳过1个单位(因为步长是2),这样输出特征图的尺寸就会减小,会得到一个2×2的矩阵,计算量也会相应减少。较大的步长容易导致特征图中信息的稀疏,但可以有效降低模型的复杂度

3.3 卷积的Padding

卷积Padding的重要作用应该是保持输入和输出图像尺寸保持一致性

举例说明:

  • 假设我们有一张 5×5 的图像(简化为黑白图像,忽略颜色通道)。当我们使用一个 3×3 的卷积核进行卷积操作,并且步长为 1 时,如果没有 padding,输出的特征图尺寸就会变成 3×3。这是因为卷积核只能完全覆盖在原始图像的范围内进行计算。

  • 加齐边缘的作用:如果在原始图像的四周添加一层零填充(padding 的大小设为 1),那么整个图像的尺寸就变成了 7×7(5+2×1)。此时,3×3 的卷积核仍然使用步长 1 进行计算,输出的特征图尺寸就会回到 5×5。这样就能确保输出的特征图在尺寸上与输入图像保持一致,从而便于后续的处理和分析。

卷积之后的特征图大小可以通过一个具体的公式来计算。假设输入图像的尺寸是 W×H(宽度和高度),卷积核的大小是 F×F,步长(stride)为 S,填充(padding)大小为 P,则输出特征图的尺寸 W×H 可以通过以下公式计算:

W_{out}=\frac{W-F+2P}{S}+1\\\\ W_{out}=\frac{H-F+2P}{S}+1

这里需要注意的是:

  • (W) 和 (H) 分别代表输入图像的宽度和高度。

  • (F) 是卷积核的尺寸,通常是一个正方形的滤波器,因此宽高相同。

  • (P) 是在输入图像边缘添加的像素数(即填充)。如果在所有边上都添加了相同的填充,则使用这个值。

  • (S) 是卷积核每次移动的步长。

此外,为了确保计算结果是整数(因为特征图的尺寸必须是整数),上述公式的结果应该向下取整。但在大多数情况下,设计网络时会选择合适的参数使得结果正好是整数。然而,在某些实现中,特别是使用深度学习框架时,可能会自动处理这种情况,使得即使计算结果不是整数也能正常工作,比如通过特定的边界条件处理方式。

3.4 Python计算输出特征图

实例1:

import torch
import torch.nn as nn

# 创建一个大小为 5*5 的单通道图像
input_data = torch.randn(1, 1, 5, 5)  # 一个大小为5x5的单通道图像

# 创建卷积层,输入通道数为 1
# 输出通道数16
# 步长默认是1
# 卷积核大小3*3
# 0个0填充
conv_layer = nn.Conv2d(in_channels=1, out_channels=1, stride=1, kernel_size=3, padding=0)

# 对输入数据进行卷积操作
output_data = conv_layer(input_data)

# 输出结果
print(output_data.shape)

输出:

torch.Size([1, 1, 3, 3])

这个例子里大家可以自行调整out_channels,stride,kernel_size和padding的四个参数来观察输出特征图的变化情况。

实例2: 下面是卷积的Padding实例图的实现代码。

import torch
import torch.nn as nn
import numpy as np

# 创建一个大小为 28*28 的单通道图像
# input_data = torch.randn(1, 1, 5, 5)  # 一个大小为28x28的单通道图像

# 创建一个 NumPy 数组
matrix_np = np.array([[[[0.0, 2.0, 4.0, 1.0, 0.0],
                        [3.0, 1.0, 1.0, 0.0, 1.0],
                        [2.0, 4.0, 1.0, 0.0, 1.0],
                        [2.0, 0.0, 5.0, 2.0, 2.0],
                        [0.0, 1.0, 3.0, 2.0, 1.0]]]])

kernel = torch.tensor([
    [1., 0., -1.],
    [1., 0., -1.],
    [1., 0., -1.]
], dtype=torch.float32)

matrix_np = np.array(matrix_np).astype(np.float32)
# 转换为 PyTorch 张量
input_data = torch.from_numpy(matrix_np)

print(input_data)

# 创建卷积层,输入通道数为 1
# 输出通道数16
# 步长默认是1
# 卷积核大小3*3
# 0个0填充
conv_layer = nn.Conv2d(in_channels=1, out_channels=1, stride=1, kernel_size=3, padding=0)

conv_layer.weight.data = kernel.view(1, 1, 3, 3)
# 对输入数据进行卷积操作
output_data = conv_layer(input_data)

# 输出结果
print(output_data.shape)
print(torch.round(output_data))

运行结果:

tensor([[[[0., 2., 4., 1., 0.],
          [3., 1., 1., 0., 1.],
          [2., 4., 1., 0., 1.],
          [2., 0., 5., 2., 2.],
          [0., 1., 3., 2., 1.]]]])
torch.Size([1, 1, 3, 3])
tensor([[[[-1.,  6.,  4.],
          [-0.,  3.,  3.],
          [-5.,  1.,  5.]]]], grad_fn=<RoundBackward0>)

如果调整padding=1,则输出结果如下:

tensor([[[[0., 2., 4., 1., 0.],
          [3., 1., 1., 0., 1.],
          [2., 4., 1., 0., 1.],
          [2., 0., 5., 2., 2.],
          [0., 1., 3., 2., 1.]]]])
torch.Size([1, 1, 5, 5])
tensor([[[[-3., -2.,  2.,  4.,  1.],
          [-7., -1.,  6.,  4.,  1.],
          [-5., -0.,  3.,  3.,  2.],
          [-5., -5.,  1.,  5.,  4.],
          [-1., -6., -3.,  5.,  4.]]]], grad_fn=<RoundBackward0>)

运行效果与示例图显示效果完全吻合。

3.5 卷积核为什么总是选取奇数

在深度学习中,卷积核的大小一般选择奇数是为了方便处理和避免引入不必要的对称性。当卷积核的大小是奇数时,它具有唯一的一个中心像素,这个中心像素点可以作为滑动的默认参考点,即锚点。这使得在进行卷积操作时,卷积核可以在输入图像的每个像素周围均匀地取样。这样的好处是,在进行卷积操作时,可以保持对称地处理图像的每个位置,从而避免引入额外的偏差和不对称性。相反,如果卷积核的大小是偶数,那么在某些位置上,中心像素会落在两个相邻的像素之间,这可能导致对称性问题。

此外,选择奇数大小的卷积核还有一个重要的优点是,在进行空间卷积时,可以确保卷积核有一个明确的中心像素,这有助于处理图像的边缘和边界像素,避免模糊和信息损失。当然,并不是所有情况下都必须选择奇数大小的卷积核。在某些特定情况下,偶数大小的卷积核也可以使用,并且在某些特定任务中可能表现得更好。但是在大多数情况下,奇数大小的卷积核是一种常见且推荐的选择,因为它可以简化卷积操作,并有助于保持图像处理的对称性和一致性。


网站公告

今日签到

点亮在社区的每一天
去签到