优化算法(1)

在配置训练、验证和测试数据集的过程中做出正确决策会在很大程度上帮助大家创建高效的神经网络。训练神经网络时,我们需要做出很多决策,例如:神经网络分多少层、每层含有多少个隐藏单元、学习速率是多少、各层采用哪些激活函数等问题。

训练、验证和测试集

我们通常会将这些数据划分成三部分,一部分作为训练集(train set),一部分作为简单交叉验证集,有时也称之为验证集,方便起见,我就叫它验证集(dev set),其实都是同一个概念。最后一部分则作为测试集(test set)。

接下来,我们开始对训练执行算法,通过验证集或简单交叉验证集选择最好的模型,经过充分验证,我们选定了最终模型,然后就可以在测试集上进行评估了,为了无偏评估算法的运行状况。

在机器学习发展的小数据量时代,常见做法是将所有数据三七分,就是人们常说的70%验证集,30%测试集,如果没有明确设置验证集,也可以按照60%训练,20%验证和20%测试集来划分。没有测试集也不要紧,测试集的目的是对最终所选定的神经网络系统做出无偏估计,如果不需要无偏估计,也可以不设置测试集。

在机器学习中,如果只有一个训练集和一个验证集,而没有独立的测试集,遇到这种情况,训练集还被人们称为训练集,而验证集则被称为测试集。

那么验证集和测试集有什么区别呢?为什么要划分训练集、验证集和测试集?

训练集用于训练模型参数,测试集用于估计模型对样本的泛化误差,验证集用于“训练”模型的超参数。

偏差(Bias)、方差(Variance)

假设这就是数据集,如果给这个数据集拟合一条直线,可能得到一个逻辑回归拟合,但它并不能很好地拟合该数据,这是高偏差(high bias)的情况,我们称为“欠拟合”(underfitting)。

相反的如果我们拟合一个非常复杂的分类器,比如深度神经网络或含有隐藏单元的神经网络,可能就非常适用于这个数据集,但是这看起来也不是一种很好的拟合方式分类器方差较高(high variance),数据过度拟合(overfitting)。

在两者之间,可能还有复杂程度适中,数据拟合适度的分类器,这个数据拟合看起来更加合理,我们称之为“适度拟合”(just right)是介于过度拟合和欠拟合中间的一类。

下面举例子:

假定训练集误差是1%,为了方便论证,假定验证集误差是11%,可以看出训练集设置得非常好,而验证集设置相对较差,我们可能过度拟合了训练集,在某种程度上,验证集并没有充分利用交叉验证集的作用,像这种情况,我们称之为“高方差”。

通过查看训练集误差和验证集误差,我们便可以诊断算法是否具有高方差。也就是说衡量训练集和验证集误差就可以得出不同结论。

假设训练集误差是15%,我们把训练集误差写在首行,验证集误差是16%,假设该案例中人的错误率几乎为0%,人们浏览这些图片,分辨出是不是猫。算法并没有在训练集中得到很好训练,如果训练数据的拟合度不高,就是数据欠拟合,就可以说这种算法偏差比较高。相反,它对于验证集产生的结果却是合理的,验证集中的错误率只比训练集的多了1%,所以这种算法偏差高,因为它甚至不能拟合训练集,这与上一张图最左边的图片相似。

上面的分析都是基于假设预测,假设人眼辨别的错误率接近0%。最优误差也被称为贝叶斯误差。如果最优误差或贝叶斯误差非常高,比如15%。我们再看看上面这个分类器(训练误差15%,验证误差16%),15%的错误率对训练集来说也是非常合理的,偏差不高,方差也非常低。

正则化(Rugularization)

如果你怀疑神经网络过度拟合了数据,即存在高方差问题,那么最先想到的方法可能是正则化,另一个解决高方差的方法就是准备更多数据,这也是非常可靠的办法,但你可能无法时时准备足够多的训练数据,或者,获取更多数据的成本很高,但正则化有助于避免过度拟合,或者减少网络误差,下面我们就来讲讲正则化的作用原理。

L2正则化

我们用逻辑回归讲解原理。

λ/2m乘以ω范数的平方。ω是欧几里得范数,ω的平方等于ωj (j值从1到nx),也可以表示为ωTω。此方法称为L2正则化。这里的λ就是正则化参数。因为这里使用了欧几里得法线,被称为向量参数ω的L2范式。

为什么只正则化参数ω呢?我们可以加上参数b吗?我们可以这么做,但是一般习惯省略不写。因为ω通常是一个高维参数矢量,已经可以表达高偏差问题,ω可能包含很多参数,我们不可能拟合所有参数,而b只是单个数字,所以ω几乎涵盖所有参数,而不是b。如果加了参数b,没什么影响。因为b也是众多参数的一个,想加就加,没有问题。

