卷积神经网络CNN

卷积神经网络CNN

🤔 为什么需要CNN?

在CNN出现之前,处理图像等数据通常使用全连接神经网络。这种方式存在两个致命缺陷:

  1. 参数爆炸:一张224x224像素的RGB彩色图像,展平后就有超过15万个输入维度。如果连接到一个仅有1000个神经元的全连接层,仅这一层的参数就高达1.5亿个,导致模型极难训练且容易过拟合。
  2. 丢失空间信息:将图像展平为一维向量,完全破坏了像素之间的空间位置关系和局部相关性,而这对理解图像内容至关重要。

CNN通过其独特的设计思想完美解决了这些问题。

CNN的核心设计思想

  1. 局部连接 (Local Connectivity):网络中的每个神经元只与输入数据的一个局部小区域(称为"感受野")相连。这使得网络能够首先关注并提取图像的局部特征,如边缘、角点等。
  2. 权值共享 (Weight Sharing):同一个"特征探测器"(即卷积核)会滑过整张图像。这意味着,无论一个特征(比如猫的眼睛)出现在图像的哪个位置,都由同一组参数来识别。这极大地减少了模型参数量,并赋予了模型一定的平移不变性。
  3. 层次化特征提取 (Hierarchical Feature Extraction):通过堆叠多个卷积层,网络能够自动学习从简单到复杂的特征。浅层网络学习低级特征(边缘、颜色),深层网络则将这些低级特征组合成高级语义特征(如眼睛、车轮、完整的物体)。

CNN的典型架构

一个经典的CNN可以看作一个特征提取器(卷积层+池化层)和一个分类器(全连接层)的组合。

  1. 卷积层 (Convolutional Layer)
    • 作用:特征提取的核心。
    • 工作原理 :使用一个小的矩阵(称为卷积核滤波器 )在输入图像上滑动。在每个位置,将卷积核与覆盖的图像区域进行点积运算(对应元素相乘再求和),得到一个值。这个过程会生成一个二维的特征图 (Feature Map)。使用多个不同的卷积核,就能提取多种不同的特征。
    • 关键超参数:
      • 卷积核大小 (Kernel Size):通常是 3x3 或 5x5。
      • 步长 (Stride) :卷积核每次滑动的像素数。步长越大,输出特征图的尺寸越小,但同时也会增大每个神经元在输入数据上的感受野,这意味这每个神经元能够捕捉到更大范围的输入信息。
      • 填充 (Padding) :在输入图像边缘补零。常用"Same Padding"来保持输入输出尺寸一致,防止边缘信息丢失
  2. 激活函数 (Activation Function)
    • 作用:引入非线性。卷积运算是线性的,如果没有激活函数,无论堆叠多少层,整个网络仍然等价于一个线性模型,无法学习复杂模式。
    • 常用函数ReLU (Rectified Linear Unit) 是最主流的选择,其公式为 f(x) = max(0, x)。它计算简单,能有效加速训练并缓解梯度消失问题。
  3. 池化层 (Pooling Layer)
    • 作用:也称为下采样层,用于降低特征图的空间尺寸,减少计算量和参数,同时增强模型对微小平移的鲁棒性。
    • 常用方法最大池化 (Max Pooling),例如在一个 2x2 的窗口中取最大值作为输出。这相当于保留了该区域内最显著的特征。
  4. 全连接层 (Fully Connected Layer)
    • 作用:通常位于网络末端。它将前面所有卷积层和池化层提取到的高级特征图"展平"成一维向量,并整合所有信息,进行最终的分类或回归决策。

典型数据流:输入图像 → [卷积层 → 激活函数 → 池化层] (重复N次) → 展平 → 全连接层 → 输出

卷积计算

卷积计算是卷积神经网络(CNN)最核心的运算过程。你可以把它理解为一个"特征提取器",通过一个小的矩阵(卷积核)在大的矩阵(输入图像)上滑动,来探测图像中的特定模式,如边缘、纹理等。

卷积计算的核心步骤

卷积计算的本质是"滑动窗口"下的"乘法-加法"运算。下面通过一个详细的例子来拆解整个过程。

1. 准备工作:定义输入与卷积核

