3.2 深度学习正则化

3.2 深度学习正则化

学习目标

  • 目标
    • 知道偏差与方差的意义
    • 掌握L2正则化与L1正则化的数学原理
    • 掌握droupout原理以及方法
    • 知道正则化的作用应用
    • 知道常用的一些神经网络超参数
    • 知道BN层的意义以及数学原理
  • 应用
    • 应用完成dropout的实现

3.2.1 偏差与方差

3.2.1.1 数据集划分

首先我们对机器学习当中涉及到的数据集划分进行一个简单的复习
  • 训练集(train set):用训练集对算法或模型进行 训练 过程;
  • 验证集(development set):利用验证集(又称为简单交叉验证集,hold-out cross validation set)进行 交叉验证 选择出最好的模型
  • 测试集(test set):最后利用测试集对模型进行测试,对学习方法进行评估。
小数据量 的时代,如 100、1000、10000 的数据量大小,可以将数据集按照以下比例进行划分:
  • 无验证集的情况:70% / 30%
  • 有验证集的情况:60% / 20% / 20%
而在如今的 大数据时代 ,拥有的数据集的规模可能是百万级别的,所以验证集和测试集所占的比重会趋向于变得更小。
  • 100 万数据量:98% / 1% / 1%
  • 超百万数据量:99.5% / 0.25% / 0.25%
以上这些比例可以根据数据集情况选择。

3.2.1.2 偏差与方差的意义

“偏差-方差分解”(bias-variance decomposition) 是解释学习算法泛化性能的一种重要工具。
泛化误差可分解为偏差、方差与噪声, 泛化性能 是由 学习算法的能力 数据的充分性 以及 学习任务本身的难度 所共同决定的。
  • 偏差 :度量了学习算法的期望预测与真实结果的偏离程度,即刻画了 学习算法本身的拟合能力
  • 方差 :度量了同样大小的训练集的变动所导致的学习性能的变化,即刻画了 数据扰动所造成的影响
  • 噪声 :表达了在当前任务上任何学习算法所能够达到的期望 泛化误差的下界 ,即刻画了 学习问题本身的难度
那么偏差、方差与我们的数据集划分到底有什么关系呢?
  • 1、训练集的错误率较小,而验证集/测试集的错误率较大, 说明模型存在较大方差,可能出现了过拟合
  • 2、训练集和测试集的错误率都较大,且两者相近, 说明模型存在较大偏差,可能出现了欠拟合
  • 3、训练集和测试集的错误率都较小,且两者相近,说明方差和偏差都较小,这个模型效果比较好。
所以我们最终总结, 方差一般指的是数据模型得出来了,能不能对未知数据的扰动预测准确 。而 偏差说明在训练集当中就已经误差较大了,基本上在测试集中没有好的效果。
所以如果我们的模型出现了较大的方差或者同时也有较大的偏差,该怎么去解决?

3.2.1.3 解决方法

对于高方差,有以下几种方式
  • 获取更多的数据,使得训练能够包含所有可能出现的情况
  • 正则化(Regularization)
  • 寻找更合适的网络结构
对于高偏差,有以下几种方式
  • 扩大网络规模,如添加隐藏层或者神经元数量
  • 寻找合适的网络架构,使用更大的网络结构,如AlexNet
  • 训练时间更长一些
不断尝试,直到找到低偏差、低方差的框架。

3.2.2 正则化(Regularization)

正则化 即在成本函数中加入一个正则化项(惩罚项),惩罚模型的复杂度,防止网络过拟合

3.2.2.1 L1与L2正则化(复习)

3.2.2.2 正则化项的理解

3.2.2.3 神经网络中的正则化

3.2.2.4 L1与L2正则化为什么能够防止过拟合

3.2.3 Droupout正则化

翻译解释:
1、过拟合是一个严重的问题。大型网络的使用速度也较慢,这使得在测试时结合许多不同的大型神经网络的预测来处理过拟合问题变得非常棘手。Dropout是解决这个问题的一种技巧。关键的想法是在训练过程中,从神经网络中随机丢弃神经元(以及它们的连接)
2、在训练过程中,dropout技巧会从指数级的的不同的“稀疏”网络中抽取样本。在测试时,就可以很容易地估计出所有这些稀疏网络的 预测结果的平均 。这显著地减少了过拟合,并且比其他正则化方法有了很大的改进
3、drop改进了神经网络在视觉、语音识别、文档分类和计算生物学等监督学习任务上的性能,获得了许多基准数据集state-of-the-art结果。
  • 定义:Droupout是随机的对神经网络每一层进行丢弃部分神经元操作。
对于网络的每一层会进行设置保留概率,即keep_prob。假设keep_prob为0.8,那么也就是在每一层所有神经元有20% 的概率直接失效,可以理解为0.

