多层感知机

隐藏层

在多层感知机(Multilayer Perceptron, MLP)中,隐藏层(Hidden Layer)是指位于输入层和输出层之间的神经元层。多层感知机是一种前馈神经网络,通常由多个隐藏层组成,每个隐藏层由多个神经元组成。

隐藏层的作用是对输入数据进行非线性映射和特征提取,使得神经网络能够学习到复杂的模式和关系。每个隐藏层中的每个神经元接收来自上一层(或者输入层)的输入,通过加权和和激活函数处理后输出给下一层(或者输出层)。

具体来说,多层感知机的运作过程如下:

  1. 输入层接收原始数据或特征向量作为输入。
  2. 隐藏层接收来自输入层的输入,对其进行线性加权和,并通过一个非线性的激活函数(如ReLU、sigmoid等)进行转换,输出给下一层或输出层。
  3. 输出层接收最后一个隐藏层的输出,进行最终的处理和分类,输出预测结果。

隐藏层的引入使得多层感知机能够处理复杂的非线性问题,并且通过调整隐藏层的神经元数量和结构,可以增加神经网络的表达能力,提高其对数据的建模能力和预测精度。

线性模型可能会出错

例如,线性意味着单调假设: 任何特征的增大都会导致模型输出的增大(如果对应的权重为正), 或者导致模型输出的减小(如果对应的权重为负)。 有时这是有道理的。 例如,如果我们试图预测一个人是否会偿还贷款。 我们可以认为,在其他条件不变的情况下, 收入较高的申请人比收入较低的申请人更有可能偿还贷款。 但是,虽然收入与还款概率存在单调性,但它们不是线性相关的。 收入从0增加到5万,可能比从100万增加到105万带来更大的还款可能性。 处理这一问题的一种方法是对我们的数据进行预处理, 使线性变得更合理,如使用收入的对数作为我们的特征。

然而我们可以很容易找出违反单调性的例子。 例如,我们想要根据体温预测死亡率。 对体温高于37摄氏度的人来说,温度越高风险越大。 然而,对体温低于37摄氏度的人来说,温度越高风险就越低。 在这种情况下,我们也可以通过一些巧妙的预处理来解决问题。 例如,我们可以使用与37摄氏度的距离作为特征。

但是,如何对猫和狗的图像进行分类呢? 增加位置(13,17)处像素的强度是否总是增加(或降低)图像描绘狗的似然? 对线性模型的依赖对应于一个隐含的假设, 即区分猫和狗的唯一要求是评估单个像素的强度。 在一个倒置图像后依然保留类别的世界里,这种方法注定会失败。

与我们前面的例子相比,这里的线性很荒谬, 而且我们难以通过简单的预处理来解决这个问题。 这是因为任何像素的重要性都以复杂的方式取决于该像素的上下文(周围像素的值)。 我们的数据可能会有一种表示,这种表示会考虑到我们在特征之间的相关交互作用。 在此表示的基础上建立一个线性模型可能会是合适的, 但我们不知道如何手动计算这么一种表示。 对于深度神经网络,我们使用观测数据来联合学习隐藏层表示和应用于该表示的线性预测器。

在网络中加入隐藏层

我们可以通过在网络中加入一个或多个隐藏层来克服线性模型的限制, 使其能处理更普遍的函数关系类型。 要做到这一点,最简单的方法是将许多全连接层堆叠在一起。 每一层都输出到上面的层,直到生成最后的输出。 我们可以把前𝐿−1层看作表示,把最后一层看作线性预测器。 这种架构通常称为多层感知机(multilayer perceptron),通常缩写为MLP。 下面,我们以图的方式描述了多层感知机

1

这个多层感知机有4个输入,3个输出,其隐藏层包含5个隐藏单元。 输入层不涉及任何计算,因此使用此网络产生输出只需要实现隐藏层和输出层的计算。 因此,这个多层感知机中的层数为2。 注意,这两个层都是全连接的。 每个输入都会影响隐藏层中的每个神经元, 而隐藏层中的每个神经元又会影响输出层中的每个神经元。

