Skip to content

PyTorch - 卷积网络介绍

PyTorch - 卷积神经网络 (ConvNets) 简介

Section titled “PyTorch - 卷积神经网络 (ConvNets) 简介”

卷积神经网络(ConvNets 或 CNNs)是一类深度神经网络,最常用于分析视觉图像。它们的灵感来源于动物视觉皮层的组织结构,旨在自动且自适应地学习输入图像中特征的空间层级。典型的 ConvNet 架构包含几种关键类型的层:

  • Conv2D(卷积层):核心构建模块。它将一组可学习的滤波器(filter,也称卷积核 kernel)应用到输入图像。每个滤波器在输入数据的宽度和高度上滑动,计算滤波器条目与输入数据在各个位置的点积,从而生成该滤波器的 2D 激活图(activation map)。这个过程有助于检测边缘、角点和纹理等特征。
  • 激活函数(Activation Function,例如 ReLU):在卷积层(和线性层)之后逐元素应用,以向模型引入非线性,使其能够学习更复杂的模式。ReLU(修正线性单元 Rectified Linear Unit)是一种流行的选择,如果输入为正,则直接输出;否则输出零。
  • MaxPool2D(最大池化层):减小输入体的空间维度(宽度和高度)。它通过在一个窗口(例如 2x2)内对每个特征图取最大值来实现,有效地对其进行下采样(downsampling)。这有助于使表示对微小的平移更具鲁棒性,并降低计算复杂度。
  • Flatten 层(展平层):将卷积/池化层输出的多维数据转换为一维向量。在将数据输入到全连接层(Linear Layer)之前,这是必需的。
  • Linear 层(全连接层 Fully Connected Layer):标准的神经网络层,其中每个输入神经元都连接到每个输出神经元。在 ConvNets 中,这些层通常位于网络的末端,用于基于学习到的特征执行分类任务。

在 PyTorch 中定义一个简单的 ConvNet

Section titled “在 PyTorch 中定义一个简单的 ConvNet”

让我们使用 torch.nn.Module 定义一个用于图像分类的简单 ConvNet:

import torch
import torch.nn as nn
import torch.nn.functional as F # Contains activation functions and loss functions
# 包含激活函数和损失函数
class SimpleConvNet(nn.Module):
def __init__(self, num_classes=10): # Assuming 10 classes for output (e.g., MNIST digits)
# 假设输出有 10 个类别(例如 MNIST 数字)
super(SimpleConvNet, self).__init__()
# 输入图像:对于 MNIST 是 (批量大小 Batch_size, 1, 28, 28)(1 个颜色通道,28x28 像素)
# Convolutional Layer 1
# 卷积层 1
# Input channels = 1, Output channels = 16, Kernel size = 3, Stride = 1, Padding = 1
# 输入通道数 = 1,输出通道数 = 16,卷积核大小 = 3,步长 (Stride) = 1,填充 (Padding) = 1
self.conv1 = nn.Conv2d(in_channels=1, out_channels=16, kernel_size=3, stride=1, padding=1)
# Output shape: (Batch_size, 16, 28, 28)
# 输出形状:(批量大小 Batch_size, 16, 28, 28)
# After ReLU: same shape
# 经过 ReLU 后:形状相同
# Max Pooling Layer 1
# 最大池化层 1
# Kernel size = 2, Stride = 2
# 核大小 = 2,步长 = 2
self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)
# Output shape: (Batch_size, 16, 14, 14)
# 输出形状:(批量大小 Batch_size, 16, 14, 14)
# Convolutional Layer 2
# 卷积层 2
self.conv2 = nn.Conv2d(in_channels=16, out_channels=32, kernel_size=3, stride=1, padding=1)
# Output shape: (Batch_size, 32, 14, 14)
# 输出形状:(批量大小 Batch_size, 32, 14, 14)
# After ReLU: same shape
# 经过 ReLU 后:形状相同
# Max Pooling Layer 2
# 最大池化层 2
self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)
# Output shape: (Batch_size, 32, 7, 7)
# 输出形状:(批量大小 Batch_size, 32, 7, 7)
# Flatten the output for the fully connected layer
# 展平输出以用于全连接层
# The input features to fc1 will be 32 * 7 * 7
# 输入到 fc1 层的特征数量将是 32 * 7 * 7
self.flatten = nn.Flatten()
# Fully Connected Layer
# 全连接层
self.fc1 = nn.Linear(32 * 7 * 7, num_classes)
def forward(self, x):
x = self.conv1(x)
x = F.relu(x)
x = self.pool1(x)
x = self.conv2(x)
x = F.relu(x)
x = self.pool2(x)
x = self.flatten(x) # Flatten the tensor
# 展平张量
# Or manually: x = x.view(-1, 32 * 7 * 7)
# 或者手动 reshape: x = x.view(-1, 32 * 7 * 7)
x = self.fc1(x)
# For classification with CrossEntropyLoss, raw logits are usually returned.
# 对于使用 CrossEntropyLoss 进行分类,通常返回原始 logits (预测分数)。
# Softmax is often included in the loss function itself.
# Softmax 通常包含在损失函数本身中。
return x
# Example instantiation
# 示例实例化
# model = SimpleConvNet(num_classes=10)