3.2.3.1 Dropout 模型描述

下面我们来讲解dropout的工作过程
  • 1、训练过程
    • 1、神经元随机失效,概率为P
    • 2、并且在神经元存在且工作的状态下,权重才会更新,权重更新的越多理论上会变得更大
  • 2、测试过程
    • 1、神经元随机失效,概率为0
    • 2、所有的神经元都会参与计算,大于训练时候的任意一个模型的计算量
  • 3、模型过程伪代码过程
讲解:
 
 
  • 4、结构代码实现-(inverted dropout)
实现随机失活算法,最常用的一种是反向随机失活(inverted dropout) ,这种方式会对每层进行如下代码操作
def dropout (x, level) :
if level < 0. or level >= 1 :
raise Exception( 'dropout保持概率在0到1之间' )
sample = np.random.binomial(n= 1 , p=level, size=x.shape)
print(sample)
x *= sample
x /= level
return x
x = np.asarray([ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ], dtype=np.float32)
dropout(x, 0.8 )
 
# 其中这步骤在计算的时候对余下的非0的进行扩大倍数,因为p<0。0/x=0,所以0不影响,训练的时候代码可以直接写上
x /= level
  • 利用inverted dropout,我们可以在训练的时候直接将dropout后留下的权重扩大1/p 倍,这样就可以使结果的scale保持不变, 而在预测的时候也不用做额外的操作了,更方便一些。

问题:为什么需要去做rescale(重要)

  • 解释原理:
通俗解释:训练的时候只有占比为p 的隐藏层单元参与训练,那么在预测的时候,如果所有的隐藏层单元都需要参与进来,则得到的结果相比训练时平均要大1/p ,为了避免这种情况,就需要测试的时候 将输出结果乘以 1/p 使下一层的输入规模保持不变
数学解释:试的时候不去随机失活?所以为了保证训练和预测的时候期望一样,必须得做scale; 我们设置dropout probability为p, 那么该层大约有比例为p的单元会被drop掉,因为每个神经元是否drop就是一次伯努利实验,这层的dropout概率服从伯努利分布,而分布的期望就是np。
例子:
 

1、使用dropout对特征的影响

左边没有dropout的情况下,每个单元并没有很明确地要去检测某个图像特征,这是由于单元之间的相互适应性太强,需要组合在一起才能比较好地工作,但是就右图有dropout的情况而言很明显可以看出,每个单元都在检测一种边缘、笔画或者点,也就是说每个单元之间没有那么强的相互适应性。 泛化能力会强很多

2、使用dropout对参数稀疏程度影响

 

 
dropout对隐层激活值稀疏性的影响,两种模型均使用ReLU。左:平均激活度的直方图显示大多数单位的平均激活度约为2.0。激活的直方图显示了一个远离零的巨大模式。显然,很大一部分单元具有很高的活化度。右图:平均激活度的直方图显示,大多数单位的平均激活度较小,约为0.7。可以看出加入了dropout以后激活值的分布向0靠近, 稀疏性明显增加

3、dropout 率大小影响

论文坐着做了两种尝试,如果保持隐藏层神经元个数n固定的时候,p的值影响如左图表示,取值0.5~0.8合适;如果是pn保持不变的时候, P也是在0.5到0.8之间比较合适 。(在实验的时候做了pn=256第一层网络和pn=512第二层网络进行的测试)

3.2.3.2 Droupout为什么有效(如何理解)

  • 动机
    • 1、Dropout的动机来自于关于性别在进化中的作用的理论。
      • 有性生殖包括从一个亲本和另一个亲本中提取一半的基因,加入非常少量的随机突变,并将它们结合产生受精卵。
      • 无性繁殖是通过父母基因的拷贝中加入微小突变来创造后代。
      • 结果:中大型动物中,一般是有性繁殖,有性繁殖是指后代的基因从父母两方各继承一半。但是从直观上看,似乎无性繁殖更加合理,无性繁殖可以保留大段大段的优秀基因。
      • 原因:有性繁殖则将基因随机拆了又拆,破坏了大段基因的联合适应性。 自然选择中毕竟没有选择无性繁殖,而选择了有性繁殖,  因此一些基因必须要自己学会做一些事而不只是跟很多其他基因合作,这种合作会减少个体适应性。类似地,随机的选择dropout可以增加隐层神经元的健壮性。
  • 解释
    • 、减少神经元之间复杂的共适应性。当隐藏层神经元被随机删除之后,使得全连接网络具有了一定的稀疏化,从而有效地减轻了不同特征的协同效应。也就是说,有些特征可能会依赖于固定关系的隐含节点的共同作用,而通过Dropout的话,它强迫一个神经单元,和随机挑选出来的其他神经单元共同工作,达到好的效果。消除减弱了神经元节点间的联合适应性,增强了泛化能力。
    • 2、Dropout可以看作是一种随机正则化技术,加入了 dropout 后,输入的特征都存在被随机清除的可能,所以该神经元不会再特别依赖于任何一个输入特征,也就是不会给任何一个输入特征设置太大的权重。通过传播过程,dropout 将产生和 L2 正则化相同的 收缩权重 的效果。
      • 对于不同的层,设置的keep_prob大小也不一致,神经元较少的层,会设keep_prob为 1.0,而神经元多的层则会设置比较小的keep_prob