L2正则化是最常见的正则化类型,还有L1正则化。L1正则化加的不是L2范式,而是正则项 λ/m乘以$\sum_{j=1}^n $ |ω|,这也被称为参数ω向量的L1范数。(这里的n就是项数,和L2范式的nx一样)

如果用的L1是正则化,最终会是稀疏的,也就是说ω向量中有很多0,有人说这样有利于压缩模型,因为集合中参数均为0,存储模型所占用的内存更少。实际上,虽然L1正则化使模型变得稀疏,却没有降低太多存储内存,所以我认为这并不是L1正则化的目的,至少不是为了压缩模型,人们在训练网络时,越来越倾向于使用L2正则化。

为什么正则化有利于预防过拟合?

如果正则化λ设置得足够大,权重矩阵W被设置为接近于0的值(我也没看懂)。实际上不会发生这种情况的。直观理解就是把多隐藏单元的权重设为0,于是基本上消除了这些隐藏单元的许多影响。如果是这种情况,这个被大大简化了的神经网络会变成一个很小的网络,小到如同一个逻辑回归单元,可是深度却很大,它会使这个网络从过度拟合的状态更接近高偏差状态。

假设我们用tanh作为我们的激活函数,用g(z)表示tanh(z)。那么我们发现,只要z非常小,z只涉及少量参数。这里我们利用双曲正切函数的线性状态,只要z可以扩展为这样的更大值或者更小值,激活函数开始变得非线性。

总结一下,如果正则化参数变得很大,参数W很小,z也会相对变小,此时忽略b的影响,z会相对变小,实际上,z的取值范围很小,这个激活函数,也就是曲线函数tanh会相对呈线性,整个神经网络会计算离线性函数近的值,这个线性函数非常简单,并不是一个极复杂的高度非线性函数,不会发生过拟合。

dropout正则化

除了L2正则化,还有一个非常使用的正则化方法——Dropout(随机失活)。

假设你在训练上图这样的神经网络,它存在过拟合,这就是dropout所要处理的,我们复制这个神经网络,dropout会遍历网络的每一层,并设置消除神经网络中节点的概率。假设网络中的每一层,每个节点都以抛硬币的方式设置概率,每个节点得以保留和消除的概率都是0.5,设置完节点概率,我们会消除一些节点,然后删除掉从该节点进出的连线,最后得到一个节点更少,规模更小的网络,然后用backprop方法进行训练。如下图:

这是网络节点精简后的一个样本,对于其它样本,我们照旧以抛硬币的方式设置概率,保留一类节点集合,删除其它类型的节点集合。对于每个训练样本,我们都将采用一个精简后神经网络来训练它,这种方法似乎有点怪,单纯遍历节点,编码也是随机的,可它真的有效。不过可想而知,我们针对每个训练样本训练规模极小的网络,最后你可能会认识到为什么要正则化网络,因为我们在训练极小的网络。

如何实施dropout?

吴恩达老师讲了最常用的方法。就是inverted dropout(随机失活)。

首先定义变量d,d3表示一个三层的dropout向量:

d3 = np.random.rand(a3.shape[0],a3.shape[1])

然后看它是否小于某数,我们称之为keep-prob,keep-prob是一个具体数字,上个示例中它是0.5,而假设在本例中它是0.8,它表示保留某个隐藏单元的概率,此处keep-prob等于0.8,它意味着消除任意一个隐藏单元的概率是0.2。keep-prob的作用是生成随机矩阵,如果对a3进行因子分解,效果也是一样的。d3是一个矩阵,其中d3中的值为1的概率都是0.8,对应为0的概率是0.2。

接下来要做的就是从第三层中获取激活函数,这里我们叫它a3,a3含有要计算的激活函数,等于上面的a3乘以d3

a3 =np.multiply(a3,d3),这里是元素相乘,也可写为a3*=d3。

它的作用就是让过滤d3中所有等于0的元素,而各个元素等于0的概率只有20%,乘法运算最终把d3中相应元素归零,即让d3中0元素与a3中相对元素归零。

最后,我们向外扩展a3。用它除以0.8,或者除以keep-prob参数。

a3/= (keep-prob)

解释一下最后一步,我们假设第三隐藏层上有50个神经元,保留和删除它们的概率分别为80%和20%,这意味着最后被删除或归零的单元平均有10(50×20%)个。现在我们看下z[4],z[4][4]*a[3]+b[4],我们的预期是,a[3]减少20%,也就是说a[3]中有20%的元素被归零。为了不影响z[4]的期望值,我们需要用ω[4]a[3]/0.8,它将会修正或弥补我们所需的那20%,a[3]的期望值不会变。

它的功能是,不论keep-prop的值是多少0.8,0.9甚至是1,如果keep-prop设置为1,那么就不存在dropout,因为它会保留所有节点。反向随机失活(inverted dropout)方法通过除以keep-prob,确保a[3]的期望值不变。

