本文参考了周志华老师的《机器学习》(俗称“西瓜书”)。这里是 第五章“神经网络” 的阅读笔记。本文专注于前馈神经网络,并且记录了我的思考,希望对你有所帮助🎉


基础知识

1 前馈神经网络的定义
前馈神经网络是一种基础的人工神经网络结构,其特点是信息沿单方向从输入层传递到输出层,且没有循环或反馈。这种网络通常用于分类和回归任务。

关键特性

2 网络结构
前馈神经网络的结构包括三个基本部分:输入层、隐藏层和输出层。

  1. 输入层

    • 接收原始数据。
    • 每个神经元对应输入数据的一个特征。
  2. 隐藏层

    • 通过加权求和和激活函数对输入数据进行非线性变换。
    • 隐藏层数量和神经元数量是网络设计的重要参数。
  3. 输出层

    • 生成最终结果。
    • 分类问题中通常使用Softmax或Sigmoid激活函数。
    • 回归问题中可以是线性输出。

神经元模型

神经网络的架构

1 前馈神经网络的常见结构

  1. 单层前馈神经网络

    • 仅包含一个隐藏层。
    • 适合简单任务,非线性表达能力有限。
  2. 多层前馈神经网络(MLP)

    • 包含多个隐藏层。
    • 可以学习更复杂的特征表示。

4.2 重要设计原则

  1. 层数与神经元数量

    • 层数越多,模型的表达能力越强,但可能导致过拟合。
    • 隐藏层神经元数量需平衡复杂性和计算成本。
  2. 激活函数选择

    • ReLU 是目前使用最广泛的激活函数,因其计算简单且缓解梯度消失问题。
  3. 权重初始化

    • 使用均匀分布或正态分布随机初始化。
    • Xavier 初始化或 He 初始化常用于深层网络。

3 前馈神经网络的实现
以下是一个简单的实现示例(Python + PyTorch):

import torch
import torch.nn as nn
import torch.optim as optim

# 定义前馈神经网络
class FeedforwardNN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(FeedforwardNN, self).__init__()
        self.fc1 = nn.Linear(input_size, hidden_size)
        self.relu = nn.ReLU()
        self.fc2 = nn.Linear(hidden_size, output_size)
        self.softmax = nn.Softmax(dim=1)

    def forward(self, x):
        x = self.fc1(x)
        x = self.relu(x)
        x = self.fc2(x)
        x = self.softmax(x)
        return x

# 创建网络
input_size = 4
hidden_size = 16
output_size = 3
model = FeedforwardNN(input_size, hidden_size, output_size)

# 损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.01)

# 训练过程(示例)
inputs = torch.randn(10, input_size)  # 随机生成输入
labels = torch.randint(0, output_size, (10,))  # 随机生成标签

for epoch in range(100):
    optimizer.zero_grad()
    outputs = model(inputs)
    loss = criterion(outputs, labels)
    loss.backward()
    optimizer.step()

print("训练完成!")

头脑风暴

问题 1:前馈神经网络的局限性是什么?如何克服这些局限性?

前馈神经网络存在以下局限性:

  1. 无法处理序列数据或时间相关数据

    • 前馈神经网络是静态模型,无法捕捉输入数据的时间依赖性或上下文信息。
    • 解决方案:可以使用循环神经网络(RNN)或长短时记忆网络(LSTM)来处理时间序列数据。
  2. 参数数量庞大,容易导致过拟合

    • FNN 的每一层都与上一层完全连接,当网络层数或神经元数量较多时,参数量会迅速膨胀。
    • 解决方案
      • 使用正则化(如 L1、L2 正则化)。
      • 采用 Dropout 等方法来随机丢弃部分神经元,防止模型过度拟合训练数据。
  3. 对高维输入的特征提取能力有限

    • 前馈神经网络缺乏针对特定任务(如图像分类)的结构化设计。
    • 解决方案:在图像处理任务中,可以使用卷积神经网络(CNN),通过局部连接和权重共享机制减少参数量并提高性能。
  4. 梯度消失问题

    • 当网络较深时,梯度可能会在反向传播中逐层缩小,导致参数无法有效更新。
    • 解决方案
      • 使用 ReLU 或 Leaky ReLU 替代 Sigmoid 激活函数。
      • 使用梯度裁剪或采用更深层次的残差网络(ResNet)。

问题 2:如何选择前馈神经网络的架构(层数和每层的神经元数量)?

选择前馈神经网络的架构是一个经验性过程,需要根据具体问题和数据特点进行调整。以下是一些指导原则:

  1. 层数的选择

    • 较少的层数适合简单问题,例如线性可分问题。
    • 更深的网络适合复杂问题,但可能需要更多数据和计算资源。
    • 一般从 1-3 层开始,逐步增加,观察验证集上的性能。
  2. 每层神经元数量的选择

    • 输入层神经元数量应等于输入特征的维度。
    • 输出层神经元数量取决于任务:
      • 分类问题:与类别数量一致。
      • 回归问题:通常为 1。
    • 隐藏层的神经元数量可以从输入和输出层数量的平均值开始尝试,然后通过实验调整。
  3. 试验与调整

    • 使用网格搜索或随机搜索超参数优化方法,系统地尝试不同的架构。
    • 可以通过交叉验证选择性能最优的架构。
  4. 规则与经验

    • 遵循“少量神经元的浅层网络优于过深或过宽的网络”的原则。
    • 避免过度复杂的架构导致过拟合。

问题 3:为什么激活函数对前馈神经网络如此重要?如何选择合适的激活函数?

激活函数决定了神经网络的非线性表达能力,是前馈神经网络的核心组件。

  1. 重要性

    • 如果没有激活函数,前馈神经网络的每一层只能进行线性变换,整体上等效于单层网络,无法拟合非线性问题。
    • 激活函数引入非线性,使网络能够拟合复杂的决策边界和特征模式。
  2. 选择激活函数的关键考虑

    • ReLU(Rectified Linear Unit):
      • 优点:简单计算,缓解梯度消失问题。
      • 缺点:可能导致神经元“死亡”(梯度恒为 0)。
      • 使用场景:隐藏层的默认选择。
    • Sigmoid
      • 优点:输出范围在 (0,1),适合概率输出。
      • 缺点:容易导致梯度消失,计算较慢。
      • 使用场景:输出层的二分类问题。
    • Tanh
      • 优点:输出范围在 (-1,1),中心对称。
      • 缺点:同样面临梯度消失问题。
      • 使用场景:隐藏层的非线性变换(较少使用)。
    • Softmax
      • 优点:将输出转化为概率分布。
      • 使用场景:多分类问题的输出层。

文章参考