3.2.3.3 Dropout实用指南

论文中给出了相关dropout时间的注意事项
  • 1、 dropout会增加训练的时间 ,通常带有dropout的网络会比不带的标准网络需要2~3倍的训练时间。
    • 主要原因在于参数更新非常多,每次训练都要训练不同失去神经元的结构。因此,不是最后一次计算出的梯度需要在测试期间使用。使用的dropout率越高,所需要的训练时间越长,所以需要在训练时间和防止过拟合之间做出权衡。
    • 对于线性回归可以使用L2正则化等方法,对于更加复杂的结构,使用具体那种正则化方法不是很明确。
  • 2、因为dropout在梯度下降中 引入了大量的噪声导致梯度相互抑制 ,因此 学习速率要增加10-100倍 。另外一个减少噪声的方法是用momentum,momentum对于标准网络一般采用0.9,对于dropout网络一般是0.95-0.99。两个方法可以同时采用
  • 3、 防止学习过快导致网络增长太大 ,一般给隐藏层权重的norm一个上限c,c一般取值3-4
    • dropout和max-normalization、large decaying learning rates and high momentum等组合起来效果更好
  • 4、Dropout Rate: 一般取值0.5-0.8之间 ,drop比例p越小,要求隐含层n越大,训练也会越慢
  • 5、调试时候使用技巧:
    • 先确定网络没问题 ,再打开dropout训练测试:dropout 的缺点是成本函数无法被明确定义,因为每次会随机消除一部分神经元,所以参数也无法确定具体哪一些,在反向传播的时候带来计算上的麻烦,也就无法保证当前 网络是否损失函数下降的 。如果要使用droupout,会先关闭这个参数,保证损失函数是单调下降的,确定网络没有问题,再次打开droupout才会有效。

 

3.2.4 案例:DropOut机制与L2正则化实现

1、目的

实现下面模型中引入DropOut机制以及L2正则化机制,提高模型效果。
# LINEAR -> RELU -> LINEAR -> RELU -> LINEAR -> SIGMOID
z1 = np.dot(W1, X) + b1
a1 = relu(z1)
z2 = np.dot(W2, a1) + b2
a2 = relu(z2)
z3 = np.dot(W3, a2) + b3
a3 = sigmoid(z3)
引入DropOut机制需要做的事情有, 前向传播的随机失活,在反向计算损失时候的处理以及在预测阶段的实现。

2、步骤分析

  • 1、数据读取、模型前向与反向传播代码封装
  • 2、在前向传播与反向传播中插入DropOut机制代码
  • 3、实现L2正则化机智的前向与反向传播

3、代码实现

2、在前向传播与反向传播中插入DropOut机制代码
代码实现
def forward_propagation_with_dropout (X, parameters, keep_prob= 0.5 ) :
"""
带有dropout的前向传播
"""
W1 = parameters[ "W1" ]
b1 = parameters[ "b1" ]
W2 = parameters[ "W2" ]
b2 = parameters[ "b2" ]
W3 = parameters[ "W3" ]
b3 = parameters[ "b3" ]
 
# LINEAR -> RELU -> LINEAR -> RELU -> LINEAR -> SIGMOID
# 计算第一层输出,对余下的非0的进行扩大倍数,因为p<0。0/x=0,所以0不影响
Z1 = np.dot(W1, X) + b1
A1 = relu(Z1)
 
D1 = np.random.binomial(n= 1 , p=keep_prob, size=A1.shape)
 
A1 = np.multiply(A1, D1)
 
A1 /= keep_prob
 
# 计算第二层输出
Z2 = np.dot(W2, A1) + b2
A2 = relu(Z2)
 
D2 = np.random.binomial(n= 1 , p=keep_prob, size=A2.shape)
 
A2 = np.multiply(A2, D2)
 
A2 /= keep_prob
 
# 最后一层输出
Z3 = np.dot(W3, A2) + b3
A3 = sigmoid(Z3)
 
cache = (Z1, D1, A1, W1, b1, Z2, D2, A2, W2, b2, Z3, A3, W3, b3)
 
