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 torchimport torch.nn as nnimport 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()vsmodel.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 函数。