Skip to content

PyTorch - 卷积神经网络

深度学习是机器学习的一个子领域,推动了人工智能的重大发展。卷积神经网络(CNNs)是一类深度神经网络,已在各种感知任务中占据主导地位,尤其是在计算机视觉领域。

虽然有多种类型的深度神经网络,但其中两个主要的类型是:

  • 卷积神经网络 (CNNs)
  • 循环神经网络 (RNNs)

本章重点介绍卷积神经网络,其核心组件,以及在 PyTorch 中的一个基本实现。

CNNs 专门设计用于处理具有网格状拓扑结构的数据,例如图像(像素的二维网格)或序列(一维网格)。它们广泛应用于图像识别、目标检测和图像分割等领域。

CNNs 和标准全连接神经网络(fully connected neural networks)的一个关键区别在于,CNNs 利用了输入数据的空间结构。CNNs 不像标准网络那样将每个输入神经元连接到下一层的每个神经元,而是使用局部连接和共享权重,这使得它们对于图像等高维输入更加高效和可扩展。

主要的科技公司在 CNN 的研究和开发上投入巨资,应用范围从照片标记到自动驾驶。

每个 CNN 架构都基于三个基本思想构建:

  • 局部感受野 (Local Receptive Fields)
  • 共享权重 (卷积 Shared Weights/Convolution)
  • 池化 (Pooling)

1. 局部感受野 (Local Receptive Fields)

Section titled “1. 局部感受野 (Local Receptive Fields)”

CNNs 利用输入数据中存在的空间相关性。CNN 早期层中的神经元并不连接到所有输入像素。相反,每个神经元仅连接到输入的一个小的局部区域,称为其局部感受野(Local Receptive Field)。例如,一个神经元可能连接到图像中一个 5x5 的像素块。这个神经元处理来自这个块的信息,有效地寻找小的、特定的模式或特征。

想象一下将这个小块滑过整个输入图像。块的每个位置都由不同的神经元(或同一组权重,我们接下来会看到)处理,这使得网络能够检测特征,而不管它们在图像中的位置如何。

2. 共享权重 (卷积 Shared Weights/Convolution)

Section titled “2. 共享权重 (卷积 Shared Weights/Convolution)”

将滤波器(filters)(一组权重 weights)在输入上滑动的过程称为卷积(convolution)。一个关键方面是,同一组权重(滤波器 filters 或核 kernel)用于输入上所有的局部感受野(local receptive fields)。这被称为权重共享(weight sharing)。如果一个滤波器在图像的某个部分有效地检测到特定特征(例如,边缘或角),那么它在其他部分也可能有用。

权重共享显著减少了模型的参数数量,使其更容易训练且不易过拟合(overfitting)。在输入上应用滤波器后的输出称为特征图(feature map),它突出显示了滤波器检测到的特定特征所在的位置。

一个卷积层(convolutional layer)通常并行学习多个这样的滤波器,每个滤波器检测不同的特征。

池化层(Pooling layers)通常插入在 CNN 的卷积层(convolutional layers)之后。它们目的是减小特征图(feature maps)的空间维度(宽度和高度),从而减少参数数量和计算负载。这也有助于使特征表示对输入中的小幅度平移或扭曲更加鲁棒。

最常见的池化类型是最大池化(Max Pooling)。它涉及在一个特征图上滑动一个窗口,并在每个窗口位置取最大值。这保留了最突出的特征,同时丢弃了不太重要的细节,并降低了维度。

让我们创建一个简单的 CNN 使用 PyTorch 的 nn.Module。

我们需要 torch、用于神经网络层的 torch.nn 以及用于激活函数的 torch.nn.functional。

import torch
import torch.nn as nn
import torch.nn.functional as F
# 注意:对于现代 PyTorch 版本,不再需要 torch.autograd.Variable。
# 如果 Tensors 的 'requires_grad' 属性为 True,它们可以直接跟踪梯度。

我们将创建一个继承自 nn.Module 的 SimpleCNN 类。假设我们的输入图像是 3 通道(RGB),尺寸为 32x32 像素。

