5层全连接前馈神经网络入门指南:从理论到PyTorch实战

1次阅读
没有评论

共计 3788 个字符,预计需要花费 10 分钟才能阅读完成。

image.webp

前馈神经网络简介

前馈神经网络(Feedforward Neural Network)是深度学习中最基础的网络结构之一,它的数据流向是单向的,从输入层经过隐藏层最终到达输出层。这种网络结构在图像分类、语音识别、推荐系统等领域有着广泛的应用。

5 层全连接前馈神经网络入门指南:从理论到 PyTorch 实战

相比于更复杂的网络结构(如 CNN、RNN),前馈神经网络结构简单,非常适合初学者理解神经网络的基本原理和工作机制。

3 层 vs 5 层网络性能对比

  1. 表达能力差异:理论上,5 层网络比 3 层网络具有更强的函数逼近能力。根据通用近似定理,具有单个隐藏层的网络就能近似任何函数,但更深的网络可以用更少的神经元达到相同的近似效果。

  2. 训练难度增加

  3. 梯度消失问题:在深层网络中,反向传播时梯度会逐层衰减,导致前面层参数更新缓慢
  4. 训练时间更长:更多层意味着更多参数和更长的计算时间
  5. 更容易过拟合:模型复杂度增加可能导致对训练数据的过度拟合

  6. 实际表现:对于简单任务,3 层网络可能就足够;但对于复杂任务,5 层网络通常能获得更好的性能。

PyTorch 实现 5 层全连接网络

网络结构定义

import torch
import torch.nn as nn
import torch.nn.init as init

class FiveLayerNN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(FiveLayerNN, self).__init__()
        # 输入层到第一隐藏层
        self.fc1 = nn.Linear(input_size, hidden_size)
        # 隐藏层之间连接
        self.fc2 = nn.Linear(hidden_size, hidden_size)
        self.fc3 = nn.Linear(hidden_size, hidden_size)
        # 最后一层到输出层
        self.fc4 = nn.Linear(hidden_size, output_size)

        # 初始化权重
        self.init_weights()

        # ReLU 激活函数
        self.relu = nn.ReLU()

    def init_weights(self):
        # Xavier/Glorot 初始化
        for m in self.modules():
            if isinstance(m, nn.Linear):
                init.xavier_uniform_(m.weight)
                init.zeros_(m.bias)

    def forward(self, x):
        x = self.relu(self.fc1(x))
        x = self.relu(self.fc2(x))
        x = self.relu(self.fc3(x))
        x = self.fc4(x)  # 输出层通常不加激活函数
        return x

维度设计说明

  1. 输入层维度:取决于你的数据特征数量。例如,对于 MNIST 手写数字识别,输入维度是 784(28×28 像素展开)。

  2. 隐藏层维度:通常选择 2 的幂次方(如 64、128、256 等),这是为了 GPU 计算效率。隐藏层之间通常保持相同维度。

  3. 输出层维度:由任务决定。分类任务中等于类别数,回归任务中等于输出变量数。

完整训练流程

数据准备

from torchvision import datasets, transforms
from torch.utils.data import DataLoader

# 数据转换
transform = transforms.Compose([transforms.ToTensor(),
    transforms.Normalize((0.5,), (0.5,))
])

# 加载 MNIST 数据集
train_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
test_dataset = datasets.MNIST(root='./data', train=False, download=True, transform=transform)

# 创建数据加载器
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False)

训练循环

import matplotlib.pyplot as plt

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = FiveLayerNN(784, 256, 10).to(device)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

# 记录训练过程
train_losses = []
test_accuracies = []

epochs = 10
for epoch in range(epochs):
    model.train()
    running_loss = 0.0

    for images, labels in train_loader:
        images = images.view(-1, 784).to(device)
        labels = labels.to(device)

        # 前向传播
        outputs = model(images)
        loss = criterion(outputs, labels)

        # 反向传播和优化
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        running_loss += loss.item()

    # 记录训练损失
    train_loss = running_loss / len(train_loader)
    train_losses.append(train_loss)

    # 验证集评估
    model.eval()
    correct = 0
    total = 0

    with torch.no_grad():
        for images, labels in test_loader:
            images = images.view(-1, 784).to(device)
            labels = labels.to(device)
            outputs = model(images)
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()

    accuracy = 100 * correct / total
    test_accuracies.append(accuracy)

    print(f'Epoch [{epoch+1}/{epochs}], Loss: {train_loss:.4f}, Accuracy: {accuracy:.2f}%')

# 可视化训练过程
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(train_losses, label='Training Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(test_accuracies, label='Test Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy (%)')
plt.legend()
plt.show()

避坑指南

学习率设置

  1. 常见问题:学习率太大导致震荡不收敛,太小导致训练过慢

  2. 经验值

  3. Adam 优化器:1e- 3 到 1e-5
  4. SGD 优化器:0.1 到 0.001
  5. 可以先用一个中等值(如 0.001),观察训练情况再调整

  6. 学习率调度 :可以使用torch.optim.lr_scheduler 实现动态调整

Batch Size 选择

  1. 大 Batch Size(如 256+):
  2. 训练更稳定
  3. 计算效率更高
  4. 可能收敛到较差的局部最优

  5. 小 Batch Size(如 32-64):

  6. 引入更多噪声,可能有助于找到更好的解
  7. 需要更小的学习率
  8. 训练时间更长

  9. 建议:从 64 或 128 开始尝试,根据 GPU 内存调整

过拟合应对

  1. 识别过拟合:训练准确率远高于验证准确率

  2. 解决方法

  3. 增加数据量(数据增强)
  4. 使用 Dropout 层
  5. L2 正则化(权重衰减)
  6. 早停(Early Stopping)
  7. 减少网络复杂度

  8. Dropout 实现

    self.dropout = nn.Dropout(0.5)  # 添加到__init__中
    
    # 在 forward 中使用
    x = self.dropout(self.relu(self.fc1(x)))

进阶思考

  1. 尝试在不同隐藏层使用不同大小的神经元数量,观察对性能的影响

  2. 将 ReLU 激活函数替换为 LeakyReLU 或 Sigmoid,比较训练效果

  3. 实验不同的权重初始化方法(如 Kaiming 初始化),分析收敛速度的变化

总结

通过本文,我们系统地学习了 5 层全连接前馈神经网络的原理和实现。从网络结构设计、PyTorch 实现到训练技巧,涵盖了构建深度神经网络的关键知识点。虽然全连接网络在图像等结构化数据上不如 CNN 高效,但它仍然是理解深度学习基础的重要模型。希望读者能通过实践加深理解,并尝试将其应用到自己的项目中。

正文完
 0
评论(没有评论)