DL:卷积神经网络的基本原理与 PyTorch 实现

卷积神经网络(Convolutional Neural Network,CNN)是深度学习中最重要的模型结构之一,尤其适合处理图像、视频等具有空间结构的数据。与普通多层感知机(MLP)直接把输入展开为一维向量不同,卷积神经网络会尽量保留图像中的局部邻域关系,通过卷积核在图像上滑动,自动学习边缘、纹理、形状等层级特征。

MLP 可以通过多层"线性变换 + 非线性激活"学习复杂非线性关系。但是,如果直接用 MLP 处理图像,通常需要把二维图像展开成一维向量,这会削弱图像原有的空间结构信息。

图 1:从 MLP 到卷积神经网络

卷积神经网络正是为了解决这类问题而发展起来的。它通过局部连接、权重共享、特征图、池化等机制,使模型能够更有效地从图像中提取空间特征,并逐层形成从低级视觉特征到高级语义特征的表示。

一、为什么需要卷积神经网络

图像数据与普通表格数据不同。

在表格数据中,每一列通常表示一个明确特征,例如面积、楼龄、收入、消费次数等。而在图像中,每个像素本身并不总是具有独立语义。一个像素是否重要,往往取决于它周围像素的排列关系。

例如,在一张手写数字图片中:

• 相邻像素共同形成笔画

• 笔画组合形成局部结构

• 局部结构进一步组合成数字整体

• 数字类别取决于这些空间结构的整体模式

如果把图像直接展平成一维向量,再交给 MLP 处理,模型虽然仍然可以训练,但会遇到几个问题。

第一,空间结构被削弱。

原本相邻的像素在展平后可能相距很远,模型不容易直接利用局部邻域关系。

第二,参数数量可能过大。

如果一张图像大小为 224 × 224 × 3,展平后就是 150528 个输入值。若直接连接到一个有 1000 个神经元的隐藏层,仅第一层就会产生大量参数。

第三,模型难以自动利用平移不变性。

图像中的同一个边缘、纹理或局部形状,可能出现在不同位置。普通全连接网络需要在不同位置重复学习类似特征。

卷积神经网络的基本思想是:用较小的卷积核在图像上滑动,在不同位置重复检测相同局部模式。

这样做带来两个重要好处:

• 局部连接:每个神经元只关注局部区域

• 权重共享:同一个卷积核在整张图像上重复使用

因此,CNN 比普通 MLP 更适合处理图像这类具有空间结构的数据。

二、卷积运算的基本思想

卷积神经网络的核心操作是卷积(Convolution)。在图像处理中,可以把卷积理解为:用一个小矩阵在图像上滑动,并对局部区域进行加权求和。

这个小矩阵通常称为卷积核(Kernel)或滤波器(Filter)。

图 2:卷积核在图像上滑动并生成特征图

假设输入图像是一个二维矩阵,卷积核也是一个较小的二维矩阵。卷积核每次覆盖图像中的一个局部区域,然后把对应位置相乘并求和,得到输出特征图中的一个数值。

对于二维输入,简化的卷积计算可以写为:

其中:

• X 表示输入图像或输入特征图

• K 表示卷积核

• S(i,j) 表示输出特征图在位置 (i,j) 的值

• m、n 表示卷积核内部的位置索引

• ∑ 表示对局部区域内的元素求和

从直观角度看,卷积核像一个"局部模式检测器"。

不同卷积核可以学习不同视觉模式:

• 有的卷积核可能检测水平边缘

• 有的卷积核可能检测垂直边缘

• 有的卷积核可能检测斜线纹理

• 更深层的卷积核可能检测局部形状或物体部件

在 PyTorch 中,torch.nn.Conv2d 用于对由多个输入平面组成的输入信号执行二维卷积,常用于图像特征提取。

三、卷积层的关键概念

理解 CNN,需要掌握几个核心概念:卷积核、步幅、填充、通道和特征图。

图 3:卷积层中的卷积核、通道与特征图

1、卷积核:局部特征检测器