训练 ConvNet 涉及迭代数据集,执行前向传播(forward pass)和反向传播(backward pass),以及更新模型权重(updating model weights)。下面是一个概念性的训练/验证函数。在实际场景中,您还需要定义一个 DataLoader,一个优化器(optimizer,例如 torch.optim.Adam),以及一个判据(criterion,即损失函数 loss function,例如 nn.CrossEntropyLoss)。

def train_model_epoch(model, data_loader, criterion, optimizer, device, phase='training'):
running_loss = 0.0
running_corrects = 0
if phase == 'training':
model.train() # Set model to training mode
# 将模型设置为训练模式
else: # 'validation' or 'testing'
model.eval() # Set model to evaluate mode
# 将模型设置为评估模式
for inputs, labels in data_loader:
inputs = inputs.to(device) # Move data to the device (CPU or GPU)
# 将数据移动到设备(CPU 或 GPU)
labels = labels.to(device)
# Zero the parameter gradients (only in training phase)
# 将参数的梯度清零(仅在训练阶段)
if phase == 'training':
optimizer.zero_grad()
# Forward pass
# 前向传播
# Track history only if in training phase
# 仅在训练阶段跟踪历史记录(用于计算梯度)
with torch.set_grad_enabled(phase == 'training'):
outputs = model(inputs)
loss = criterion(outputs, labels)
_, preds = torch.max(outputs, 1) # Get the index of the max log-probability (predicted class)
# 获取最大对数概率的索引(预测的类别)
# Backward pass + optimize only if in training phase
# 仅在训练阶段进行反向传播 + 优化
if phase == 'training':
loss.backward() # Compute gradients
# 计算梯度
optimizer.step() # Update weights
# 更新权重
# Statistics
# 统计信息
running_loss += loss.item() * inputs.size(0) # loss.item() gives scalar loss, inputs.size(0) is batch size
# loss.item() 返回标量损失值,inputs.size(0) 是当前批次的大小
running_corrects += torch.sum(preds == labels.data)
epoch_loss = running_loss / len(data_loader.dataset)
epoch_acc = running_corrects.double() / len(data_loader.dataset)
print(f'{phase.capitalize()} Loss: {epoch_loss:.4f} Acc: {epoch_acc:.4f}')
return epoch_loss, epoch_acc
# To use this function, you would typically loop through epochs:
# 要使用此函数,通常会通过循环遍历 epoch:
# num_epochs = 25
# for epoch in range(num_epochs):
# print(f'Epoch {epoch}/{num_epochs-1}')
# print('-' * 10)
# train_loss, train_acc = train_model_epoch(model, train_loader, criterion, optimizer, device, phase='training')
# val_loss, val_acc = train_model_epoch(model, val_loader, criterion, optimizer, device, phase='validation')

此 train_model_epoch 函数处理训练和验证两个阶段。主要区别在于:

  • model.train() vs model.eval():这些方法将模型设置为训练模式或评估模式。这对于 Dropout 和 BatchNorm 等层非常重要,它们在训练期间(例如,应用 Dropout,更新运行中的统计数据)和推理期间(例如,禁用 Dropout,使用固定的统计数据)行为不同。
  • 梯度计算(Gradient Calculation):梯度(gradient)的计算(loss.backward())以及权重的更新(optimizer.step())仅在训练阶段进行。在验证/测试期间,使用 torch.set_grad_enabled(False) 或 with torch.no_grad(): 来禁用梯度计算,从而节省内存和计算资源。

对于一个完整的示例,您需要设置您的数据(例如,使用 torchvision.datasets 和 DataLoader),实例化(instantiate)您的模型、优化器和损失函数,然后在 epoch 循环中调用 train_model_epoch 函数。