然而,具有全连接层的多层感知机的参数开销可能会高得令人望而却步。 即使在不改变输入或输出大小的情况下, 可能在参数节约和模型有效性之间进行权衡

从线性到非线性

在每一个隐藏层的输出后面使用一个激活函数,来引入非线性特性和增强模型的表达能力

如果没有激活函数,多层感知机就只是简单的线性变换堆叠,无法学习非线性函数

常见激活函数

Sigmoid函数(Logistic函数): 将输入映射到(0, 1)区间,主要用于输出层的二分类问题。
$$
\sigma(x) = \frac{1}{1 + e^{-x}}
$$
ReLU函数(Rectified Linear Unit): 对于正数部分输出其本身,负数部分输出0,通常在隐藏层中使用,有助于加速训练。
$$
\text{ReLU}(x) = \max(0, x)
$$
tanh函数(双曲正切函数): 将输入映射到(-1, 1)区间,与sigmoid函数类似,但输出范围更广泛,有时在隐藏层中使用。
$$
\tanh(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}}
$$

多层感知机的pytorch实现

依旧使用Fashion-MNIST图像分类数据集

1
2
3
import torch
from torch import nn
from d2l import torch as d2l

模型

与softmax回归的pytorch实现相比, 唯一的区别是我们添加了2个全连接层(之前我们只添加了1个全连接层)。 第一层是隐藏层,它包含256个隐藏单元,并使用了ReLU激活函数。 第二层是输出层。

1
2
3
4
5
6
7
8
9
10
net = nn.Sequential(nn.Flatten(),
nn.Linear(784, 256),
nn.ReLU(),
nn.Linear(256, 10))

def init_weights(m):
if type(m) == nn.Linear:
nn.init.normal_(m.weight, std=0.01)

net.apply(init_weights);

训练过程的实现与我们实现softmax回归时完全相同, 这种模块化设计使我们能够将与模型架构有关的内容独立出来。

1
2
3
4
5
6
batch_size, lr, num_epochs = 256, 0.1, 10
loss = nn.CrossEntropyLoss(reduction='none')
trainer = torch.optim.SGD(net.parameters(), lr=lr)

train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)

2

模型选择、欠拟合和过拟合

我们的目标是发现模式(pattern)。且模型应该是真正发现了一种泛化的模式, 而不是简单地记住了数据

将模型在训练数据上拟合的比在潜在分布中更接近的现象称为过拟合(overfitting), 用于对抗过拟合的技术称为正则化(regularization)

训练误差和泛化误差

训练误差(training error)是指, 模型在训练数据集上计算得到的误差。 泛化误差(generalization error)是指, 模型应用在同样从原始样本的分布中抽取的无限多数据样本时,模型误差的期望。

在实际中,我们只能通过将模型应用于一个独立的测试集来估计泛化误差, 该测试集由随机选取的、未曾在训练集中出现的数据样本构成。

过拟合、欠拟合

当训练误差和验证误差都很严重, 但它们之间仅有一点差距。 如果模型不能降低训练误差,这可能意味着模型过于简单(即表达能力不足), 无法捕获试图学习的模式。 此外,由于我们的训练和验证误差之间的泛化误差很小, 我们有理由相信可以用一个更复杂的模型降低训练误差。 这种现象被称为欠拟合(underfitting)。即因模型问题导致训练和泛化误差都很大

当我们的训练误差明显低于验证误差时, 这表明严重的过拟合(overfitting)

但是,过拟合并不总是一件坏事。 特别是在深度学习领域,众所周知, 最好的预测模型在训练数据上的表现往往比在保留(验证)数据上好得多。 最终,我们通常更关心验证误差,而不是训练误差和验证误差之间的差距。

是否过拟合或欠拟合可能取决于模型复杂性和可用训练数据集的大小

模型复杂性