代码实现如下:

1
2
3
4
5
6
7
8
9
10
Z2 = np.dot(W2, A1) + b2
A2 = relu(Z2)
# 随机生成和 A2 相同维度的矩阵
D2 = np.random.rand(A2.shape[0], A2.shape[1])
# 保留部分元素: 小于keep_prob的设为 True, 否则设为 0
D2 = D2 < keep_prob
# 对应位置的元素相乘, 可以把 True 看做 1, False 看做 0.
A2 = np.multiply(A2, D2)
# 如果没有下面这行就是普通的 Dropout
A2 = A2/keep_prob

要注意的是, 我们不希望在测试的时候, 得到的结果也是随机的, 因此Dropout 在测试过程不使用

理解dropout

Dropout可以随机删除网络中的神经单元,他为什么可以通过正则化发挥如此大的作用呢?

直观上理解:不要依赖于任何一个特征,因为该单元的输入可能随时被清除,所以不愿意给任何一个输入加上太多权重。因此该单元通过这种方式传播下去,并为单元的四个输入增加一点权重,通过传播所有权重,dropout将产生收缩权重的平方范数的效果。和之前讲的L2正则化类似,实施dropout的结果实它会压缩权重,并完成一些预防过拟合的外层正则化。

实施dropout的另一个细节是,这是一个拥有三个输入特征的网络,其中一个要选择的参数是keep-prob,它代表每一层上保留单元的概率。所以不同层的keep-prob也可以变化。第一层,矩阵W[1]是7×3,第二个权重矩阵W[2]是7×7,第三个权重矩阵W[3]是3×7,以此类推,W[2]是最大的权重矩阵,因为W[2]拥有最大参数集,即7×7,为了预防矩阵的过拟合,对于这一层,它的keep-prob值应该相对较低,假设是0.5。对于其它层,过拟合的程度可能没那么严重,它们的keep-prob值可能高一些,可能是0.7甚至更高,假设这里是0.7。如果在某一层,我们不必担心其过拟合的问题,那么keep-prob可以为1。

总结:如果我们担心某些层比其他层更容易发生过拟合,可以把某些层的keep-prob值设置得比其他层更低,缺点是为了使用交叉验证,我们要搜索更多得超级参数。另一种方案是在一些层上应用dropout,而有些层不用dropout应用。dropout的层只含有keep-prob这一个超参数。

dropout一大缺点就是代价函数J不再被明确定义,因为每次迭代都会随机移除一些节点,如果再三检查梯度下降的性能,实际上是很难进行复查的。所以吴恩达老师推荐通常会关闭dropout函数,将keep-prob的值设为1,运行代码,确保J函数单调递减。然后打开dropout函数,希望在dropout过程中,代码并未引入bug。

其它正则化方法

数据扩增

假设我们正在拟合猫咪图片分类器,如果我们想通过扩增训练数据来解决过拟合,但扩增数据代价高,而且有时候我们无法扩增数据,但我们可以通过添加这类图片来增加训练集。例如,水平翻转图片,并把它添加到训练集。所以现在训练集中有原图,还有翻转后的这张图片,所以通过水平翻转图片,训练集则可以增大一倍,因为训练集有冗余,这虽然不如我们额外收集一组新图片那么好,但这样做节省了获取更多猫咪图片的花费。

除了水平翻转图片,你也可以随意裁剪图片,这张图是把原图旋转并随意放大后裁剪的,仍能辨别出图片中的猫咪。

通过随意翻转和裁剪图片,我们可以增大数据集,额外生成假训练数据。和全新的,独立的猫咪图片数据相比,这些额外的假的数据无法包含像全新数据那么多的信息,但我们这么做基本没有花费,代价几乎为零,除了一些对抗性代价。以这种方式扩增算法数据,进而正则化数据集,减少过拟合比较廉价。

对于光学字符识别,我们还可以通过添加数字,随意旋转或扭曲数字来扩增数据,把这些数字添加到训练集,它们仍然是数字。为了方便说明,我对字符做了强变形处理,所以数字4看起来是波形的,其实不用对数字4做这么夸张的扭曲,只要轻微的变形就好,我做成这样是为了让大家看的更清楚。实际操作的时候,我们通常对字符做更轻微的变形处理。因为这几个4看起来有点扭曲。所以,数据扩增可作为正则化方法使用,实际功能上也与正则化相似。

early stopping

因为在训练过程中,我们希望训练误差,代价函数J都在下降,通过early stopping,我们不但可以绘制上面这些内容,还可以绘制验证集误差,它可以是验证集上的分类误差,或验证集上的代价函数,逻辑损失和对数损失等,你会发现,验证集误差通常会先呈下降趋势,然后在某个节点处开始上升。early stopping 代表提早停止训练神经网络。

