type
status
date
slug
summary
tags
category
icon
password
在掌握了神经网络的基本结构之后,我们将深入探讨那些赋予神经网络“智能”的核心组件,包括激活函数、参数初始化策略、损失函数的设计、反向传播的优化,以及如何防止模型“学跑偏”的正则化技巧。最后,我们会通过一个完整的项目实战来巩固所学知识。
一、激活函数:赋予网络非线性能力
激活函数是神经网络的灵魂,它负责在神经元的输出端引入非线性能力。如果没有激活函数,无论神经网络有多少层,其本质都只是一个线性模型,无法解决复杂的现实问题。
1.1 Sigmoid
Sigmoid 函数能将任意实数输入映射到 (0, 1) 区间,常用于二分类任务的输出层,因为它能直观地解释为概率。
- 核心原理:
- 主要问题:
- 梯度消失:当输入值过大或过小时,其导数接近于 0,导致深层网络在反向传播时梯度更新缓慢。
- 计算成本高:包含指数运算。
- 实践建议:由于梯度消失问题,现在已很少在隐藏层使用 Sigmoid,主要用于二分类输出层。
1.2 Tanh(双曲正切)
Tanh 函数将输入映射到 (-1, 1) 区间,输出以 0 为中心,这在一定程度上优于 Sigmoid,有助于加速模型收敛。
- 核心原理:
- 主要问题:仍然存在梯度消失问题,只是比 Sigmoid 稍好。
- 实践建议:在某些场景下可以作为 Sigmoid 的替代品,但同样不建议在深层网络的隐藏层中大规模使用。
1.3 ReLU(修正线性单元)
ReLU 是目前深度学习中最常用的激活函数,因其简单高效而广受欢迎。
- 核心原理:
- 优点:
- 计算极其简单,训练速度快。
- 在正数区间的导数恒为 1,有效缓解了梯度消失问题。
- 引入稀疏性,让部分神经元输出为 0,降低了计算量。
- 主要问题:“神经元死亡”(Dying ReLU),即如果一个神经元的输入恒为负,它将永远不会被激活。
1.4 LeakyReLU
LeakyReLU 是 ReLU 的改进版,旨在解决“神经元死亡”问题。
- 核心原理:为负数区间的输入赋予一个微小的正斜率 (α)。
- 优点:避免了神经元完全不激活的情况,允许负值信息通过。
- 实践建议:当发现 ReLU 效果不佳或怀疑存在大量“死亡”神经元时,可以尝试使用 LeakyReLU。
1.5 Softmax
Softmax 主要用于多分类问题的输出层,它将一组实数转换为概率分布,所有输出值的和为 1。
- 核心原理:对每个输出值取指数,然后用该值除以所有输出值的指数之和。
- 特点:
- 将输出转换为概率分布,便于理解和决策。
- 放大差异,最大的输入值对应的概率会更突出。
- 实践建议:多分类任务的输出层标准选择,通常与交叉熵损失函数配合使用。
1.6 如何选择激活函数?
- 隐藏层:
- 首选 ReLU:它简单、高效,是绝大多数情况下的第一选择。
- 备选 LeakyReLU:如果 ReLU 效果不佳,可以尝试 LeakyReLU 或其变体。
- 尽量避免 Sigmoid:它容易导致梯度消失,拖慢训练。
- 输出层:
- 二分类问题:使用 Sigmoid。
- 多分类问题:使用 Softmax。
- 回归问题:通常 不使用 激活函数。
二、参数初始化:决定训练的起点
正确的参数初始化对模型的训练速度和最终性能至关重要。糟糕的初始化可能导致梯度消失/爆炸,使模型难以收敛。
nn.init
模块:PyTorch 在torch.nn.init
中提供了多种初始化方法。
- 目标:打破对称性(各神经元学习不同特征)、保持输入输出方差一致。
2.1 固定值初始化(不推荐)
如全零、全一或固定常数初始化。会导致所有神经元学习到完全相同的特征(对称性问题),模型无法学习。
2.2 随机初始化
从正态分布或均匀分布中随机采样,打破对称性。这是最基本的有效方法,但可能导致梯度不稳定。
2.3 Xavier 初始化 (Glorot 初始化)
一种自适应初始化方法,根据输入和输出神经元的数量调整权重方差,使各层输出的方差保持一致,以缓解梯度问题。
- 核心思想:
- 适用场景:在 Sigmoid 和 Tanh 激活函数下表现良好。
2.4 He 初始化 (Kaiming 初始化)
专为 ReLU 及其变体设计的初始化方法。考虑到 ReLU 会将一半的输入置零,He 初始化相应地调整了权重的方差。
- 核心思想:
- 适用场景:当隐藏层使用 ReLU 或 LeakyReLU 时,He 初始化是首选。
三、损失函数:衡量模型的预测差距
损失函数(Loss Function)用于量化模型预测值与真实值之间的差异。训练的目标就是通过优化算法(如梯度下降)最小化这个损失。
3.1 回归任务损失函数
- MAE (L1-Loss):平均绝对误差,对异常值不敏感(鲁棒性好)。
- MSE (L2-Loss):均方误差,对较大误差的惩罚更重,数学性质好,是回归任务中最常用的损失函数。
3.2 分类任务损失函数
- CrossEntropyLoss:交叉熵损失,是多分类任务的标准选择。
- 重要提示:PyTorch 的
nn.CrossEntropyLoss
内置了 Softmax 操作。因此,模型的最后一层不应再接 Softmax 激活函数。
- BCELoss:二元交叉熵损失,专用于二分类任务。
- 重要提示:使用
nn.BCELoss
时,模型的输出 必须 先经过 Sigmoid 激活函数。为了数值稳定性,更推荐使用nn.BCEWithLogitsLoss
,它将 Sigmoid 和 BCELoss 合二为一。
四、反向传播与优化器:如何更新模型参数
反向传播(Backpropagation, BP)是训练神经网络的核心算法,它计算损失函数对每个参数的梯度。而优化器(Optimizer)则根据这些梯度来更新参数,以最小化损失。
4.1 梯度下降基础
- 批量梯度下降 (BGD):使用整个数据集计算梯度,方向准确但速度慢,内存消耗大。
- 随机梯度下降 (SGD):每次只用一个样本计算梯度,速度快但更新不稳定,容易震荡。
- 小批量梯度下降 (MBGD):BGD 和 SGD 的折中,是目前的主流选择。它在每次更新时使用一小批数据(batch)。
4.2 常用优化器
- Momentum:在 SGD 基础上引入“动量”(历史梯度的指数加权平均),帮助“冲过”局部最小值或鞍点,加速收敛。
- AdaGrad:为每个参数自适应调整学习率,对稀疏数据友好,但学习率会随时间单调递减,可能导致训练后期停滞。
- RMSProp:AdaGrad 的改进版,使用梯度的指数加权移动平均而非累积,解决了学习率过早衰减的问题。
- Adam:当前最流行的优化器之一。它结合了 Momentum 和 RMSProp 的优点,既有动量加速,又能自适应调整学习率,适用于绝大多数场景。
五、正则化:防止模型过拟合
过拟合指模型在训练集上表现优异,但在未见过的新数据(测试集)上表现很差。正则化是解决过拟合的常用技术。
5.1 L1 和 L2 正则化
通过在损失函数中添加对权重的惩罚项来限制模型复杂度。
- L2 正则化 (权重衰减):惩罚项为权重的平方和,使权重值趋向于较小但非零的值,使模型更平滑。在 PyTorch 的优化器中通过
weight_decay
参数实现。
- L1 正则化:惩罚项为权重的绝对值和,倾向于将不重要的特征权重压缩至零,从而实现特征选择,产生稀疏模型。需要手动实现。
5.2 Dropout
在训练过程中,以一定概率
p
随机“丢弃”(即置零)一部分神经元的输出。这能强制网络学习更鲁棒的特征,减少神经元之间的协同适应。- 核心组件:
nn.Dropout
用于全连接层,nn.Dropout2d
用于卷积层。
- 注意:Dropout 只在训练时生效 (
model.train()
),在评估时会自动关闭 (model.eval()
)。
5.3 数据增强
通过对训练数据进行随机变换(如旋转、裁剪、翻转、颜色抖动等)来创造更多样的训练样本,是提高模型泛化能力、降低过拟合风险的有效手段。
- 核心模块:
torchvision.transforms
- 实践:通常使用
transforms.Compose
将多个变换组合成一个处理流水线。
六、批量标准化 (Batch Normalization)
Batch Normalization (BN) 是一种强大的正则化技术,通过对每一批数据的每个特征通道进行标准化,来解决“内部协变量偏移”问题。
- 位置:通常放在全连接层或卷积层之后,激活函数之前。
- 作用:
- 加速网络训练,允许使用更大的学习率。
- 提高模型稳定性,减少对参数初始化的依赖。
- 提供轻微的正则化效果。
- 核心组件:
nn.BatchNorm1d
,nn.BatchNorm2d
。
七、模型的保存与加载
训练好的模型需要被保存,以便后续部署、评估或继续训练。
- 推荐方法:只保存模型的状态字典(
state_dict
),它包含了模型的所有可学习参数(权重和偏置)。这种方式轻量且灵活。
- 不推荐方法:保存整个模型对象。虽然简单,但代码依赖性强,可能在不同环境中加载失败。
八、项目实战:用全连接网络训练和验证 CIFAR10 数据集
- 作者:sisui
- 链接:https://www.sisui.me//article/pytorch-fcnn-core-concepts-for-beginners
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
相关文章