假设我们有一张 5x5 的灰度图像(输入矩阵)和一个 3x3 的卷积核(也叫滤波器)。

  • 输入图像 (Input): 一个 5x5 的矩阵。
  • 卷积核 (Kernel/Filter): 一个 3x3 的矩阵,其内部的数值是模型学习到的权重。
  • 偏置 (Bias): 一个标量值,会加到卷积运算的结果上。
  • 步长 (Stride): 卷积核每次滑动的像素距离,这里我们假设步长为 1。
  • 填充 (Padding): 为了简化,我们先假设不进行填充(即 "Valid" Padding)。
tex 复制代码
输入图像 (5x5)          卷积核 (3x3)
[1, 0, 1, 0, 1]         [1, 0, 1]
[0, 1, 0, 1, 0]         [0, 1, 0]
[1, 0, 1, 0, 1]         [1, 0, 1]
[0, 1, 0, 1, 0]
[1, 0, 1, 0, 1]
2. 单次卷积运算:点积求和

卷积核会从输入图像的左上角开始,覆盖一个 3x3 的区域。

  1. 对齐 (Alignment): 将 3x3 的卷积核覆盖在输入图像左上角的 3x3 区域上。
  2. 逐元素相乘 (Element-wise Multiplication): 将卷积核中的每个数字与输入图像对应位置的数字相乘。
  3. 求和 (Summation): 将所有 9 个乘积结果相加。
  4. 加偏置 (Add Bias): 将上一步得到的和加上一个偏置值 b

这个过程可以用一个公式来表示,输出特征图在位置 (i, j) 的值 O(i, j) 为:

复制代码
O(i, j) = Σ(输入区域 * 卷积核权重) + 偏置

让我们计算输出特征图的第一个值(左上角):

  • 输入区域:

    tex 复制代码
    [1, 0, 1]
    [0, 1, 0]
    [1, 0, 1]
  • 卷积核:

    tex 复制代码
    [1, 0, 1]
    [0, 1, 0]
    [1, 0, 1]
  • 逐元素相乘再求和:

    (1×1) + (0×0) + (1×1) + (0×0) + (1×1) + (0×0) + (1×1) + (0×0) + (1×1)
    = 1 + 0 + 1 + 0 + 1 + 0 + 1 + 0 + 1
    = 5

  • 加上偏置 (假设 b=0):

    5 + 0 = 5

所以,输出特征图的左上角第一个值就是 5

3. 滑动窗口,生成特征图

完成第一次计算后,卷积核会根据设定的步长向右滑动。

  • 向右滑动一格 (步长=1): 卷积核现在覆盖了输入图像顶部中间的 3x3 区域。重复上述的"乘法-加法"过程,得到输出特征图第一行的第二个值。
  • 继续滑动: 重复此过程,直到卷积核无法再向右移动。
  • 换行: 当一行结束后,卷积核会回到最左侧,并向下滑动一格(步长=1),然后开始计算下一行。

这个过程会一直持续,直到卷积核遍历完输入图像所有可能的位置。

4. 计算输出尺寸

对于一个输入尺寸为 H_in × W_in,卷积核尺寸为 K × K,步长为 S,填充为 P 的情况,输出特征图的尺寸 H_out × W_out 可以通过以下公式计算:

H_out = ⌊(H_in - K + 2P) / S⌋ + 1
W_out = ⌊(W_in - K + 2P) / S⌋ + 1

在我们的例子中:

  • H_in = 5, W_in = 5
  • K = 3
  • P = 0 (无填充)
  • S = 1

代入公式:
H_out = ⌊(5 - 3 + 2×0) / 1⌋ + 1 = ⌊2 / 1⌋ + 1 = 3
W_out = ⌊(5 - 3 + 2×0) / 1⌋ + 1 = ⌊2 / 1⌋ + 1 = 3

因此,最终生成的特征图 (Feature Map) 是一个 3x3 的矩阵。这个特征图就是卷积层提取出的某种特定特征(由该卷积核的权重决定)在原始图像上的分布情况。

多通道卷积计算

多通道卷积是卷积神经网络处理真实世界数据(如彩色图像)的基础。它的核心思想是**"先分后合"**:对每个输入通道分别进行卷积,然后将所有通道的结果相加,融合成一个输出通道的特征图。

计算准备:定义输入与卷积核