卷积核是一个可学习的小矩阵。训练开始时,卷积核中的数值通常是随机初始化的;训练过程中,模型会通过反向传播不断调整这些数值,使卷积核逐渐学会对任务有用的局部模式。

例如,一个 3 × 3 卷积核可以写成:

其中:

• K 表示卷积核

• k₁₁、k₁₂、...、k₃₃ 表示卷积核中的可学习参数

• 3 × 3 表示卷积核覆盖的局部区域大小

卷积核越大,每次看到的局部区域越大;卷积核越小,计算更轻量,也更容易堆叠成深层结构。

在现代 CNN 中,3 × 3 卷积非常常见,因为它既能捕捉局部结构,又具有较好的计算效率。

2、步幅:卷积核每次移动的距离

步幅(Stride)表示卷积核每次滑动的距离。

如果 stride = 1,卷积核每次移动 1 个像素;如果 stride = 2,卷积核每次移动 2 个像素。

步幅越大,输出特征图的尺寸通常越小。

可以简单理解为:

• 小步幅:保留更多空间细节

• 大步幅:更快降低特征图尺寸

3、填充:控制边缘信息与输出尺寸

填充(Padding)是在输入图像边缘补充额外像素,常见做法是在边缘补 0。

填充的作用主要有两个:

• 让卷积核能够覆盖图像边缘区域

• 控制输出特征图的空间尺寸

如果没有填充,卷积核在滑动时无法完全覆盖边缘像素,输出尺寸会逐层变小。适当填充可以缓解这一问题。

4、通道:从灰度图到彩色图

图像通常具有通道(Channel)。

例如:

• 灰度图通常有 1 个通道

• 彩色 RGB 图像通常有 3 个通道

• 卷积层输出的特征图也可以有多个通道

在 CNN 中,每个输出通道通常对应一个卷积核学习到的一类特征。多个卷积核会生成多个输出通道,也就是多张特征图。

例如,一个卷积层可以把输入从 3 个通道变成 32 个通道:

apache 复制代码
nn.Conv2d(in_channels=3, out_channels=32, kernel_size=3)

其中:

• in_channels=3 表示输入有 3 个通道

• out_channels=32 表示输出 32 个特征通道

• kernel_size=3 表示使用 3 × 3 卷积核

在 PyTorch 中,二维卷积层的输入通常具有形状 N、C、H、W,分别表示批量大小、通道数、高度和宽度;Conv2d 的 in_channels 和 out_channels 用来指定输入与输出通道数量。

5、特征图:卷积层的输出

卷积层的输出称为特征图(Feature Map)。一张特征图可以理解为某个卷积核在图像不同位置上的响应强度。

如果某个位置与卷积核学习到的模式相似,响应值通常较高;如果不相似,响应值可能较低。

从直观角度看,特征图回答的是:某种局部模式在图像的哪些位置出现得比较明显?

随着网络层数加深,特征图表达的内容也会逐渐抽象:

go 复制代码
边缘、角点→ 纹理、笔画→ 局部形状→ 物体部件→ 类别相关语义

四、池化层与空间尺寸压缩

卷积层负责提取局部特征,但如果一直保留完整空间尺寸,计算量会很大,而且模型可能过度关注细节位置。

池化层(Pooling Layer)用于降低特征图的空间尺寸,同时保留主要响应信息。

最常见的是最大池化(Max Pooling)。它在局部区域中取最大值,例如在 2 × 2 区域中选出最大响应。

图 4:最大池化如何压缩特征图尺寸

最大池化可以写为:

其中:

• X 表示输入特征图

• Y(i,j) 表示池化后在位置 (i,j) 的值

• R(i,j) 表示当前位置对应的局部区域

• max 表示取最大值

从直观角度看,最大池化保留的是局部区域中最明显的特征响应。

池化层的作用包括:

• 降低特征图尺寸

• 减少计算量

• 增强一定程度的位置鲁棒性

• 保留局部区域中最显著的特征

例如,一个 2 × 2 最大池化层通常会把特征图的高和宽约缩小一半。

