本文参考了周志华老师的《机器学习》(俗称“西瓜书”)。这里是 第五章“神经网络” 的阅读笔记。本文专注于前馈神经网络,并且记录了我的思考,希望对你有所帮助🎉
基础知识
1 前馈神经网络的定义
前馈神经网络是一种基础的人工神经网络结构,其特点是信息沿单方向从输入层传递到输出层,且没有循环或反馈。这种网络通常用于分类和回归任务。
关键特性:
- 信息只沿“前向”传播。
- 没有循环或时间依赖性。
- 适用于静态数据处理任务。
2 网络结构
前馈神经网络的结构包括三个基本部分:输入层、隐藏层和输出层。
-
输入层:
- 接收原始数据。
- 每个神经元对应输入数据的一个特征。
-
隐藏层:
- 通过加权求和和激活函数对输入数据进行非线性变换。
- 隐藏层数量和神经元数量是网络设计的重要参数。
-
输出层:
- 生成最终结果。
- 分类问题中通常使用Softmax或Sigmoid激活函数。
- 回归问题中可以是线性输出。
神经元模型
- 输入:( x_1, x_2, ..., x_n ) 为输入特征。
- 权重:( w_1, w_2, ..., w_n ) 为每个输入的权重。
- 偏置:( b ) 为一个可学习的常数项。
- 激活函数:将线性组合的结果转换为非线性输出。
神经网络的架构
1 前馈神经网络的常见结构
-
单层前馈神经网络:
- 仅包含一个隐藏层。
- 适合简单任务,非线性表达能力有限。
-
多层前馈神经网络(MLP):
- 包含多个隐藏层。
- 可以学习更复杂的特征表示。
4.2 重要设计原则
-
层数与神经元数量:
- 层数越多,模型的表达能力越强,但可能导致过拟合。
- 隐藏层神经元数量需平衡复杂性和计算成本。
-
激活函数选择:
- ReLU 是目前使用最广泛的激活函数,因其计算简单且缓解梯度消失问题。
-
权重初始化:
- 使用均匀分布或正态分布随机初始化。
- 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:前馈神经网络的局限性是什么?如何克服这些局限性?
前馈神经网络存在以下局限性:
-
无法处理序列数据或时间相关数据:
- 前馈神经网络是静态模型,无法捕捉输入数据的时间依赖性或上下文信息。
- 解决方案:可以使用循环神经网络(RNN)或长短时记忆网络(LSTM)来处理时间序列数据。
-
参数数量庞大,容易导致过拟合:
- FNN 的每一层都与上一层完全连接,当网络层数或神经元数量较多时,参数量会迅速膨胀。
- 解决方案:
- 使用正则化(如 L1、L2 正则化)。
- 采用 Dropout 等方法来随机丢弃部分神经元,防止模型过度拟合训练数据。
-
对高维输入的特征提取能力有限:
- 前馈神经网络缺乏针对特定任务(如图像分类)的结构化设计。
- 解决方案:在图像处理任务中,可以使用卷积神经网络(CNN),通过局部连接和权重共享机制减少参数量并提高性能。
-
梯度消失问题:
- 当网络较深时,梯度可能会在反向传播中逐层缩小,导致参数无法有效更新。
- 解决方案:
- 使用 ReLU 或 Leaky ReLU 替代 Sigmoid 激活函数。
- 使用梯度裁剪或采用更深层次的残差网络(ResNet)。
问题 2:如何选择前馈神经网络的架构(层数和每层的神经元数量)?
选择前馈神经网络的架构是一个经验性过程,需要根据具体问题和数据特点进行调整。以下是一些指导原则:
-
层数的选择:
- 较少的层数适合简单问题,例如线性可分问题。
- 更深的网络适合复杂问题,但可能需要更多数据和计算资源。
- 一般从 1-3 层开始,逐步增加,观察验证集上的性能。
-
每层神经元数量的选择:
- 输入层神经元数量应等于输入特征的维度。
- 输出层神经元数量取决于任务:
- 分类问题:与类别数量一致。
- 回归问题:通常为 1。
- 隐藏层的神经元数量可以从输入和输出层数量的平均值开始尝试,然后通过实验调整。
-
试验与调整:
- 使用网格搜索或随机搜索超参数优化方法,系统地尝试不同的架构。
- 可以通过交叉验证选择性能最优的架构。
-
规则与经验:
- 遵循“少量神经元的浅层网络优于过深或过宽的网络”的原则。
- 避免过度复杂的架构导致过拟合。
问题 3:为什么激活函数对前馈神经网络如此重要?如何选择合适的激活函数?
激活函数决定了神经网络的非线性表达能力,是前馈神经网络的核心组件。
-
重要性:
- 如果没有激活函数,前馈神经网络的每一层只能进行线性变换,整体上等效于单层网络,无法拟合非线性问题。
- 激活函数引入非线性,使网络能够拟合复杂的决策边界和特征模式。
-
选择激活函数的关键考虑:
- ReLU(Rectified Linear Unit):
- 优点:简单计算,缓解梯度消失问题。
- 缺点:可能导致神经元“死亡”(梯度恒为 0)。
- 使用场景:隐藏层的默认选择。
- Sigmoid:
- 优点:输出范围在 (0,1),适合概率输出。
- 缺点:容易导致梯度消失,计算较慢。
- 使用场景:输出层的二分类问题。
- Tanh:
- 优点:输出范围在 (-1,1),中心对称。
- 缺点:同样面临梯度消失问题。
- 使用场景:隐藏层的非线性变换(较少使用)。
- Softmax:
- 优点:将输出转化为概率分布。
- 使用场景:多分类问题的输出层。
- ReLU(Rectified Linear Unit):
文章参考
- 《机器学习(西瓜书)》