假设我们要处理一张小型的彩色图像,它有RGB三个通道。

  • 输入 (Input): 一个尺寸为 5x5x3 的三维矩阵。可以看作是3张 5x5 的二维图像堆叠而成,分别代表R、G、B通道。
  • 卷积核 (Filter/Kernel): 为了生成一个 输出通道,我们需要一个同样具有3个通道的卷积核,其尺寸为 3x3x3。它由3个 3x3 的二维矩阵堆叠而成,每个矩阵对应输入的一个通道。
  • 偏置 (Bias): 一个标量值 b,会加到最终求和的结果上。
  • 步长 (Stride) 和 填充 (Padding): 假设步长为1,无填充("Valid" Padding)。
tex 复制代码
输入 (5x5x3)                		卷积核 (3x3x3)
[ R通道 ] [ G通道 ] [ B通道 ]    [ K_r ] [ K_g ] [ K_b ]
  5x5       5x5       5x5        3x3     3x3     3x3
多通道卷积计算步骤

计算的目标是得到一个 3x3 的输出特征图。我们以计算输出特征图左上角第一个值为例。

  1. 分通道卷积 (Channel-wise Convolution)
    将卷积核的每个通道与输入的对应通道进行独立的二维卷积运算。
    • R通道计算: 用卷积核的R通道部分 K_r 在输入图像的R通道上进行卷积,得到一个数值 S_r
      S_r = Σ(输入R区域 * K_r)
    • G通道计算: 用卷积核的G通道部分 K_g 在输入图像的G通道上进行卷积,得到一个数值 S_g
      S_g = Σ(输入G区域 * K_g)
    • B通道计算: 用卷积核的B通道部分 K_b 在输入图像的B通道上进行卷积,得到一个数值 S_b
      S_b = Σ(输入B区域 * K_b)
  2. 结果求和 (Summation)
    将三个通道卷积得到的结果 S_r, S_g, S_b 相加,并加上偏置 b,得到输出特征图在该位置的最终值 O
    O = S_r + S_g + S_b + b

这个 O 就是融合了三通道信息后,在输出特征图左上角的值。

  1. 滑动生成完整特征图
    完成一次计算后,整个 3x3x3 的卷积核会作为一个整体,在 5x5x3 的输入数据上按照设定的步长滑动,重复上述"分通道卷积,再求和"的过程,直到遍历完所有位置,最终生成一个完整的 3x3 输出特征图。
如何得到多个输出通道?

上面的过程只生成了一个输出通道的特征图。在实际的CNN中,我们希望提取多种不同的特征(如边缘、颜色、纹理等)。

为了得到多个输出通道,我们只需要使用多个不同的卷积核(Filters)。

  • 如果我们想输出 N 个通道的特征图,我们就需要准备 N 个独立的 3x3x3 卷积核。
  • 每个卷积核都会独立地与原始输入 5x5x3 进行一次完整的多通道卷积,生成一个 3x3 的特征图。
  • 最终,我们将这 N3x3 的特征图堆叠起来,就得到了一个 3x3xN 的输出张量。
卷积层参数总量计算

一个卷积层的参数总量(即需要学习的权重和偏置数量)由以下因素决定:

复制代码
总参数量 = (卷积核高度 × 卷积核宽度 × 输入通道数 × 输出通道数) + 输出通道数 (偏置)

在我们的例子中,如果输入是 5x5x3,我们想得到 N 个输出通道,使用 3x3 的卷积核,那么参数总量为:
(3 × 3 × 3 × N) + N = 27N + N = 28N 个参数。

PyTorch卷积层API
核心 API:torch.nn.Conv2d

这是最常用的接口,继承自 nn.Module。你需要先在 __init__ 中实例化它,然后在 forward 中调用它。

常用参数详解

参数 类型 默认值 说明
in_channels int - 输入通道数。例如 RGB 图像为 3,灰度图为 1。
out_channels int - 输出通道数(即卷积核的个数)。决定了输出特征图的深度。
kernel_size int 或 tuple - 卷积核大小。3 代表 3x3,(3, 5) 代表高3宽5。
stride int 或 tuple 1 步长。控制卷积核滑动的距离。步长越大,输出尺寸越小。
padding int 或 tuple 0 填充。在输入四周补零。常用 padding=1 配合 kernel_size=3 保持尺寸不变。
bias bool True 是否添加偏置项。
dilation int 或 tuple 1 空洞率。用于空洞卷积,控制卷积核点的间距(例如 2 代表中间隔一个像素)。
groups int 1 分组卷积。控制输入输出通道的连接方式。groups=in_channels 时为深度可分离卷积。
代码示例
python 复制代码
import torch
import torch.nn as nn