在 PyTorch 中,torch.nn.MaxPool2d 用于对由多个输入平面组成的输入信号执行二维最大池化,输入形状通常也按 N、C、H、W 组织。

需要注意的是,池化并不是 CNN 中唯一的下采样方式。现代网络中,也常用步幅卷积(Strided Convolution)来替代部分池化操作。

五、CNN 的典型结构

一个基础 CNN 通常由三类模块组成:

卷积层 → 激活函数 → 池化层 → 全连接层 → 输出层

更具体地说,常见图像分类 CNN 可以写成:

go 复制代码
输入图像→ 卷积层→ ReLU→ 池化层→ 卷积层→ ReLU→ 池化层→ 展平→ 全连接层→ 输出类别得分

其中:

• 卷积层负责提取局部特征

• ReLU 负责引入非线性

• 池化层负责压缩空间尺寸

• 展平操作把多维特征图转换成一维向量

• 全连接层负责综合特征并输出类别得分

图 5:卷积神经网络的典型结构

在图像分类任务中,CNN 的前半部分通常称为特征提取器,后半部分通常称为分类器。

可以简单理解为:

• 特征提取器:从图像中提取有用视觉特征

• 分类器:根据这些特征判断类别

例如,对于手写数字识别任务,前面的卷积层可能学习笔画、弯曲结构、局部纹理等特征,后面的全连接层则根据这些特征判断数字类别。

六、CNN 为什么适合图像任务

CNN 适合图像任务,主要来自三个结构优势:局部感受野、权重共享和层级特征学习。

1、局部感受野

局部感受野(Local Receptive Field)表示一个神经元只关注输入中的局部区域。

在图像中,局部像素往往具有强相关性。例如,一个边缘通常由相邻像素共同形成。卷积层利用局部感受野,可以优先学习这些局部结构,而不必一开始就连接整张图像。

这比普通全连接层更符合图像数据的空间规律。

2、权重共享

权重共享(Weight Sharing)表示同一个卷积核在图像不同位置重复使用。

如果一个卷积核学会检测某种边缘模式,那么它可以在整张图像的不同位置检测同样的模式。

这带来两个好处:

• 大幅减少参数数量

• 提高对位置变化的适应能力

例如,同一个竖直边缘可能出现在图像左侧、右侧或中间。通过权重共享,CNN 不需要为每个位置单独学习一套参数。

3、层级特征学习

CNN 的浅层通常学习低级特征,深层逐渐学习更抽象的高级特征。

可以粗略理解为:

• 浅层:边缘、角点、纹理

• 中层:局部形状、部件

• 深层:物体结构、类别语义

这种层级特征学习能力,是 CNN 在图像识别任务中表现优异的重要原因。

4、与 MLP 的对比

如果用 MLP 直接处理图像,通常需要展平输入。这样会削弱空间结构,同时带来大量参数。

而 CNN 在卷积阶段保留了图像的二维结构,通过局部连接和权重共享更高效地提取空间特征。

图 6:MLP 与 CNN 处理图像方式的对比

可以简单对比为:

• MLP:把图像展平成向量,再进行全连接计算

• CNN:保留图像空间结构,用卷积核逐层提取局部特征

七、PyTorch 实现:手写数字分类 CNN

下面使用 PyTorch 构建一个简单 CNN,完成 MNIST 手写数字分类任务。

图 7:CNN 在 MNIST 上的训练与预测流程

MNIST 是经典手写数字图像数据集,图像为 0 到 9 的数字类别。Torchvision 提供了 torchvision.datasets.MNIST 数据集接口,可以通过 root、train、transform、target_transform、download 等参数加载数据。

1、导入库并设置环境

python 复制代码
import torch                     # PyTorch 主库import torch.nn as nn            # 神经网络模块(层、损失函数等)import torch.optim as optim      # 优化器模块(SGD、Adam等)
from torch.utils.data import DataLoader  # 数据加载器,批量加载数据from torchvision import datasets, transforms  # torchvision提供常用数据集和图像预处理工具