高阶多项式函数比低阶多项式函数复杂得多。 高阶多项式的参数较多,模型函数的选择范围较广。 因此在固定训练数据集的情况下, 高阶多项式函数相对于低阶多项式的训练误差应该始终更低(最坏也是相等)。 事实上,当数据样本包含了𝑥的不同值时, 函数阶数等于数据样本数量的多项式函数可以完美拟合训练集。 在下图中, 我们直观地描述了多项式的阶数和欠拟合与过拟合之间的关系。

3

数据集大小

训练数据集中的样本越少,我们就越有可能(且更严重地)过拟合。 随着训练数据量的增加,泛化误差通常会减小。

权重衰减

权重衰减(Weight Decay)是一种在深度学习中广泛使用的正则化技术,旨在减少模型的过拟合现象。以下是关于权重衰减的详细解释:

定义

权重衰减通过在模型的损失函数中引入一个与权重参数的平方和成正比的惩罚项,来限制模型权重的大小,从而降低模型的复杂度。这种正则化技术能够有效地防止模型在训练数据上过拟合,并提高模型的泛化能力。

作用原理

  1. 降低模型复杂度:通过惩罚模型中的权重参数,使其趋向于较小的值,从而降低模型的复杂度。这有助于减少模型对训练数据的过度依赖,避免在测试数据上表现不佳。
  2. 平衡拟合能力与泛化能力:通过调整权重衰减的系数(正则化参数λ),可以平衡模型的拟合能力和泛化能力。较小的λ值可能导致模型对训练数据的拟合能力过强,而较大的λ值则可能使模型过于简单,无法充分捕捉数据的特征。
  3. 减少特征依赖性:权重衰减可以减少模型对特定特征的过度依赖,从而降低过拟合的风险。

实现方式

权重衰减的实现通常有两种方式:L1正则化和L2正则化。

  • L1正则化:向损失函数中添加权重参数的绝对值之和作为惩罚项。这种方式会使部分权重收缩为0,从而实现特征选择的效果。
  • L2正则化:向损失函数中添加权重参数的平方和作为惩罚项。这种方式会使所有权重都趋向于较小的值,从而实现权重衰减的效果。L2正则化也称为权重衰减。

数学表示

假设模型的权重参数为W,损失函数为L,那么引入权重衰减后的损失函数可以表示为:

L’ = L + λ * ||W||^2

其中,||W||^2表示W的平方和,λ是正则化参数,用于控制惩罚的大小。λ越大,惩罚的作用越强,权重参数W越趋近于0。

实现步骤

  1. 定义模型结构和参数:首先,需要定义深度学习模型的结构和参数。
  2. 定义损失函数:然后,定义模型在训练数据上的损失函数。
  3. 添加正则化项:在损失函数中添加与权重参数的平方和成正比的惩罚项,即正则化项。
  4. 构建优化算法:使用梯度下降法或其变种来优化损失函数,同时更新模型的权重参数。
  5. 迭代训练:在每次迭代中,计算模型在训练数据上的损失函数、正则化项和总的目标函数,并使用优化算法更新模型的权重参数。

通过以上步骤,可以有效地实现权重衰减,并降低模型的过拟合风险。

自我感悟

正则化定义

凡是可以减少泛化误差,而不是减少训练误差的方法,即减小过拟合

利用L1,L2减小过拟合的原因

因为高维的模型在训练训练集时,很容易出现很大的w值,即使最后的损失函数很小,过大的w值在测试集很容易将误差和噪声增大

解决:人为地给参数画一个框框,即可行域范围:在求损失函数的时候,规定一个w的可行域,即范数

暂退法

—-另一种减少过拟合的方法

原理

前面提到,出现过拟合的原因是权重w过大,暂退法通过随机丢弃一些神经元来减少神经网络对单一神经元的依赖

Pytorch实现