# 1. 定义卷积层
# 输入3通道,输出16通道,卷积核3x3,步长1,填充1(保持尺寸不变)
conv_layer = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3, stride=1, padding=1)

# 2. 准备输入数据 (Batch_Size, Channels, Height, Width)
# 假设输入是一张 32x32 的 RGB 图片,批次大小为 1
input_tensor = torch.randn(1, 3, 32, 32)

# 3. 执行前向传播
output = conv_layer(input_tensor)

print(f"输入尺寸: {input_tensor.shape}") # torch.Size([1, 3, 32, 32])
print(f"输出尺寸: {output.shape}")     # torch.Size([1, 16, 32, 32])

池化层计算

池化层(Pooling Layer)的计算逻辑比卷积层简单得多。它的核心思想是"降维 "和"统计":通过固定大小的窗口在特征图上滑动,对窗口内的数值进行统计计算(取最大值或平均值),从而减小特征图的空间尺寸。

与卷积层不同,池化层没有需要学习的参数(权重和偏置),它只是一个固定的数学运算过程。

核心计算公式

池化层输出尺寸的计算公式与卷积层非常相似。假设输入尺寸为 Hin×WinH_{in}×W_{in}Hin×Win ,输出尺寸 Hout×WoutH_{out}×W_{out}Hout×Wout 的计算公式为:

Hout=⌊Hin+2×padding−dilation×(kernel_size−1)−1stride+1⌋H_{out}=⌊\frac{H_{in}+2×padding−dilation×(kernel\_size−1)−1}{stride}+1⌋Hout=⌊strideHin+2×padding−dilation×(kernel_size−1)−1+1⌋

  • kernel_size: 池化窗口大小(如 2x2)。
  • stride: 滑动步长(通常等于 kernel_size)。
  • padding: 填充大小。
  • dilation: 空洞大小(通常为 1)。
  • ⌊⋅⌋⌊⋅⌋ : 向下取整符号(Floor)。
详细计算步骤(以最大池化为例)

假设我们有一个 4x4 的输入特征图,使用 2x2 的池化核,步长为 2无填充

输入矩阵:

tex 复制代码
[1, 3, 2, 4]
[5, 6, 8, 7]
[4, 2, 1, 0]
[9, 7, 3, 2]
1. 划分窗口

池化核从左上角开始,覆盖 2x2 的区域。由于步长为 2,窗口之间不会重叠。

左上窗口:覆盖

tex 复制代码
[1, 3]
[5, 6]

右上窗口:覆盖

tex 复制代码
[2, 4]
[8, 7]

左下窗口:覆盖

tex 复制代码
[4, 2]
[9, 7]

右下窗口:覆盖

tex 复制代码
[1, 0]
[3, 2]
2.执行运算

根据池化类型(最大池化或平均池化)对每个窗口内的数值进行计算。

  • 最大池化 (Max Pooling) :取窗口内的最大值
    • 左上: max⁡(1,3,5,6)=6max⁡(1,3,5,6)=6max⁡(1,3,5,6)=6
    • 右上: max⁡(2,4,8,7)=8max⁡(2,4,8,7)=8max⁡(2,4,8,7)=8
    • 左下: max⁡(4,2,9,7)=9max⁡(4,2,9,7)=9max⁡(4,2,9,7)=9
    • 右下: max⁡(1,0,3,2)=3max⁡(1,0,3,2)=3max⁡(1,0,3,2)=3
  • 平均池化 (Average Pooling) :取窗口内的平均值
    • 左上: (1+3+5+6)/4=3.75(1+3+5+6)/4=3.75(1+3+5+6)/4=3.75
    • 右上: (2+4+8+7)/4=5.25(2+4+8+7)/4=5.25(2+4+8+7)/4=5.25
    • ...以此类推。
3. 生成输出

将计算结果组合成新的特征图。对于上述最大池化,输出为 2x2 的矩阵:

tex 复制代码
[6, 8]
[9, 3]
多通道池化层计算

在处理多通道输入数据时,池化层对每个输入通道分别池化,而不是像卷积层那样将各个通道的输入相加。这意味着池化层的输出和输入的通道数是相等。