当我们还未在神经网络上运行太多迭代过程的时候,参数ω接近0,因为随机初始化ω值时,它的值可能都是较小的随机值,所以在我们长期训练神经网络之前ω依然很小,在迭代过程和训练过程中ω的值会变得越来越大,比如在这儿,神经网络中参数ω的值已经非常大了,所以early stopping要做就是在中间点停止迭代过程,我们得到一个ω值中等大小的弗罗贝尼乌斯范数,与L2正则化相似,选择参数ω范数较小的神经网络,但愿那时的神经网络过度拟合不严重。

early stopping 有一个缺点,接下来了解一下。

在机器学习中,超级参数激增,选出可行的算法也变得越来越复杂。在重点优化代价函数时,你只需要留意ω和b,J(ω,b)的值越小越好,你只需要想办法减小这个值,其它的不用关注。然后,预防过拟合还有其他任务,就是减少方差”。

缺点就是我们不能独立地处理这两个问题,因为提早停止梯度下降,也就是停止了优化代价函数J,因为现在你不再尝试降低代价函数J,所以代价函数J的值可能不够小,同时你又希望不出现过拟合,你没有采取不同的方式来解决这两个问题,而是用一种方法同时解决两个问题,这样做的结果是我要考虑的东西变得更复杂。

归一化输入

假设我们有一个数据集,它有两个输入特征,所以数据是二维的。下图即为数据集散点图:

训练神经网络,其中一个加速训练的方法就是归一化输入。假设一个训练集有两个特征,输入特征为2维,归一化需要两个步骤:

  1. 零均值化
  2. 归一化方差;

我们希望无论是训练集还是测试集都是通过相同的μ和σ2定义的数据转换。

第一步是零均值化。

μ=1/m x $\sum_{i=1}^m$x(i)

μ是一个向量,x等于每个训练数据x减去μ,意思是移动数据集,直到完成零均值化。

输入数据经过零均值后,得出下面的散点图:

第二步是归一化方差,注意特征x1的方差比特征x2的方差要大得多。

μ=1/m x $\sum_{i=1}^m$(x(i))2

σ2是一个向量,它的每个特征都有方差。经过归一化方差后,得出下面结果:

如果你用它来调整训练数据,那么用相同的μ和σ2来归一化测试集。尤其是,你不希望训练集和测试集的归一化有所不同,不论的μ值是什么,也不论的σ2值是什么,这两个公式中都会用到它们。所以你要用同样的方法调整测试集,而不是在训练集和测试集上分别预估μ和σ2。因为我们希望不论是训练数据还是测试数据,都是通过相同μ和σ2定义的相同数据转换,其中μ和σ2是由训练集数据计算得来的。

为什么我们需要归一化输入特征?回想一下代价函数

J(ω,b)=1/m x $\sum_{i=1}^m$L(y(i) hat , y(i))

如果输入未归一化的输入特征,代价函数如下:

然而如果归一化特征,代价函数平均起来看更对称,如果要在上图这样的代价函数上运行梯度下降法,必须使用一个非常小的学习率。因为如果是在这个位置(蓝色箭头所在位置),梯度下降法可能需要多次迭代过程,直到最后找到最小值。但如果函数是一个更圆的球形轮廓,那么不论从哪个位置开始,梯度下降法都能够更直接地找到最小值,我们可以在梯度下降法中使用较大步长,而不需要像在左图中那样反复执行。

下图即为输入归一化输入特征的代价函数:

如果输入特征处于不同范围内,可能有些特征值从0到1,有些从1到1000,那么归一化特征值就非常重要了。如果特征值处于相似范围内,那么归一化就不是很重要了。

梯度消失/梯度爆炸

训练神经网络,尤其是深度神经所面临的一个问题就是梯度消失或梯度爆炸,也就是你训练神经网络的时候,导数或坡度有时会变得非常大,或者非常小,甚至于以指数方式变小,这加大了训练的难度。

吴恩达老师给我们的理解是:权重W只比1略大一点(或者说比单位矩阵略大一点),深度神经网络的激活函数将爆炸式增长,如果W比单位矩阵略小一点,激活函数将以指数级递减。

神经网络的权重初始化

为了避免上述的梯度爆炸,权重的初始化很重要。首先,我们来看看只有一个神经元的情况:

假设b=0,可以得到:

Z=W1x1+W2x2+W3x3+……+Wnxn,b=0。

为了不让Z那么大,我们尽量让Wi小一些。实际做法如下:

​ 使用Relu函数,W的初始化方法:

​ 使用tanh函数,W的初始化方法:

其中, n[l−1] 表示第l层的输入特征的个数, 也就是第 l - 1 层神经元的个数.

----本文结束,感谢您的阅读。如有错,请指正。----
大哥大嫂过年好!支持我一下呗
0%