class SimpleCNN(nn.Module):
def __init__(self):
super(SimpleCNN, self).__init__()
# 卷积层 1
# 输入通道 Input channels = 3(例如,RGB 图像)
# 输出通道 Output channels = 18(要学习的滤波器数量)
# Kernel size = 3x3
# Stride = 1(滤波器每次移动的步长)
# Padding = 1(在输入周围添加 1 像素宽度的零边框)
# Padding 有助于保持空间维度:(W - K + 2P)/S + 1 = (32 - 3 + 2*1)/1 + 1 = 32
self.conv1 = nn.Conv2d(in_channels=3, out_channels=18, kernel_size=3, stride=1, padding=1)
# 最大池化层 Max Pooling Layer
# Kernel size = 2x2
# Stride = 2
# 池化减小维度:(W - K)/S + 1 = (32 - 2)/2 + 1 = 16
self.pool = nn.MaxPool2d(kernel_size=2, stride=2, padding=0)
# 在 conv1(18 通道,32x32)和 pool(18 通道,16x16)之后,
# 展平后的特征向量大小将是 18 * 16 * 16 = 4608。
# 全连接层 1 (线性层 Fully Connected Layer/Linear Layer)
# 输入特征 Input features = 18 * 16 * 16 = 4608
# 输出特征 Output features = 64
self.fc1 = nn.Linear(18 * 16 * 16, 64)
# 全连接层 2 (输出层 Fully Connected Layer/Output Layer)
# 输入特征 Input features = 64(来自前一个 FC 层)
# 输出特征 Output features = 10(例如,用于分类任务中的 10 个类别)
self.fc2 = nn.Linear(64, 10)
def forward(self, x):
# x 是输入 tensor,例如 (batch_size, 3, 32, 32)
# 应用卷积 1,然后是 ReLU 激活
# Shape 变化:(batch_size, 3, 32, 32) -> (batch_size, 18, 32, 32)
x = F.relu(self.conv1(x))
# 应用最大池化
# Shape 变化:(batch_size, 18, 32, 32) -> (batch_size, 18, 16, 16)
x = self.pool(x)
# 展平池化层的输出,以便送入全连接层
# -1 推断出 batch_size。剩余维度被展平为一个。
# Shape 变化:(batch_size, 18, 16, 16) -> (batch_size, 18 * 16 * 16),即 (batch_size, 4608)
x = x.view(-1, 18 * 16 * 16)
# 应用全连接层 1,然后是 ReLU 激活
# Shape 变化:(batch_size, 4608) -> (batch_size, 64)
x = F.relu(self.fc1(x))
# 应用全连接层 2 (输出层)
# Shape 变化:(batch_size, 64) -> (batch_size, 10)
# 输出层的激活函数(例如,用于多分类的 softmax)
# 通常在模型外部应用,或作为损失函数(如 nn.CrossEntropyLoss)的一部分。
x = self.fc2(x)
return x

要使用这个 SimpleCNN,你需要实例化它,定义一个损失函数(例如,用于分类的 nn.CrossEntropyLoss),以及一个优化器(例如,torch.optim.Adam)。然后,通过遍历数据集进行训练,执行前向传播,计算损失,反向传播误差,并更新权重。

# 示例实例化:
# model = SimpleCNN()
# print(model)
# # 示例虚拟输入(1 个图像的批量,3 通道,32x32 像素)
# dummy_input = torch.randn(1, 3, 32, 32)
# output = model(dummy_input)
# print("Output shape:", output.shape) # 预期:torch.Size([1, 10])

CNNs 是许多最先进 AI 系统的基础:

  • 图像分类 (Image Classification): 为图像分配标签(例如,CIFAR-10, ImageNet)。
  • 目标检测 (Object Detection): 识别和定位图像中的多个对象(例如,YOLO, Faster R-CNN)。
  • 图像分割 (Image Segmentation): 将图像中的每个像素分类到特定类别(例如,用于医学影像的 U-Net)。
  • 人脸识别 (Facial Recognition): 从图像或视频中识别或验证个体。
  • 生成模型 (Generative Models): 创建新图像(例如,生成对抗网络 GANs, 变分自编码器 VAEs)。

为了进一步学习,可以探索著名的 CNN 架构,如 LeNet-5(开创性)、AlexNet、VGG、ResNet(残差网络 Residual Networks)和 DenseNet。PyTorch 官方文档和教程是深入了解 CNN 及其应用的绝佳资源。