1.4 正则化
正则化应该是你在遇到高方差时首先要想到的方法,虽然另一个方法获得更多数据来增大训练集的数据量也非常好用,但并非时时能如此做,且获取数据的成本通常比较高;
logistic regression中的正则化:
为了简单地说明正则化的原理,我们先采取logistic regression来分析,这是第一章一开始就讲的,现在学了多层神经网络以后回头看,会发现它没有隐藏层,就一个输入层和一个输出层,并且输出层是一个采用函数的节点;
我们的目的是通过训练不断地减小损失函数最后达到最小化
的效果:
想要在logistic regression中加入正则化,只要在它的损失函数中加上一项,变为:
是正则化参数,
是
的欧几里得范数平方,并且右下角的下标2代表这种正则化方法是L2正则化;
为什么只正则化参数
却不对
进行正则化呢?
事实上可以再加一项实现对
正则化,但可以省略,因为
已经是一个高维的参数向量,可以表达高偏差问题,
有很多很多参数,几乎涵盖了所有参数,而相比之下
只是众多参数中的一个数,所以可加可不加;
什么是L1正则化?
L2正则化是最常用的,但可以了解一下L1正则化,它加的项改为:
但是,使用L1正则化会使最终计算完的变为稀疏的
,意思是
向量中会有很多的0值,有些人认为这能压缩模型,实际上,
稀疏后并没有降低太多存储内存,所以压缩模型并非L1正则化的目的;人们更倾向于使用L2正则化而不是L1正则化;
参数λ的细节:
是一个超参数,通常使用开发集来配置
,尝试各种数据来寻找最优参数值,要考虑训练集之间的权衡,把参数正常值设为比较小的值,避免过拟合;
在Python编码中,lambda是一个保留字段,所以要删掉末尾的a写为避免冲突;
多层神经网络中的正则化:
多层神经网络的损失函数是一个和每一层的都有关的函数,所以写为:
正则化的方法是给损失函数添加一项:
这个矩阵范数叫“弗罗贝尼乌斯范数”
,基于
是一个
维度的矩阵,所以这个矩阵范数被定义为矩阵中所有元素的平方求和:
如何使用Frobenius范数实现梯度下降呢?
首先要利用back prop计算出的值,并给
加上正则项,然后再更新
:
也就是说新定义的会含有成本函数的偏导数和额外的正则项;
为何L2正则化会被成为“权重衰减”
呢?
无论
是什么,我们通过乘以一个
就能让它变得更小。
1.5 为什么正则化可以减少过拟合?
我们通过两个例子直观理解这一点;
例子1:一个庞大的深度拟合神经网络
想象一下这是一个过度拟合的神经网络,损失函数加入了正则项,记为:
直观上理解,如果被设置得很大很大,权重矩阵
就会由于前面谈到的“权重衰减”也就是系数
的存在而设置为接近0矩阵,
,这意味着可能很多隐藏单元的权重就被设为0了,应该还记得前面的公式
其中的就是指第一层从上往下的第一个单元所带的参数;
于是,基本上就消除了这些隐藏单元的许多影响,最终神经网络被大大简化为一个很小的神经网络,也许就是一个长得很像逻辑回归但深度很大的网络,这时会让高方差状态转变为高偏差状态:
但这只是因为被设置得太大了,实际上
存在一个中间值能让网络的高方差状态变为适度拟合的状态;
这一段带一点玄学意味在里面,比如老吴这么说:
直觉上我们认为大量隐藏单元被完全消失了,其实不然,实际上神经网络的所有隐藏单元仍然存在,但它们的影响变小了。
例子2:假设我们使用双曲正切激活函数
我们发现只要很小,只在0附近的一小段参数以内,那么
就接近于线性状态,而只要
超出了这段参数,
就开始变得非线性;
如果很大,那么
会变得很小,进一步由于
就会使
也变得很小,最终落在了0附近的参数,使激活函数
接近于线性状态,我们必须有一个直觉就是非线性环节或函数让整个模型变得复杂和容易过拟合,而如果变得接近于或干脆成为线性的环节、函数,整个模型就很简单并且很难过拟合,方差也就自然缩小了;
所以每一层几乎都是线性的,最后会达到类似线性网络的效果,这种网络的特点是即使网络很深,也最终只能计算线性函数,不适合复杂决策以及过度拟合数据集的非线性决策边界;
执行正则化的建议:
正则项的添加就是为了防止权重过大,如果我们使用梯度下降法,那么在调试梯度下降时,有一步非常重要,就是要将成本函数设计为含有正则项的:
我们将梯度下降法的次数叫做梯度下降的调幅数量,只有将正则项加入成本函数中,我们才能看到那种针对每个调幅成本函数都下降的情况:
如果忘记加正则项,可能导致无法看到这种单调递减的图像,而是有噪音或其他情况的图像;
1.6 dropout正则化
dropout也叫随机失活,假设一个神经网络存在过拟合,如图所示,dropout将会遍历网络的每一层,并设置消除神经网络中节点的失活概率;如果网络中每一个节点的失活概率都是0.5,那么每个节点保留和消除的概率都是50%,当设置完节点概率以后就会消除这个网络的一些节点,然后删除从这些节点进出的连线,最终得到一个节点更少、规模更小的网络:
我们就在这样一个精简过节点的样本上用backproop进行训练;
而对于其他样本,我们可以依然保持每个节点50%失活的概率,保留一些节点,删掉其他的,对每一个样本我们都通过dropout精简了网络以后再去训练它;
这种方法奇怪的点在于,它单纯只是遍历所有节点,编码也是随机的,但很有效果;
如何实施dropout呢?
最常用的一种方法是“反向随机失活”,用一个多层网络举例,只说明它在第三层对单个样本如何实现dropout:
(老吴这个课上到这里,才知道原来要有一定编程基础,像我这样对python是0基础的,就先用各种gpt就行了吧,直接问代码是什么意思就行,文心一言挺好用的)
首先定义
,它是一个三层的dropout向量:
是NumPy的一个函数,用于生成给定形状的矩阵,矩阵中的每个元素都是从0到1的随机浮点数;
分别获取矩阵
即第三层输出的激活函数值
的第一维(通常是行)和第二维(通常是列)的大小;
是一个介于0和1之间的浮点数,表示保留元素的概率,本例中将
设为0.8,所以消除任意一个隐藏单元的概率是0.2,而前面的例子中
是0.5;
是一个比较操作,它将上一步生成的随机数组中的每个元素与
进行比较,比较操作的结果是一个布尔矩阵,其中元素值为
(即1)表示对应位置的随机数小于
,
(即0)表示不小于
;
最后,将比较操作的结果即布尔矩阵赋值给变量;
仔细想想上述的思路是不是这样:为了对第三层的每个节点进行随机失活,还记得是什么吗?
(1)是一个
维的矩阵,这里
对应的就是第三层从上往下数第一个节点对应的输出函数值,其他类似,都是实数,我们对
进行一定概率的随机赋0就是我们对第三层的每个节点进行随机失活;
(2)我们进行随机失活的具体操作是什么呢?就是先生成一个和一样维度的矩阵,要求这个矩阵必须所有元素的值是在0~1之间的随机数,这样每个位置的元素不就能有80%的概率是0~0.8了么?接着拿这个矩阵和
进行比较,就会自动将每个矩阵的元素和0.8比较,小于0.8的认为满足
条件,为
,赋值为1;而大于0.8的认为不满足
条件,为
,赋值为0;这样不就创造出一个和
一样维度的矩阵,每个位置的元素有80%概率变成1,有20%概率变为0了吗?
(3)将随机浮点数矩阵转变为所有元素都是0和1的布尔矩阵是为了干啥?就是为了拿布尔矩阵和逐元素相乘嘛,一乘,就能让某些位置的节点失活,也就是激活函数值变为0,而另外一些节点就保留,也就是激活函数值仍保持原来的数值;而且(2)中的随机生成浮点数矩阵+通过与设定的
进行比较两个操作将其转换为布尔矩阵,两个操作就已经实现了“按一定概率失活”的目的了;
然后我们要将
与
进行逐元素相乘,来更新
使之达到随机失活:
这里就是让两个同维度矩阵进行逐元素相乘,即
布尔矩阵
;
虽然在Python中布尔矩阵严格说所有值是两种,但实际运算中会默认
进行运算;
最后我们要向外扩展
,为什么呢?
实际上早期的dropout版本里都没有进行这一步,这使得测试阶段的平均值变得越来越复杂;
方便起见假设第三层上有50个单元,保留的概率是80%,删除的概率是20%,那么最后被删除或归零的单元平均有10个,而,我们的预期是
会减少20%,即
中的20%元素会归零;为了不影响
的期望值,我们要做的就是
,这将修正或弥补我们所需的那20%,让
的期望值不变;
值得注意的是,如果将设置为1,那么相当于保留所有节点,没有dropout;
反向随机失活通过除以来确保
的期望不变,事实证明在测试阶段,当我们评估一个神经网络时,这种做法让测试阶段变得更加容易,因为它的数据扩展问题变得更少;
你会发现对于不同的训练样本,清除的隐藏单元也不一样:
实际上,如果你通过相同训练集多次传递数据,每次训练数据的梯度不同,则随机对不同的隐藏单元归零;而有时又并非如此,需要将相同隐藏单元归零,第一次梯度下降迭代时,把一些隐藏单元归零,第二次梯度下降时也就是遍历训练集时,你可能会对不同类型的隐层单元归零;
向量(注意因为它与
保持相同维度)用来决定第三层中哪些单元归零,无论用for prop还是back prop,这里只展示了for prop;
如何在测试阶段训练算法?
在测试阶段,我们已经给出了,或者是想要预测的变量,用的是标准计数法,我们将测试样本记为
,并且在测试阶段我们不使用dropout,然后进行前向传播:
直到得到预测值;测试阶段自然不用决定要消除哪些隐藏单元,不用dropout是因为在测试阶段进行预测时,我们不希望输出结果是随机的,如果应用了dropout,预测就会受到干扰,理论上,你只要多次运行预测处理过程,每一次不同的隐藏单元会被随机归零,预测处理遍历它们,计算结果差不多,但计算效率低(老吴这里没讲的太清楚,意思就是,一般不要在测试阶段用dropout,如果要在测试阶段用dropout,那就得多次计算dropout的模型,然后对
取平均值);
反向随机激活的第三步也就是除以那一步对测试阶段也是大有益处的,即使在测试阶段选择不用dropout,激活函数的预期结果也并不会发生变化,所以就不用在测试阶段额外添加尺度参数了。(试想一下要是没有除以
,是不是测试的时候就得加一些
去使得测试阶段激活函数的期望能跟训练时的期望匹配呢?)
1.7 理解dropout
似乎使用dropout每次迭代后,神经网络都会变得比以前更小;
使用一个较小的神经网络是否和dropout正则化的效果一样呢?
第二个直观认识从单个神经元入手,这个神经元的工作就是接受输入并生成一些有意义的输出,通过dropout,这个神经元的输入几乎都可以被消除,随机地消除随机个数的输入,所以这个单个的神经元就不能依赖任何特征,任何特征都可能会被随机消除;我们不愿意将所有赌注都放在一个节点上,不愿给任何一个输入加上太多的权重,因此该单元将通过这种方式积极地传播开来,并为单元的四个输入各增加一点点权重;
通过传播所有权重,dropout将产生和收缩权重的平方范数类似的效果,就和L2正则化类似,实施dropout的结果是它会压缩权重,并完成一些预防过拟合的外层正则化,事实上dropout就是正则化的一种有效代替形式;而L2正则化对不同权重的衰减是不同的,它取决于倍增的激活函数的大小;总的来说dropout和L2正则化效果很像,但区别在于L2正则化被应用方式不同结果也会有所不同,而dropout更适用于不同的输入范围;
使用dropout的另一点细节:
假设一个神经网络如上图所示,其中一个要选择的参数就是,实际上不同层的
也可以变化,分析每一层
的维数:
所以实际上就是最大的权重矩阵,拥有
个参数,为了预防矩阵的过拟合,对第二层的
可能要设置得比较低,比如取0.5;而其他层的过拟合程度可能没那么严重,
的值可能设置得比较高,比如取0.7;而如果像第四层我们不需要担心它过拟合的问题,直接将这一层的
设置为1也是可以的;
技术上我们还可以对输入层应用dropout,随机删除一个或多个输入特征,但通常不建议这么做,应该使输入层的dropout=1或0.9这样较大的数字;
虽然对不同层应用不同的dropout,对权重矩阵较大的层用更低的能更好地防止过拟合,但缺点是你要使用开发集搜索更多的超参数,也就是更多的
;
另一种替代方案是在一些层上使用dropout,而另一些层不适用dropout,并且应用dropout的层只有一个超参数就是;
两个实施dropout过程中的技巧:
计算机视觉领域输入量十分大,因此需要更加庞大的权重矩阵,以为着更多要训练的参数,而现有的数据量运输能力不足,所以很多人经常使用dropout,但要记住一点,dropout可以算是一种正则化方法,是为了防止过拟合的,除非算法过拟合了,否则不要使用dropout;
dropout的一大缺点就是成本函数不再被明确定义,每次迭代都会随机消除某些节点,如果再三检查梯度下降的性能是很困难的,只有定义明确的代价函数才会在每次迭代后不断下降,而失去明确定义后,我们就失去了调试工具去绘制一个梯度下降迭代中成本函数函数值不断下降的图像;
所以我们在调试时要经常关闭dropout,或者让,确保成本函数随迭代次数单调递减,然后再打开dropout,在dropout代码过程中代码并未引入bug,我们没有关于这些方法的性能数据统计,但你可以把它们与dropout方法一起使用;
1.8 其他正则化方法
解决过拟合的一个重要方法就是获得更多训练数据,增大训练集的规模,而普通的扩增训练数据成本昂贵,因此,数据扩增成为一种常用的扩增训练数据的方法;
比如,通过将图片进行水平翻转,并将其添加到训练集中,就可以扩大一倍的训练集;虽然此时的训练集有些冗余,不如我们搜集另一组新图那么好,但节省了很多成本;还可以随意裁剪、旋转图片,额外生成假训练数据;这么做几乎没有成本,只会有一些对抗性代价;像这样人工合成数据的话,我们要通过算法验证,即猫经过水平翻转后仍然是猫;
而对于光学字符识别,我们还可以通过添加数字、随意旋转或随意轻微扭曲数字来扩增训练数据:
提前停止训练
也是一种减少过拟合的方法:
运行梯度下降时我们可以绘制训练误差,或者绘制成本函数的优化过程,在训练集上用0-1记录分类误差次数,呈单减趋势;训练过程中我们希望训练误差和成本函数都在不断下降;而应用“提前停止训练”时,我们还可以绘制开发集误差,它可以是开发集上的分类误差、成本函数、逻辑损失、对数损失……我们将发现开发集误差通常是先下降,然后在某个节点处开始上升,因此“提前停止训练”就是在迭代过程的某个点神经网络已经表现得很好,于是就在此处提前停止训练;
它是怎么发挥作用的?
当你还未在神经网络上运行太多迭代过程时,由于随机初始化一般将中的参数设置为很小的值,所以参数
解决于0矩阵;但在迭代和训练的过程中,
会变得越来越大,我们所要做的就是在中间某个点停止迭代过程,得到一个
值中等大小的
,这与L2正则化选择
范数较小的神经网络类似;
但是提前训练也有缺点:
机器学习包括几个步骤,其中一步就是选择一个算法来优化成本函数,优化算法有梯度下降法、Momentum、RMSprop、Adam等;但是优化了成本函数
后我们也不想发生过拟合,有一些工具可以解决此类问题,比如正则化和扩增数据;现在机器学习中的超参数激增,选出可行的算法也越来越复杂,但我们发现:只要我们用一组工具优化成本函数
,机器学习就会变得更简单,在重点优化成本函数
时只要留意
使
变得越小越好;
预防过拟合还有其他任务,即减小方差,一般我们这一步会用另一套工具实现,即:
“正交化”
:
它的思路就是在一个时间做一个任务;而提前停止训练最大的缺点就是它并不能实现和正交化一样的效果,无法独立地去解决两个问题(过拟合问题和足够次数的梯度下降问题),提前停止训练也就提前停止了梯度下降,也就停止了优化函数,意味着的值可能并不够小;这在第三章老吴举电视机的例子里会讲得更加清楚;
用L2正则化可能会让神经网络的训练时间变得更长,这导致超参数搜索空间更加容易分解,也更容易搜索;缺点是你要尝试很多值,导致搜索大量
值的计算代价太高;
而提前停止训练的优点是只运行一次梯度下降,就可以找出的较小值、中间值、较大值,无需尝试很多
值