对于深度学习框架的高级API,我们只需在每个全连接层之后添加一个Dropout层, 将暂退概率作为唯一的参数传递给它的构造函数。 在训练时,Dropout层将根据指定的暂退概率随机丢弃上一层的输出(相当于下一层的输入)。 在测试时,Dropout层仅传递数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
net = nn.Sequential(nn.Flatten(),
nn.Linear(784, 256),
nn.ReLU(),
# 在第一个全连接层之后添加一个dropout层
nn.Dropout(dropout1),
nn.Linear(256, 256),
nn.ReLU(),
# 在第二个全连接层之后添加一个dropout层
nn.Dropout(dropout2),
nn.Linear(256, 10))

def init_weights(m):
if type(m) == nn.Linear:
nn.init.normal_(m.weight, std=0.01)

net.apply(init_weights);

对模型进行训练和测试

1
2
trainer = torch.optim.SGD(net.parameters(), lr=lr)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)

4

前向传播,反向传播和计算图

以带权重衰减(𝐿2正则化)的单隐藏层多层感知机为例。

前向传播

假设输入样本是 𝑥∈𝑅𝑑, 并且我们的隐藏层不包括偏置项。

5

前向传播计算图

计算图可以可视化计算中操作符和变量的依赖关系

正方形为变量;圆形为操作符

6

反向传播

——指的是计算神经网络参数的方法,及根据求导的链式法则,按反顺序从输出层到输入层遍历网络

最终目的是计算:J对W的偏导

7

梯度消失和梯度爆炸

梯度消失的原因及影响

原因:

  1. 网络层数过多:随着网络层数的增加,梯度在反向传播过程中会逐层乘以前面层的导数,导致梯度值逐渐变小。
  2. 激活函数选择不当:例如使用sigmoid函数时,其导数在两端非常小(最大为0.25),导致梯度消失现象严重。

影响:

  • 梯度消失会导致参数更新几乎不发生变化,使得网络难以收敛,学习效果差。
  • 这种现象限制了模型的最大深度,使得许多理论上可能有效的深度模型在实际中无法实现。

梯度爆炸的原因及影响

原因:

  1. 网络层数过多:与梯度消失类似,随着网络层数的增加,梯度在反向传播过程中会逐层乘以前面层的导数,导致梯度值逐渐变大。
  2. 学习率设置过高:如果学习率设置得过高,梯度更新的幅度也会相应增大,容易导致梯度爆炸。
  3. 损失函数设计不合理:某些损失函数可能导致梯度值在反向传播过程中不断放大。

影响:

  • 梯度爆炸会导致模型权重参数变化过大,使整个网络变得不稳定,甚至直接导致权重值溢出(NaN)。
  • 这种现象会导致模型无法利用训练数据进行有效学习,训练过程中的梯度更新变得非常大,从而使得模型难以训练和收敛。

解决方法

针对梯度消失和梯度爆炸的问题,有多种解决方案:

  1. 更换激活函数:使用ReLU、LeakyReLU、PReLU等非饱和激活函数,这些激活函数在正区间导数恒等于1,可以有效避免梯度消失问题,但需要注意防止梯度爆炸。
  2. 权重初始化策略:采用He或Xavier初始化策略,可以更好地控制权重的初始范围,减少梯度爆炸的可能性。
  3. 残差连接:通过引入残差连接,可以有效地缓解梯度消失和梯度爆炸的问题。
  4. 批量归一化:在每一层之后添加批量归一化操作,可以稳定网络的训练过程,减少梯度消失和梯度爆炸的影响。
  5. 梯度裁剪:在反向传播过程中对梯度值进行限制,防止其过大或过小。
  6. 调整学习率:适当调整学习率,避免过大或过小的学习率导致梯度消失或梯度爆炸。

通过以上方法,可以有效地缓解深度学习中梯度消失和梯度爆炸的问题,提高模型的训练效率和最终性能。

如何选择最适合特定深度学习任务的激活函数以避免梯度消失?