return A3, cache
 
 
def backward_propagation_with_dropout (X, Y, cache, keep_prob) :
"""
droupout的反向传播
需要随机失活神经元,同样对输出做扩大
"""
 
m = X.shape[ 1 ]
(Z1, D1, A1, W1, b1, Z2, D2, A2, W2, b2, Z3, A3, W3, b3) = cache
 
dZ3 = A3 - Y
dW3 = 1. / m * np.dot(dZ3, A2.T)
db3 = 1. / m * np.sum(dZ3, axis= 1 , keepdims= True )
dA2 = np.dot(W3.T, dZ3)
dA2 = np.multiply(dA2, D2)
dA2 /= keep_prob
 
dZ2 = np.multiply(dA2, np.int64(A2 > 0 ))
dW2 = 1. / m * np.dot(dZ2, A1.T)
db2 = 1. / m * np.sum(dZ2, axis= 1 , keepdims= True )
 
dA1 = np.dot(W2.T, dZ2)
dA1 = np.multiply(dA1, D1)
dA1 /= keep_prob
 
dZ1 = np.multiply(dA1, np.int64(A1 > 0 ))
dW1 = 1. / m * np.dot(dZ1, X.T)
db1 = 1. / m * np.sum(dZ1, axis= 1 , keepdims= True )
 
gradients = { "dZ3" : dZ3, "dW3" : dW3, "db3" : db3, "dA2" : dA2,
"dZ2" : dZ2, "dW2" : dW2, "db2" : db2, "dA1" : dA1,
"dZ1" : dZ1, "dW1" : dW1, "db1" : db1}
 
return gradients
3、实现L2正则化机智的前向与反向传播
def compute_cost_with_regularization(A3, Y, parameters, lambd):
    """
    损失函数中增加L2正则化
    """
    m = Y.shape[1]
    W1 = parameters["W1"]
    W2 = parameters["W2"]
    W3 = parameters["W3"]
 
    # 1、计算交叉熵损失
    cross_entropy_cost = compute_cost(A3, Y)
 
    # 2、加入L2正则化的损失部分
    L2_regularization_cost = (1. / m) * (lambd / 2) * (np.sum(np.square(W1)) + np.sum(np.square(W2)) + np.sum(np.square(W3)))
 
    # 两个损失进行合并,完整损失
    cost = cross_entropy_cost + L2_regularization_cost
 
    return cost
 
 
 
def backward_propagation_with_regularization(X, Y, cache, lambd):
    """
    对增加了L2正则化后的损失函数进行反向传播计算
    """
 
    m = X.shape[1]
    (Z1, A1, W1, b1, Z2, A2, W2, b2, Z3, A3, W3, b3) = cache
 
    # 含有正则化的反向传播公式
    # z = a - y
    # # dw = 1/ m * ((dz*A.T)+ lambd * w)
    dZ3 = A3 - Y
    dW3 = 1. / m * (np.dot(dZ3, A2.T) + lambd * W3)
    db3 = 1. / m * np.sum(dZ3, axis=1, keepdims=True)
 
    dA2 = np.dot(W3.T, dZ3)
    dZ2 = np.multiply(dA2, np.int64(A2 > 0))
    dW2 = 1. / m * (np.dot(dZ2, A1.T) + lambd * W2)
    db2 = 1. / m * np.sum(dZ2, axis=1, keepdims=True)
 
    dA1 = np.dot(W2.T, dZ2)
    dZ1 = np.multiply(dA1, np.int64(A1 > 0))
    dW1 = 1. / m * (np.dot(dZ1, X.T) + lambd * W1)
    db1 = 1. / m * np.sum(dZ1, axis=1, keepdims=True)
 
    gradients = {"dZ3": dZ3, "dW3": dW3, "db3": db3, "dA2": dA2,
                 "dZ2": dZ2, "dW2": dW2, "db2": db2, "dA1": dA1,
                 "dZ1": dZ1, "dW1": dW1, "db1": db1}
 
    return gradients
运行效果
# dropout
迭代次数为 0 : 损失结果大小: 0.6616994233431039
迭代次数为 10000 : 损失结果大小: 0.20018054682333422
迭代次数为 20000 : 损失结果大小: 0.197457875306683
训练集的准确率:
Accuracy: 0.9383886255924171
测试集的准确率:
Accuracy: 0.95 # 正则化
迭代次数为 0 : 损失结果大小: 0.6543912405149825
迭代次数为 10000 : 损失结果大小: 0.0
迭代次数为 20000 : 损失结果大小: 0.0
训练集的准确率:
Accuracy: 0.9289099526066351
测试集的准确率:
Accuracy: 0.95
 
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值