常见池化类型与计算特点
类型 计算逻辑 特点与应用
最大池化 (Max Pooling) 取窗口内的最大值 保留最显著特征(如边缘、纹理)。抗噪能力强,是最常用的池化方式。
平均池化 (Average Pooling) 取窗口内的平均值 保留整体背景信息,特征图更平滑。常用于网络末端(如全局平均池化)。
全局池化 (Global Pooling) 窗口大小 = 特征图大小 将整个 H×WH×WH×W 压缩为 1×11×11×1 。常用于替代全连接层,防止过拟合。
PyTorch 代码

PyTorch 中,我们通常使用 nn.MaxPool2dnn.AvgPool2d

python 复制代码
import torch
import torch.nn as nn

# 1. 定义输入 (1, 1, 4, 4) -> (Batch, Channel, Height, Width)
input_tensor = torch.tensor([[[[1., 3, 2, 4],
                               [5, 6, 8, 7],
                               [4, 2, 1, 0],
                               [9, 7, 3, 2]]]])

# 2. 定义最大池化层 (2x2 窗口, 步长 2)
max_pool = nn.MaxPool2d(kernel_size=2, stride=2)

# 3. 计算
output = max_pool(input_tensor)

print("输入尺寸:", input_tensor.shape) # torch.Size([1, 1, 4, 4])
print("输出尺寸:", output.shape)     # torch.Size([1, 1, 2, 2])
print("输出结果:\n", output)
# 输出结果: tensor([[[[6., 8.], [9., 3.]]]])
进阶:自适应池化 (Adaptive Pooling)

有时候我们不想手动计算 kernel_sizestride,而是希望指定输出尺寸 (例如无论输入多大,输出都必须是 7x7)。这时可以使用 自适应池化 (如 nn.AdaptiveAvgPool2d)。

  • 计算逻辑 :库函数会自动根据输入尺寸和期望的输出尺寸,反推合适的 kernel_sizestride
  • 应用场景:处理不同分辨率的输入图像,或者在 CNN 末端替代全连接层。
python 复制代码
# 无论输入是 32x32 还是 64x64,输出强制变为 1x1
global_avg_pool = nn.AdaptiveAvgPool2d((1, 1)) 

CNN的主要应用

  • 计算机视觉:图像分类、目标检测(如YOLO)、语义分割、人脸识别、图像生成等。
  • 自然语言处理:文本分类、情感分析等,通过一维卷积提取文本的局部语义特征。
  • 语音信号处理:语音识别,通常将音频信号转换为语谱图后,使用二维卷积进行处理。
  • 时序数据分析:金融预测、工业故障诊断等。

CNN的优势与局限

  • 优势
    1. 参数效率高:局部连接和权值共享使其参数量远小于全连接网络。
    2. 自动特征提取:无需人工设计特征,能自动学习最适合任务的层次化特征。
    3. 平移不变性:对目标在图像中的位置变化具有一定的鲁棒性。
  • 局限
    1. 全局建模能力弱:传统CNN的感受野有限,难以捕捉图像中相距较远像素间的长距离依赖关系。
    2. 对几何变换敏感:对图像的旋转、缩放等非刚性形变泛化能力有限,通常需要大量数据增强来弥补。
相关推荐
kishu_iOS&AI2 小时前
深度学习 —— 正则化&批量归一化BN
人工智能·pytorch·python·深度学习
科技小花2 小时前
测评|2026五大数据治理平台横向对比:谁在定义数据中台的“智能引擎”?
大数据·数据库·人工智能·数据治理·数据中台
Lsk_Smion2 小时前
【类增量学习之2025ICCV】TUNA : 让AI像搭积木一样学习新知识,TUNA的适配器融合之道
人工智能·深度学习·机器学习·论文笔记
沫儿笙2 小时前
弧焊机器人智能节气设备
人工智能·机器人
前端不太难2 小时前
如何设计 Agent 的“最小权限原则”
人工智能·状态模式·agent
JAVA学习通2 小时前
AI 工作流编排系统的任务拆分、重试与观测:2026年工程实践深度解析
java·人工智能·spring
cl131413142 小时前
烟气测量格恩朗流量计选型指南
大数据·网络·人工智能·产品运营
xixixi777772 小时前
国内首家“AI+量子”实体公司成立:量智开物发布“追风”“扁鹊”,开启下一代计算文明大门
大数据·网络·人工智能·安全·ai·科大讯飞·量子计算
武帝为此2 小时前
【相关性分析综述】
人工智能·数学建模