1.线性模型
线性模型可能出错
例如,线性意味着单调假设: 任何特征的增大都会导致模型输出的增大(如果对应的权重为正), 或者导致模型输出的减小(如果对应的权重为负)。 有时这是有道理的。 例如,如果我们试图预测一个人是否会偿还贷款。 我们可以认为,在其他条件不变的情况下, 收入较高的申请人比收入较低的申请人更有可能偿还贷款。 但是,虽然收入与还款概率存在单调性,但它们不是线性相关的。 收入从0增加到5万,可能比从100万增加到105万带来更大的还款可能性。 处理这一问题的一种方法是对我们的数据进行预处理, 使线性变得更合理,如使用收入的对数作为我们的特征。
然而我们可以很容易找出违反单调性的例子。 例如,我们想要根据体温预测死亡率。 对体温高于37摄氏度的人来说,温度越高风险越大。 然而,对体温低于37摄氏度的人来说,温度越高风险就越低。 在这种情况下,我们也可以通过一些巧妙的预处理来解决问题。 例如,我们可以使用与37摄氏度的距离作为特征。
但是,如何对猫和狗的图像进行分类呢? 增加位置(13,17)处像素的强度是否总是增加(或降低)图像描绘狗的似然? 对线性模型的依赖对应于一个隐含的假设, 即区分猫和狗的唯一要求是评估单个像素的强度。 在一个倒置图像后依然保留类别的世界里,这种方法注定会失败。
与我们前面的例子相比,这里的线性很荒谬, 而且我们难以通过简单的预处理来解决这个问题。 这是因为任何像素的重要性都以复杂的方式取决于该像素的上下文(周围像素的值)。 我们的数据可能会有一种表示,这种表示会考虑到我们在特征之间的相关交互作用。 在此表示的基础上建立一个线性模型可能会是合适的, 但我们不知道如何手动计算这么一种表示。 对于深度神经网络,我们使用观测数据来联合学习隐藏层表示和应用于该表示的线性预测器。
2.加入隐藏层
我们可以通过在网络中加入一个或多个隐藏层来克服线性模型的限制, 使其能处理更普遍的函数关系类型。 要做到这一点,最简单的方法是将许多全连接层堆叠在一起。 每一层都输出到上面的层,直到生成最后的输出。 我们可以把前𝐿−1层看作表示,把最后一层看作线性预测器。 这种架构通常称为多层感知机(multilayer perceptron),通常缩写为MLP。
3.从线性到非线性
注意在添加隐藏层之后,模型现在需要跟踪和更新额外的参数。 可我们能从中得到什么好处呢?在上面定义的模型里,我们没有好处! 原因很简单:上面的隐藏单元由输入的仿射函数给出, 而输出(softmax操作前)只是隐藏单元的仿射函数。 仿射函数的仿射函数本身就是仿射函数, 但是我们之前的线性模型已经能够表示任何仿射函数。
为了发挥多层架构的潜力, 我们还需要一个额外的关键要素: 在仿射变换之后对每个隐藏单元应用非线性的激活函数(activation function)𝜎。 激活函数的输出(例如,𝜎(⋅))被称为活性值(activations)。 一般来说,有了激活函数,就不可能再将我们的多层感知机退化成线性模型。
4.激活函数
激活函数(activation function)通过计算加权和并加上偏置来确定神经元是否应该被激活, 它们将输入信号转换为输出的可微运算。 大多数激活函数都是非线性的。 由于激活函数是深度学习的基础,下面简要介绍一些常见的激活函数。
5.模型复杂性
当我们有简单的模型和大量的数据时,我们期望泛化误差与训练误差相近。 当我们有更复杂的模型和更少的样本时,我们预计训练误差会下降,但泛化误差会增大。 模型复杂性由什么构成是一个复杂的问题。 一个模型是否能很好地泛化取决于很多因素。 例如,具有更多参数的模型可能被认为更复杂, 参数有更大取值范围的模型可能更为复杂。 通常对于神经网络,我们认为需要更多训练迭代的模型比较复杂, 而需要早停(early stopping)的模型(即较少训练迭代周期)就不那么复杂。
我们很难比较本质上不同大类的模型之间(例如,决策树与神经网络)的复杂性。 就目前而言,一条简单的经验法则相当有用: 统计学家认为,能够轻松解释任意事实的模型是复杂的, 而表达能力有限但仍能很好地解释数据的模型可能更有现实用途。 在哲学上,这与波普尔的科学理论的可证伪性标准密切相关: 如果一个理论能拟合数据,且有具体的测试可以用来证明它是错误的,那么它就是好的。 这一点很重要,因为所有的统计估计都是事后归纳。 也就是说,我们在观察事实之后进行估计,因此容易受到相关谬误的影响。 目前,我们将把哲学放在一边,坚持更切实的问题。
本节为了给出一些直观的印象,我们将重点介绍几个倾向于影响模型泛化的因素。
可调整参数的数量。当可调整参数的数量(有时称为自由度)很大时,模型往往更容易过拟合。
参数采用的值。当权重的取值范围较大时,模型可能更容易过拟合。
训练样本的数量。即使模型很简单,也很容易过拟合只包含一两个样本的数据集。而过拟合一个有数百万个样本的数据集则需要一个极其灵活的模型。
6.验证集
在机器学习和深度学习中,将数据集分为训练集、验证集和测试集是为了确保模型的性能和泛化能力。这三个数据集的角色和作用分别如下:
-
训练集(Training Set):
- 用于训练模型,即模型在这些数据上进行参数调整和学习模式。
- 模型在训练集上的表现主要反映其在已知数据上的学习能力。
-
验证集(Validation Set):
- 用于模型的选择和超参数调整。
- 在训练过程中,验证集帮助监控模型的表现,以防止过拟合(即模型在训练集上表现很好,但在新数据上表现不佳)。
- 通过验证集,能够在训练过程中评估模型的性能,调整模型的架构或选择最优的超参数组合(如学习率、正则化参数等)。
- 验证集的结果用于指导模型的优化和调整,而不是最终的模型评估。
-
测试集(Test Set):
- 用于评估模型的最终性能。
- 测试集在训练和验证过程中不参与任何模型调整。
- 测试集的结果代表模型在真实世界新数据上的性能,反映模型的泛化能力。
总结来说,验证集的引入是为了在模型训练过程中提供一个独立的数据集来进行模型评估和调整,以防止过拟合,并帮助选择最优的模型参数。测试集则是在模型训练和调优完成后,用于对模型进行最终的性能评估。通过这种分割方法,可以更好地评估模型的性能,并确保模型在未见过的数据上也能表现良好。
7.K折交叉验证
当训练数据稀缺时,我们甚至可能无法提供足够的数据来构成一个合适的验证集。 这个问题的一个流行的解决方案是采用𝐾折交叉验证。 这里,原始训练数据被分成𝐾个不重叠的子集。 然后执行𝐾次模型训练和验证,每次在𝐾−1个子集上进行训练, 并在剩余的一个子集(在该轮中没有用于训练的子集)上进行验证。 最后,通过对𝐾次实验的结果取平均来估计训练和验证误差。
8.权重衰减
在训练参数化机器学习模型时, 权重衰减(weight decay)是最广泛使用的正则化的技术之一, 它通常也被称为𝐿2正则化。 这项技术通过函数与零的距离来衡量函数的复杂度, 因为在所有函数𝑓中,函数𝑓=0(所有输入都得到值0) 在某种意义上是最简单的。 但是我们应该如何精确地测量一个函数和零之间的距离呢? 没有一个正确的答案。 事实上,函数分析和巴拿赫空间理论的研究,都在致力于回答这个问题。
一种简单的方法是通过线性函数 𝑓(𝑥)=𝑤⊤𝑥 中的权重向量的某个范数来度量其复杂性, 例如‖𝑤‖2。 要保证权重向量比较小, 最常用方法是将其范数作为惩罚项加到最小化损失的问题中。 将原来的训练目标最小化训练标签上的预测损失, 调整为最小化预测损失和惩罚项之和。 现在,如果我们的权重向量增长的太大, 我们的学习算法可能会更集中于最小化权重范数‖𝑤‖2。
此外,为什么我们首先使用𝐿2范数,而不是𝐿1范数。 事实上,这个选择在整个统计领域中都是有效的和受欢迎的。 𝐿2正则化线性模型构成经典的岭回归(ridge regression)算法, 𝐿1正则化线性回归是统计学中类似的基本模型, 通常被称为套索回归(lasso regression)。 使用𝐿2范数的一个原因是它对权重向量的大分量施加了巨大的惩罚。 这使得我们的学习算法偏向于在大量特征上均匀分布权重的模型。 在实践中,这可能使它们对单个变量中的观测误差更为稳定。 相比之下,𝐿1惩罚会导致模型将权重集中在一小部分特征上, 而将其他权重清除为零。 这称为特征选择(feature selection),这可能是其他场景下需要的。
权重衰减的数学表达如下:
其中:
- LtotalL是总损失。
- Ldata是原始损失(如均方误差或交叉熵损失)。
- λ是正则化系数,控制权重衰减的强度。
- wi是模型的权重参数。
具体来说,权重衰减的工作原理如下:
- 损失函数修改:在原始损失函数(如交叉熵损失或均方误差损失)中添加一个项,惩罚所有权重的平方和。这意味着权重的值越大,这个惩罚项的值就越大。
- 控制复杂性:通过添加这个惩罚项,模型的优化过程会倾向于保持权重较小,这样可以避免模型过于复杂,从而减少过拟合的风险。
- 参数更新:在每次迭代中,优化算法(如梯度下降)不仅会根据损失函数对权重进行更新,还会根据权重衰减项对权重进行额外的调整,使得权重逐渐减小。
权重衰减在实际应用中具有以下优点:
- 防止过拟合:通过减少权重值,模型对训练数据的拟合不会过于复杂,从而提高对新数据的泛化能力。
- 简化模型:权重衰减鼓励模型选择较小的权重,从而学习到更简单的特征。
- 提高稳定性:通过惩罚大权重,权重衰减可以提高模型训练的稳定性,减少训练过程中的波动。
在实践中,选择合适的正则化系数 λ 是关键。通常需要通过交叉验证来确定最佳的 λ 值,以在防止过拟合和保持模型表现之间取得平衡。
9.Dropout
在探究泛化性之前,我们先来定义一下什么是一个“好”的预测模型? 我们期待“好”的预测模型能在未知的数据上有很好的表现: 经典泛化理论认为,为了缩小训练和测试性能之间的差距,应该以简单的模型为目标。 简单性以较小维度的形式展现, 正如我们讨论权重衰减(𝐿2正则化)时看到的那样, 参数的范数也代表了一种有用的简单性度量。
简单性的另一个角度是平滑性,即函数不应该对其输入的微小变化敏感。 例如,当我们对图像进行分类时,我们预计向像素添加一些随机噪声应该是基本无影响的。 1995年,克里斯托弗·毕晓普证明了 具有输入噪声的训练等价于Tikhonov正则化 。 这项工作用数学证实了“要求函数光滑”和“要求函数对输入的随机噪声具有适应性”之间的联系。
然后在2014年,斯里瓦斯塔瓦等人就如何将毕晓普的想法应用于网络的内部层提出了一个想法: 在训练过程中,他们建议在计算后续层之前向网络的每一层注入噪声。 因为当训练一个有多层的深层网络时,注入噪声只会在输入-输出映射上增强平滑性。
这个想法被称为暂退法(dropout)。 暂退法在前向传播过程中,计算每一内部层的同时注入噪声,这已经成为训练神经网络的常用技术。 这种方法之所以被称为暂退法,因为我们从表面上看是在训练过程中丢弃(drop out)一些神经元。 在整个训练过程的每一次迭代中,标准暂退法包括在计算下一层之前将当前层中的一些节点置零。
需要说明的是,暂退法的原始论文提到了一个关于有性繁殖的类比: 神经网络过拟合与每一层都依赖于前一层激活值相关,称这种情况为“共适应性”。 作者认为,暂退法会破坏共适应性,就像有性生殖会破坏共适应的基因一样。
10.前向传播、反向传播和计算图
前向传播计算图
绘制计算图有助于我们可视化计算中操作符和变量的依赖关系。计算图是简单网络相对应的计算图, 其中正方形表示变量,圆圈表示操作符。 左下角表示输入,右上角表示输出。 注意显示数据流的箭头方向主要是向右和向上的。
s为正则化项,与交叉熵损失函数相加得到最终的损失函数。
反向传播
反向传播(backward propagation或backpropagation)指的是计算神经网络参数梯度的方法。 简言之,该方法根据微积分中的链式规则,按相反的顺序从输出层到输入层遍历网络。 该算法存储了计算某些参数梯度时所需的任何中间变量(偏导数)。 假设我们有函数𝑌=𝑓(𝑋)和𝑍=𝑔(𝑌), 其中输入和输出𝑋,𝑌,𝑍是任意形状的张量。 利用链式法则,我们可以计算𝑍关于𝑋的导数
训练神经网络
在训练神经网络时,前向传播和反向传播相互依赖。 对于前向传播,我们沿着依赖的方向遍历计算图并计算其路径上的所有变量。 然后将这些用于反向传播,其中计算顺序与计算图的相反。
以上述简单网络为例:一方面,在前向传播期间计算正则项取决于模型参数𝑊(1)和 𝑊(2)的当前值。 它们是由优化算法根据最近迭代的反向传播给出的。 另一方面,反向传播期间参数的梯度计算, 取决于由前向传播给出的隐藏变量ℎ的当前值。
因此,在训练神经网络时,在初始化模型参数后, 我们交替使用前向传播和反向传播,利用反向传播给出的梯度来更新模型参数。 注意,反向传播重复利用前向传播中存储的中间值,以避免重复计算。 带来的影响之一是我们需要保留中间值,直到反向传播完成。 这也是训练比单纯的预测需要更多的内存(显存)的原因之一。 此外,这些中间值的大小与网络层的数量和批量的大小大致成正比。 因此,使用更大的批量来训练更深层次的网络更容易导致内存不足(out of memory)错误。
11.梯度消失、梯度爆炸
梯度是𝐿−𝑙个矩阵 𝑀(𝐿)⋅…⋅𝑀(𝑙+1) 与梯度向量 𝑣(𝑙)的乘积。 因此,我们容易受到数值下溢问题的影响. 当将太多的概率乘在一起时,这些问题经常会出现。
不稳定梯度带来的风险不止在于数值表示; 不稳定梯度也威胁到我们优化算法的稳定性。 我们可能面临一些问题。 要么是梯度爆炸(gradient exploding)问题: 参数更新过大,破坏了模型的稳定收敛; 要么是梯度消失(gradient vanishing)问题: 参数更新过小,在每次更新时几乎不会移动,导致模型无法学习。
曾经sigmoid函数很流行, 因为它类似于阈值函数。 由于早期的人工神经网络受到生物神经网络的启发, 神经元要么完全激活要么完全不激活(就像生物神经元)的想法很有吸引力。 然而,它却是导致梯度消失问题的一个常见的原因, 让我们仔细看看sigmoid函数为什么会导致梯度消失。
正如上图,当sigmoid函数的输入很大或是很小时,它的梯度都会消失。 此外,当反向传播通过许多层时,除非我们在刚刚好的地方, 这些地方sigmoid函数的输入接近于零,否则整个乘积的梯度可能会消失。 当我们的网络有很多层时,除非我们很小心,否则在某一层可能会切断梯度。 事实上,这个问题曾经困扰着深度网络的训练。 因此,更稳定的ReLU系列函数已经成为从业者的默认选择(虽然在神经科学的角度看起来不太合理)。
梯度爆炸可能同样令人烦恼。 为了更好地说明这一点,我们生成100个高斯随机矩阵,并将它们与某个初始矩阵相乘。 对于我们选择的尺度(方差𝜎2=1),矩阵乘积发生爆炸。 当这种情况是由于深度网络的初始化所导致时,我们没有机会让梯度下降优化器收敛。
解决方法
参数初始化:解决(或至少减轻)上述问题的一种方法是进行参数初始化, 优化期间的注意和适当的正则化也可以进一步提高稳定性。如果我们不指定初始化方法, 框架将使用默认的随机初始化方法,对于中等难度的问题,这种方法通常很有效。
使用适当的激活函数:
- ReLU(Rectified Linear Unit):ReLU激活函数有助于缓解梯度消失问题,因为它在正区域不会饱和。
- Leaky ReLU 和 ELU(Exponential Linear Unit):这些是ReLU的变体,可以在负区域有非零梯度,进一步减少梯度消失问题。
归一化层:
- 批归一化(Batch Normalization):通过在每个小批量数据上归一化输入,可以稳定训练过程并加快收敛。
- 层归一化(Layer Normalization):对每个训练样本的激活进行归一化,适合于RNN等不适合批归一化的场景。
梯度裁剪:设定梯度的阈值,避免梯度爆炸问题。常用的方法是将梯度的范数裁剪到一个预定的最大值。
调整学习率:
- 使用学习率调度器:如学习率逐步衰减(Learning Rate Decay),或者在某些训练时期动态调整学习率。
- 自适应优化方法:如Adam、RMSprop,它们会根据梯度的历史信息自适应调整学习率。
更深层网络的架构设计:
- 残差网络(ResNet):引入残差连接,有效地缓解了梯度消失和梯度爆炸问题。
- 长短期记忆网络(LSTM)和门控循环单元(GRU):在RNN中,通过引入门控机制,缓解梯度消失和梯度爆炸问题。