在深度学习的学习过程中,我们会接触到大量的理论知识,如梯度下降,反向传播,损失函数等。然而,真正理解并应用这些理论知识,才能使我们在解决实际问题时如鱼得水,运用自如。本篇博客根据吴恩达教授的Deep Learning Specialization中的Course2第一周的内容,从深度学习的实践角度出发,深入解读一些至关重要的概念和方法,例如如何划分训练集、开发集和测试集,如何理解和处理偏差和方差,何时并如何使用正则化,以及如何正确设置优化问题。
1. 数据集划分:训练集、验证集与测试集
当我们训练一个深度学习模型时,我们需要一个训练集(Train Set)来训练模型,一个开发集(Dev Set)或者叫做验证集来调整模型的参数,最后通过一个测试集(Test Set)来评估模型的性能。
- 训练集(Train Set):用于训练模型的数据集。模型会学习这些数据并尝试找出其潜在的规律。
- 验证集(Dev Set):在训练过程中,我们需要一种方式来检查模型是否正在正确学习。验证集就派上用场了。我们使用这部分数据来调整模型的超参数和决定最佳的模型架构。
- 测试集(Test Set):最后,我们需要一种方式来确认模型对于未见过的数据的处理能力。这就是测试集的作用。在整个开发过程结束后,我们在测试集上进行一次评估,检验模型的泛化能力。
常见的划分比例是60%/20%/20%,但如果数据量比较大,例如大于百万级别,实际上我们也可以按照98%/1%/1%这样的比例进行划分。另外,如果项目需求允许,也可以只设置训练集和验证集,不使用测试集。
2. 理解偏差和方差
在统计和机器学习中,偏差和方差是两个重要的概念。
- 偏差(Bias):偏差是指我们的预测值与实际值之间的差异。高偏差通常意味着模型过于简单(也称为欠拟合),没有能够捕获到数据中所有的规律。例如,如果我们的训练集误差为15%,而开发集误差为16%,那么可能意味着我们的模型存在高偏差。
- 方差(Variance):方差是指模型对于训练集的敏感性。如果方差过高,那么模型可能过于复杂(也称为过拟合),对训练集的微小变化反应过度。例如,如果我们的训练集误差为1%,而开发集误差为11%,那么可能意味着我们的模型存在高方差。
当我们的训练集误差为15%,而开发集误差为30%时,那可能意味着我们的模型既存在高偏差又存在高方差。而当我们的训练集误差为0.1%,开发集误差为1%时,可能意味着我们的模型偏差和方差都控制得相当好。
但值得注意的是,如果我们使用贝叶斯错误率假设人类识别的错误率为15%,那么训练集误差为15%,而开发集误差为16%的模型,其实也是一个很好的模型。
3. 机器学习的基本策略
在机器学习中,我们可以通过一些基础策略来对偏差和方差进行权衡和控制。
- 诊断偏差与方差:我们可以通过观察模型在训练集和开发集上的误差,来判断算法是否存在高偏差(Bias)和高方差(Variance)问题。
- 解决高偏差问题:如果模型在训练集上的表现不佳,说明存在高偏差。我们可以通过以下方法来解决高偏差问题:增加更多的隐藏层或隐藏单元、延长训练时间、使用更高级的优化算法或者尝试找到更合适的神经网络结构。
- 解决高方差问题:如果模型在开发集上的表现不佳,说明存在高方差。我们可以通过以下方法来解决高方差问题:获取更多的训练数据、使用正则化方法来减少过拟合、或者找到更合适的神经网络结构。
- 偏差-方差权衡:在深度学习的环境下,我们有可能同时减少偏差和方差,而无需在两者之间进行权衡。增大网络规模和获取更多数据通常可以减少偏差,而不会增大方差;同样,获取更多数据通常可以减少方差,而不会增大偏差。
- 正则化:正则化是一种用于减少过拟合,即高方差的有效方法。在某些情况下,正则化可能会稍微增加偏差,但如果网络规模足够大,这种增加通常不会很多。
4. 正则化
在训练深度学习模型时,我们经常面临着欠拟合和过拟合的问题。欠拟合通常是因为模型太简单,无法学习到数据中的所有模式,而过拟合则是因为模型太复杂,过度学习训练数据中的噪声和特殊情况,从而在未见过的测试数据上表现不佳。为了解决这些问题,我们可以利用一种技术叫做正则化。
4.1 什么是正则化
正则化是一种技术,用于减小过拟合并提高模型在测试数据上的表现。过拟合时,模型在训练数据上的表现很好,但在测试数据上的表现不好。这是因为模型过度拟合了训练数据中的噪声和异常值,无法很好地泛化到新的、未见过的数据。
正则化方法的基本理念就是通过在模型的损失函数中引入一个额外的惩罚项,从而阻止模型变得过于复杂。这个惩罚项通常与模型的权重参数有关,可以对参数值过大的模型施加严重的惩罚,从而使得模型的权重参数趋于较小的值或者在一些特征上的权重为零。这样一来,模型就不能过于依赖某些特征,或者过于复杂,这有助于提高模型的泛化能力,也就是模型对未见过的新数据的处理能力。
这种方法在直观上也很好理解。想象一下,如果我们在设计一个预测模型,如果模型过于复杂,将每个微小的细节都纳入考虑,那么模型就有可能对训练数据的一些随机噪声进行过度拟合,而在面对新的、未知的数据时,模型就可能因为过度专注于训练数据中的特定细节,而无法准确地进行预测。而通过正则化,我们在一定程度上限制了模型的复杂性,使得模型不能对训练数据中的每一个微小细节都进行拟合,这样在面对新的数据时,模型就更有可能抓住真正的规律,而不是过度关注训练数据中的噪声。
4.2 L1和L2正则化
L1和L2正则化是常见的两种正则化方法。L1正则化通过在损失函数中加入参数的绝对值作为惩罚项,鼓励模型使用较少的参数,从而得到一个稀疏的模型,这在特征选择中很有用。另一方面,L2正则化则是通过在损失函数中加入参数的平方和作为惩罚项,鼓励模型的参数值都较小,从而防止模型过度依赖训练数据中的某个特征。
4.2.1 L1正则化
L1正则化,也被称为Lasso正则化,是指在损失函数中加入模型参数绝对值的和,其公式可以表示为:
JL1(θ)=J(θ)+λ∑∣θi∣J_{L1}(θ)=J(θ)+λsum {∣θi∣}
其中,J(θ)J(theta)是原始的损失函数,θitheta_i是模型的参数,λlambda是正则化参数,用于控制正则化的强度。
L1正则化的特点是它会倾向于让模型的参数向零收敛,从而得到一个稀疏的模型。也就是说,L1正则化会让模型的一些不重要的参数变为零,从而达到特征选择的效果。
4.2.2 L2正则化
L2正则化,也被称为岭回归(Ridge Regression)或权重衰减,是指在损失函数中加入模型参数平方的和,其公式可以表示为:
JL2(θ)=J(θ)+λ∑θi2J_{L2}(θ)=J(θ)+λsum {θ_i^2}
其中,J(θ)J(theta)是原始的损失函数,θitheta_i是模型的参数,λlambda是正则化参数。
与L1正则化不同,L2正则化不会将参数强制收敛到零,而是会让参数向零靠近,得到一个”平滑”的模型。L2正则化可以防止模型的权重过大,从而降低模型的复杂度,减少过拟合。
在选择L1和L2正则化时,我们需要考虑到他们的特点和应用场景。如果我们希望得到一个稀疏的模型,或者进行特征选择,那么可以选择L1正则化。如果我们希望降低模型的复杂度,减少过拟合,那么可以选择L2正则化。在实际应用中,我们也可以同时使用L1和L2正则化,这被称为Elastic Net正则化。
4.3 Dropout正则化
Dropout是一种非常有效的防止神经网络过拟合的正则化技术,尤其在处理大型神经网络时更为有效。Dropout背后的想法很简单:在训练过程中,随机选择一部分神经元并将它们”关掉”。具体地说,在每一次的训练步骤中,我们随机地选取一部分神经元并将它们的输出设为0。这样做的一个直观解释是,我们在模拟每次训练步骤中的”网络损伤”,因此每次训练的过程就好像是在训练一个小型的网络。这样的做法实际上是在训练时间内构造和训练了大量的不同的小型网络。
那么为什么这种方法会有用呢?首先,这样做增加了模型的鲁棒性,因为模型需要学习如何在某些神经元突然“关掉”时仍然能够做出准确的预测。其次,通过随机关闭神经元,我们有效地限制了神经元之间复杂的协同效应,从而防止过拟合。每个神经元都不能依赖特定的其他神经元;它们需要在可能的情况下都能正常工作。这也就意味着神经元需要学习到更有用的、更通用的特性。
这种训练过程类似于训练一大堆小型神经网络,然后对它们的预测结果进行平均。实际上,Dropout可以看作是一种非常简洁的集成学习技术。集成学习是一种强大的机器学习技术,其中我们训练许多不同的模型,然后将它们的预测结果进行组合。Dropout提供了一种简单且计算有效的方法来近似训练和集成许多不同的网络架构。
需要注意的是,Dropout仅在训练期间使用。在评估或测试模型的性能时,我们不使用Dropout,因为我们希望神经网络使用它所有的能力来进行最好的预测。为了补偿在训练期间丢弃了一部分神经元,我们需要对剩下的神经元的输出进行缩放。例如,如果我们在训练期间以50%的概率丢弃神经元,那么在测试时,我们需要将神经元的输出减半。
4.4 其他正则化方法
除了L1、L2和Dropout正则化外,还有其他一些正则化方法:
- Early Stopping:当模型在验证集上的性能不再提升时,提前停止训练,防止模型过拟合。
- Data Augmentation:通过对训练数据进行一些改变(如旋转、缩放等),增加训练数据的多样性,增强模型的泛化能力。
- Batch Normalization:在每一层的输出之后,对数据进行标准化处理,使数据符合标准正态分布,这样可以加快模型训练速度,同时也起到一定的正则化效果。
5. 深入理解神经网络优化问题的设置
在深度学习领域,优化问题的设置是一个至关重要的话题,因为一个优化好的模型可以有效地改进学习效果,并在处理复杂任务时更有效。在这部分内容中,我们将讨论以下几个重要的主题:输入数据的标准化,梯度消失和梯度爆炸问题,深度网络的权重初始化,梯度的数值逼近,以及梯度检查。
5.1 输入数据的标准化
在开始训练神经网络模型之前,我们通常会对输入数据进行预处理,其中一种常见的方法是标准化。它可以使深度学习模型的训练过程更加稳定并提高其性能。这种技术的主要思想是对原始输入数据进行缩放,使其具有零均值(即中心化)和单位方差(即标准化)。
标准化的过程通常包括两个步骤:减去均值(使数据具有零均值)和除以标准差(使数据的标准差为1)。
对于一个特征 xx,我们首先计算其均值 μmu 和标准差 σsigma:
μ=1m∑i=1mximu = frac{1}{m}sum_{i=1}^{m} x_i
σ=1m∑i=1m(xi−u)2sigma = sqrt {frac {1}{m} sum_{i=1}^{m}(x_i-u)^2}
其中,mm 是样本的数量。然后,我们对每个样本的特征进行标准化:
xnorm=x−μσx_{norm} = frac {x-mu}{sigma}
经过上述步骤,我们就得到了标准化后的特征 xnormx_{norm},它具有零均值和单位方差。
这样做的好处是,可以使不同特征的数值范围一致,防止优化过程受到某一特征数值范围过大或过小的影响。此外,这也有助于提高模型的稳定性和收敛速度。
值得注意的是,训练集和测试集应使用相同的参数进行标准化,也就是说,我们应该使用训练集的均值和标准差来标准化测试集。这是因为在实际应用中,我们通常假设测试集的数据来源和训练集一致。
5.2 梯度消失和梯度爆炸问题
当我们使用梯度下降法或其变体进行优化时,可能会遇到梯度消失或梯度爆炸的问题,这些问题在深度神经网络中尤为突出。
- 梯度消失问题:在深度神经网络中,梯度消失问题通常是由于网络的深度造成的。由于链式法则的影响,梯度可能会随着网络层的增加而指数级地减小,这使得深层的权重很难更新。梯度消失问题可能导致网络在训练初期学习非常慢,或者完全无法学习。
- 梯度爆炸问题:与梯度消失问题相反,梯度爆炸问题是由于梯度在网络中的传播过程中指数级地增大造成的。梯度爆炸可能导致网络权重的更新过大,使得网络无法收敛。
梯度消失和爆炸问题一直以来都是训练深层神经网络的一个主要难题。一种可能的部分解决方案是谨慎地选择权重初始化方法。
5.3 深度网络的权重初始化
对于深度神经网络,权重的初始化方法对于模型的性能有着重要的影响。好的权重初始化方法可以让神经网络更快地收敛,而且可以降低训练过程中陷入不良局部最小值或者梯度消失、梯度爆炸的风险。
一般来说,我们不希望在初始化阶段就给予任何的偏见选择,这就意味着我们希望所有的神经元在初始阶段都是对称的,因此我们通常会选用随机的方式来初始化权重,以打破这种对称性。
然而,如果权重初始化过大,那么神经元的激活函数(例如sigmoid或者tanh)可能会在一开始就进入饱和区,导致梯度消失;反之,如果权重初始化过小,那么信号可能会在传播过程中迅速衰减,也无法达到好的训练效果。
对于深度神经网络的权重初始化,一种有效的方法是Xavier初始化(也叫做Glorot初始化)。假设一个神经元的输入连接数为nn,Xavier初始化的方式为从均值为0,方差为1nfrac{1}{n}的高斯分布中抽取初始权重,也就是说,
W∼N(0,1n)W sim N(0,frac{1}{n})
在实际操作中,我们还常常使用一种简化版的Xavier初始化,即从[−1n,1n][-frac{1}{sqrt{n}}, frac{1}{sqrt{n}}]的均匀分布中抽取初始权重。
当然,如果激活函数是ReLU或者其变种(如Leaky ReLU),He初始化可能是更好的选择。He初始化与Xavier初始化相似,但是考虑到了ReLU在负数部分全为0的特性,所以在计算方差时会乘以2,即
W∼N(0,2n)W sim N(0,frac{2}{n})
或者从[−2n,2n][-sqrt{frac{2}{n}}, sqrt{frac{2}{n}}]的均匀分布中抽取初始权重。
总的来说,权重初始化对于深度神经网络的训练是非常重要的。选择合适的权重初始化方法可以帮助我们的模型更快地收敛,且能够降低训练过程中出现梯度消失或者梯度爆炸的风险。
5.4 梯度检查
在深度学习的训练过程中,我们经常需要计算损失函数的梯度,以便使用梯度下降法或其变种(如Adam、RMSProp等)来优化模型的参数。然而,这个过程往往涉及复杂的反向传播和链式求导,因此有可能出错。这时,我们可以使用梯度检查(Gradient Checking)来确保我们的梯度计算是正确的。
梯度检查的基本思想是,对于网络中的每一个权重,我们都使用数值逼近来计算其梯度,然后与反向传播算法计算出的梯度进行比较。在数学中,我们知道,对于一个函数f(x)f(x),其在点xx处的导数可以通过以下的数值逼近公式得到:
f′(x)≈f(x+ϵ)−f(x−ϵ)2ϵf'(x) approx frac{f(x + epsilon) – f(x – epsilon)}{2epsilon}
其中,εvarepsilon是一个非常小的正数(例如10−710^{-7})。由于这个公式只依赖于函数ff的值,而不依赖于其导数,因此我们可以用它来检查我们的梯度计算是否正确。
在实践中,对于每一个参数θitheta_i,我们可以计算出通过反向传播得到的梯度∂J∂θifrac{partial J}{partial theta_i}和通过数值逼近法得到的梯度:
∂Japprox∂θi=J(θ1,θ2…,θi+ϵ,…)−J(θ1,θ2…,θi−ϵ,…)2ϵfrac{partial J_{approx}}{partial theta_i} = frac{J(theta_1,theta_2…,theta_i + epsilon,…) – J(theta_1,theta_2…,theta_i – epsilon,…)}{2 epsilon}
然后,我们可以计算这两个梯度之间的相对误差:
relative error=∣∣∂J∂θ−∂Japprox∂θ∣∣2∣∣∂J∂θ∣∣2+∣∣∂θJapprox∂θ∣∣2relativespace error = frac{||frac{partial J}{partial theta}-frac{partial J_{approx}}{partial theta}||_2}{|| frac{partial J}{partial theta} ||_2 + ||frac{partial theta J_{approx}}{partial theta}||_2}
如果这个相对误差小于一个很小的阈值(例如10−710^{-7}),那么我们可以认为我们的梯度计算是正确的。
然而,需要注意的是,虽然梯度检查是一个有用的工具,但它也有一些限制。
- 梯度检查应仅在调试时使用,不应在训练中使用,因为其计算过程非常慢。
- 如果算法没有通过梯度检验,你需要逐一检查它的组成部分,尝试找出问题所在。
- 在进行梯度检验时,如果使用了正则化,别忘了加入正则项。
- 梯度检验不能与随机失活(dropout)一起使用。使用梯度检验的时候,可以将dropout设为1.0(即不使用dropout)。
- 需要注意,虽然很少发生,但是可能存在这样一种情况:你的反向传播算法在w和b接近0的时候是正确的,但是当w和b变大的时候,算法精确度有所下降。在随机初始化后运行梯度检验,训练网络一段时间后,再运行一次梯度检验。
在本篇博客中,我们对深度学习的实践层面进行了深入的探讨,包括了训练/验证/测试集的划分、偏差和方差的权衡、正则化方法以及优化问题的设置。这篇也是Course2的第一周的内容,后面还会继续写第二三周的内容,希望能坚持下去吧。