这里使用:

• torch.nn 定义神经网络层

• torch.optim 定义优化器

• DataLoader 按小批量加载数据

• torchvision.datasets 加载 MNIST 数据集

• torchvision.transforms 进行图像预处理

2、准备 MNIST 数据集

python 复制代码
# 图像预处理:将 PIL 图像转为张量,并标准化到均值和标准差(基于 MNIST 统计值)transform = transforms.Compose([    transforms.ToTensor(),                     # 将 (H,W) 或 (H,W,C) 转为 (C,H,W),值域 [0,1]    transforms.Normalize((0.1307,), (0.3081,)) # 标准化:减去均值0.1307,除以标准差0.3081])
# MNIST 训练集(60000张手写数字图片)train_dataset = datasets.MNIST(    root="./data",          # 数据集存放目录    train=True,             # 加载训练集    download=True,          # 如果本地没有则自动下载    transform=transform     # 应用上述预处理)
# MNIST 测试集(10000张图片)test_dataset = datasets.MNIST(    root="./data",    train=False,    download=True,    transform=transform)
# 训练数据加载器:批次大小64,每个 epoch 打乱顺序train_loader = DataLoader(    train_dataset,    batch_size=64,    shuffle=True)
# 测试数据加载器:批次大小1000,无需打乱test_loader = DataLoader(    test_dataset,    batch_size=1000,    shuffle=False)

其中:

• ToTensor() 将图像转换为张量

• Normalize() 对图像进行标准化

• train=True 表示加载训练集

• train=False 表示加载测试集

• download=True 表示如果本地没有数据则自动下载

• DataLoader 用于按批量读取数据

MNIST 是灰度图像,因此输入通道数为 1。每张图片大小为 28 × 28。

3、定义 CNN 模型

ruby 复制代码
# 定义一个简单的 CNN 模型(适用于 MNIST 28x28 灰度图)class SimpleCNN(nn.Module):    def __init__(self):        super().__init__()
        # 特征提取部分:卷积 + ReLU + 池化(两层)        self.features = nn.Sequential(            # 第一层卷积:输入单通道 → 16通道,3x3卷积核,padding=1保持尺寸            nn.Conv2d(                in_channels=1,                out_channels=16,                kernel_size=3,                padding=1            ),            nn.ReLU(),            nn.MaxPool2d(kernel_size=2),   # 2x2池化,尺寸减半:28→14
            # 第二层卷积:16通道 → 32通道,3x3卷积核,padding=1保持尺寸            nn.Conv2d(                in_channels=16,                out_channels=32,                kernel_size=3,                padding=1            ),            nn.ReLU(),            nn.MaxPool2d(kernel_size=2)    # 再次池化:14→7        )
        # 分类部分:展平 → 全连接 → ReLU → 输出10类        self.classifier = nn.Sequential(            nn.Flatten(),                                      # 展平为向量:32*7*7=1568维            nn.Linear(32 * 7 * 7, 128),                        # 全连接层:1568 → 128            nn.ReLU(),            nn.Linear(128, 10)                                 # 输出10个类别logits        )
    def forward(self, x):        x = self.features(x)          # 提取特征        x = self.classifier(x)        # 分类输出        return x
# 实例化模型model = SimpleCNN()

这个 CNN 可以分为两部分。

第一部分是特征提取器:

Conv2d → ReLU → MaxPool2d → Conv2d → ReLU → MaxPool2d

第二部分是分类器:

Flatten → Linear → ReLU → Linear

输入图像尺寸变化过程为:

go 复制代码
输入:1 × 28 × 28第一次卷积后:16 × 28 × 28第一次池化后:16 × 14 × 14第二次卷积后:32 × 14 × 14第二次池化后:32 × 7 × 7展平后:32 × 7 × 7 = 1568输出:10 个类别得分

其中:

• 第一个卷积层把 1 个输入通道变成 16 个特征通道

• 第二个卷积层把 16 个通道变成 32 个通道

• 两次最大池化分别把空间尺寸减半