选择最适合特定深度学习任务的激活函数以避免梯度消失问题,需要综合考虑数据特征、任务类型和模型复杂度等因素。以下是一些具体的建议和方法:

  1. 根据数据特征选择激活函数
    • 对于二元或多元分类问题,可以使用sigmoid函数和tanh函数。
    • 对于回归问题和一些分类问题,ReLU函数和Leaky ReLU函数更为适用。
  2. 尝试多种激活函数
    • 在实践中,建议尝试不同的激活函数,并通过实验确定最适合特定任务的函数。
    • 常见的激活函数包括ReLU、Leaky ReLU、ELU、Sigmoid、Tanh和Softmax等。
  3. 使用残差网络(ResNet)和LSTM结构
    • 残差网络(ResNet)可以通过引入残差连接来缓解梯度消失问题。
    • LSTM(长短期记忆网络)也是一种有效的方法,特别适用于处理时间序列数据。
  4. 权重初始化和正则化技术
    • 使用合适的权重初始化方法,如Xavier初始化或He初始化,可以减少梯度消失的风险。
    • 应用梯度剪切和正则化技术也可以帮助控制梯度大小,防止梯度消失。
  5. 标准化处理
    • 使用Batch Normalization(批量归一化)可以有效地减少内部协变量偏移,从而缓解梯度消失问题。

批量归一化操作对深度学习模型训练过程的影响及其效果评估。

批量归一化(Batch Normalization, BN)是深度学习中一种重要的优化技术,其主要目的是通过在每个批次中对神经网络中每个层次的每个神经元的输入进行归一化处理,使其遵循标准正态分布。这种方法可以有效改善模型的训练过程,提升模型性能和泛化能力。

批量归一化的核心思想是在训练过程中利用小批量的均值和方差调整神经网络中间输出,从而稳定层输入的分布,使深度神经网络的训练更加快速和稳定。具体来说,批量归一化通过对每一批训练数据进行统计分析,计算出均值和标准差,然后根据这些统计量对输入数据进行归一化处理。这样可以减少内部协方差(internal covariate shift, ICS),即前几层更新时引起的层输入分布的变化,从而简化了训练过程。

批量归一化还可以加速深层网络的收敛速度,使得模型参数的更新更加平滑,避免了靠近输出层输出的剧烈变化。此外,批量归一化还可以帮助设置更小的学习率和更严格的参数初始化,进一步提高模型的训练效果。

然而,尽管批量归一化在许多情况下表现出色,但在小批量大小下保证其有效性仍是一个挑战。未来的研究方向包括开发更加适应不同任务和场景的新一代归一化方法,以克服现有技术的局限性。

批量归一化对深度学习模型训练过程有显著的积极影响,能够提升模型性能和泛化能力,并加速收敛速度。

梯度裁剪在防止深度学习模型梯度爆炸中的应用及其效果分析。

梯度裁剪技术在防止深度学习模型梯度爆炸中的应用及其效果分析如下:

梯度裁剪(Gradient Clipping)是一种常用的优化技术,用于解决深度学习模型训练过程中常见的梯度爆炸问题。梯度爆炸是指在训练过程中,梯度的大小急剧增加,导致权重更新过大,从而阻碍模型训练。为了防止这种情况,梯度裁剪通过限制梯度的范围来控制梯度幅度,使其保持在一个合理的范围内。

梯度裁剪主要有两种方法:一种是按照梯度的绝对值进行裁剪,即如果梯度的绝对值超过了一个阈值,就将其设置为该阈值的符号乘以该阈值;另一种是按照梯度的范数进行裁剪。这两种方法都可以有效地防止梯度爆炸,但具体选择哪种方法取决于具体任务和模型需求。

在实际应用中,梯度裁剪可以显著提高模型的训练效果和稳定性。例如,在PyTorch中,可以通过torch.nn.utils.clip _grad_norm_方法来进行梯度裁剪操作,这有助于确保模型训练的稳定性和收敛性。此外,梯度裁剪还可以与其他优化技术结合使用,以进一步提升模型性能。