• 最后一层输出 10 个类别得分,对应数字 0 到 9

4、定义损失函数和优化器

ini 复制代码
# 定义损失函数:交叉熵损失(适用于多分类,内部包含 Softmax)criterion = nn.CrossEntropyLoss()
# 定义优化器:Adam 自适应学习率优化器,学习率 0.001,优化模型的全部参数optimizer = optim.Adam(model.parameters(), lr=0.001)

其中:

• CrossEntropyLoss 用于多分类任务

• Adam 是常用优化器,lr=0.001 表示学习率,model.parameters() 表示把模型所有可训练参数交给优化器

需要注意,CrossEntropyLoss() 直接接收模型输出的 logits,不需要在模型最后手动添加 Softmax。PyTorch 文档说明,CrossEntropyLoss 可用于 C 类分类任务,并计算输入 logits 与目标类别之间的交叉熵损失。

5、训练模型

python 复制代码
num_epochs = 3                     # 训练轮数
for epoch in range(num_epochs):    model.train()                  # 切换到训练模式(启用Dropout、BatchNorm等)    total_loss = 0.0
    # 遍历训练集的所有批次    for images, labels in train_loader:        # 1. 前向传播:计算预测输出        outputs = model(images)    # 输入图像,得到logits        loss = criterion(outputs, labels)  # 计算交叉熵损失
        # 2. 反向传播与参数更新        optimizer.zero_grad()      # 清空旧梯度        loss.backward()            # 反向传播,计算梯度        optimizer.step()           # 更新模型参数
        # 累加当前批次的损失(按样本数加权)        total_loss += loss.item() * images.size(0)
    # 计算本轮平均损失(总损失 / 总样本数)    avg_loss = total_loss / len(train_loader.dataset)
    print(f"Epoch [{epoch + 1}/{num_epochs}], Loss: {avg_loss:.4f}")

这段训练代码体现了 PyTorch 中最常见的训练闭环:

前向传播 → 计算损失 → 清空旧梯度 → 反向传播 → 更新参数

其中:

• model.train() 表示进入训练模式

• outputs = model(images) 表示前向传播

• loss = criterion(outputs, labels) 表示计算损失

• optimizer.zero_grad() 用于清空旧梯度

• loss.backward() 用于自动计算梯度

• optimizer.step() 用于更新参数

6、评估模型

python 复制代码
# 切换到评估模式(关闭Dropout、BatchNorm等训练行为)model.eval()
correct = 0   # 正确预测的样本数total = 0     # 总样本数
# 禁用梯度计算,降低内存占用并加速with torch.no_grad():    for images, labels in test_loader:        outputs = model(images)                # 前向传播,得到logits        predicted = outputs.argmax(dim=1)      # 取概率最大的类别作为预测
        total += labels.size(0)                # 累计本批次样本数        correct += (predicted == labels).sum().item()  # 累计正确预测数
# 计算测试集准确率accuracy = correct / totalprint(f"测试集准确率:{accuracy:.4f}")

其中:

• model.eval() 表示进入评估模式

• torch.no_grad() 表示不计算梯度

• outputs.argmax(dim=1) 取最大得分对应的类别

• accuracy 表示测试集准确率

对于多分类任务,模型输出的是 10 个类别得分。哪个类别得分最高,模型就预测为哪个数字。

7、查看单个批次的形状变化

为了帮助理解 CNN 中张量形状的变化,可以打印一个批次的输入和输出形状:

apache 复制代码
# 从训练数据加载器中获取一个批次(64张图像)images, labels = next(iter(train_loader))
print("输入图像形状:", images.shape)   # 期望 (64,1,28,28)
# 将图像输入模型,得到预测logitsoutputs = model(images)
print("输出结果形状:", outputs.shape)   # 期望 (64,10)

可能看到类似结果:

css 复制代码
输入图像形状: torch.Size([64, 1, 28, 28])输出结果形状: torch.Size([64, 10])

其中:

• 64 表示 batch_size

• 1 表示灰度图像通道数

• 28 × 28 表示图像高和宽

• 10 表示 10 个类别得分

这说明模型把一批 28 × 28 的手写数字图像,转换成了对应 10 个类别的预测得分。

八、CNN 的适用场景、局限与扩展方向

卷积神经网络是计算机视觉中的基础模型之一。它非常适合处理具有空间结构的数据,但也并不是所有任务中的唯一选择。

图 8:CNN 的适用场景、局限与扩展方向

1、适用场景

CNN 常用于图像和视觉相关任务,例如:

• 图像分类

• 目标检测

• 图像分割

• 人脸识别

• 医学影像分析

• 遥感图像分析

• 视频理解中的局部空间特征提取

在这些任务中,局部结构和空间模式通常很重要,因此 CNN 具有天然优势。

2、主要优势

CNN 的主要优势包括:

• 能保留图像空间结构

• 参数量相对全连接网络更少

• 能自动学习局部特征

• 对局部平移具有一定鲁棒性

• 能逐层形成从低级到高级的视觉表示

• 适合作为复杂视觉模型的基础模块

其中,局部连接和权重共享是 CNN 高效处理图像的重要原因。

3、主要局限

CNN 也有一些局限:

• 对大规模数据和算力有一定需求

• 深层 CNN 训练时需要合理初始化、归一化和优化策略

• 对长距离依赖关系的建模能力有限

• 对图像外的结构化表格数据未必优于传统方法

• 对旋转、尺度变化等复杂变换仍可能需要数据增强或特殊结构

在一些现代视觉任务中,CNN 常与注意力机制、Transformer 或多尺度结构结合,以提升特征表达能力。

4、扩展方向

从基础 CNN 出发,可以继续学习以下模型和技术:

• LeNet:早期经典 CNN

• AlexNet:推动深度 CNN 发展的代表模型

• VGG:使用小卷积核堆叠深层网络

• ResNet:通过残差连接缓解深层网络训练困难

• U-Net:常用于医学图像分割

• YOLO:常用于目标检测

• Vision Transformer:将 Transformer 引入视觉任务

这些模型虽然结构更复杂,但都可以从卷积、特征图、下采样、非线性激活和端到端训练等基础思想出发理解。

📘 小结

卷积神经网络通过卷积核、局部连接、权重共享和池化等机制,更有效地处理图像中的空间结构。它能够逐层学习从边缘、纹理到形状和类别语义的视觉特征,是计算机视觉中的基础模型。理解 CNN,有助于继续学习 ResNet、U-Net、YOLO 和 Vision Transformer 等更复杂的视觉模型。

"点赞有美意,赞赏是鼓励"

相关推荐
金融小师妹9 小时前
基于AI联储治理模型的政策重构分析:沃什试图重塑美联储,但现实复杂度远超预期
大数据·深度学习·逻辑回归·线性回归
csdn小瓯9 小时前
前端工程化:React + TypeScript + Tailwind CSS 的组件化实践
开发语言·人工智能·python
蓦然回首却已人去楼空9 小时前
深度学习进阶:自然语言处理|3.4 QA|用 SimpleCBOW 讲清楚 backward 为什么有的 return,有的不 return
人工智能·深度学习·自然语言处理
Zldaisy3d10 小时前
为增材制造“驱动器”中国,注入规模化应用更强动力 | TCT亚洲展专访西门子全球增材制造副总裁
大数据·人工智能·制造
AllData公司负责人10 小时前
亲测丝滑,体验跃迁|AllData通过集成开源项目StreamPark,实时流任务调度更省心!
java·大数据·数据库·人工智能·算法·实时计算·实时开发平台
AskHarries10 小时前
Reddit 找需求完整教程:3小时找到20个真实痛点
人工智能
小柒儿33610 小时前
充电桩行业的秩序重构,CCC证书正在划定新的起跑线
人工智能
思诺学长10 小时前
智能物流机器人的技术演进:AGV / AMR 与具身智能融合路径
人工智能
ZHW_AI课题组10 小时前
基于LSTM的天气预测
人工智能·rnn·lstm