美文网首页
神经网络优化1

神经网络优化1

作者: 疯了个魔 | 来源:发表于2018-12-10 12:01 被阅读0次

    数据划分

    数据集分类

    通常会将数据集分层三类:

    • 训练集(Training Sets):采用训练集进行训练时,通过改变几个超参数的值,将会得到几种不同的模型。
    • 验证集(Development Sets):开发集又称为交叉验证集(Hold-out Cross Validation Sets),它用来找出建立的几个不同模型中表现最好的模型。
    • 测试集(Test Sets):对算法的好坏程度做无偏估计。
      注意:
    • 训练集和测试集的数据来自同一分布。
    • 测试集的目的是对最终所选定的神经网络系统做出无偏估计,如果不需要无偏估计,也可以不设置测试集。在不设置测试集时,常把验证集称为测试集。

    数据集划分原则

    小数据量(10000以内):

    • 三七分:70%训练集,30%测试集。
    • 六二二:60%训练, 20%验证和 20%测试集。
      大数据(百万级):
      练集可以占到 99.5%,验证和测试集各占 0.25%,或者验证集占 0.4%,测试集占 0.1%。

    模型估计

    分类问题
    左图,对图中的数据采用一个简单的模型,例如线性拟合,并不能很好地对这些数据进行分类,分类后存在较大的偏差(Bias),称这个分类模型欠拟合(Underfitting)
    右图,采用复杂的模型进行分类,例如深度神经网络模型,当模型复杂度过高时变容易出现过拟合(Overfitting),使得分类后存在较大的方差(Variance)
    中间的图中,采用一个恰当的模型,才能对数据做出一个差不多的分类。
    通常采用开发集来诊断模型中是否存在偏差或者时方差:
    偏差和方差
    • 当训练出一个模型后,发现训练集的错误率较小,而开发集的错误率却较大,这个模型可能出现了过拟合,存在较大方差;
    • 发现训练集和开发集的错误率都都较大时,且两者相当,这个模型就可能出现了欠拟合,存在较大偏差;
    • 发现训练集的错误率较大时,且开发集的错误率远较训练集大时,这个模型就有些糟糕了,方差和偏差都较大。
    • 只有当训练集和开发集的错误率都较小,且两者的相差也较小,这个模型才会是一个好的模型,方差和偏差都较小。
      模型存在较大偏差时,可采用增加神经网络的隐含层数、隐含层中的节点数,训练更长时间等方法来预防欠拟合。
      存在较大方差时,则可以采用引入更多训练样本、对样本数据正则化(Regularization)等方法来预防过拟合。

    预防过拟合

    L2正则化

    向Logistic回归的成本函数中加入L2正则化(也称“L2范数”)项:
    {J(w,b) = \frac{1}{m} \sum_{i=1}^m \mathcal{L}({\hat y}^{(i)}, y^{(i)}) + \frac{\lambda}{2m}||w||_2^2}
    其中:
    ||w||_2^2 = \sum_{j=1}^n{w_j^2} = w^T w
    L2正则化是最常用的正则化类型,也存在L1正则化项:
    \frac{\lambda}{m}||w||_1 = \frac{\lambda}{m}\sum_{j=1}^n |w_j|
    由于L1正则化最后得到w向量中将存在大量的0,使模型变得稀疏化,所以一般都使用L2正则化。其中的参数λ称为正则化参数,这个参数通常通过开发集来设置。
    向神经网络的成本函数加入正则化项:
    {J(w^{[1]},b^{[1]},…,w^{[L]},b^{[L]}) = \frac{1}{m} \sum_{i=1}^m \mathcal{L}({\hat y}^{(i)}, y^{(i)}) + \frac{\lambda}{2m} \sum\limits_{l=1}^L ||w^{[l]}}||^2_F
    因为w是一个n^{[l]} \times n^{[l-1]}矩阵所以:
    {||w^{[l]}||^2_F = \sum\limits_{i=1}^{n^{[l-1]}} \sum\limits_{j=1}^{n^{[l]}} (w_{ij}^{[l]})^2}
    这被称为弗罗贝尼乌斯范数(Frobenius Norm),所以神经网络的中的正则化项被称为弗罗贝尼乌斯范数矩阵。
    加入正则化项后,反向传播时:
    dw^{[l]} = \frac{\partial \mathcal{L}}{\partial w^{[l]}} + \frac{\lambda}{m} w^{[l]}
    更新参数时:
    w^{[l]} := w^{[l]} - \alpha \frac{\partial \mathcal{L}}{\partial w^{[l]}} - \alpha \frac{\lambda}{m} w^{[l]}
    有了新定义的dw^{[l]},参数w^{[L]}在更新时会多减去一项\alpha \frac{\lambda}{m} w^{[l]},所以L2正则化项也被称为权值衰减(Weight Decay)
    参数λ用来调整式中两项的相对重要程度,较小λ偏向于最后使原本的成本函数最小化,较大的λ偏向于最后使权值w最小化。当λ较大时,权值w^{[L]}便会趋近于0,相当于消除深度神经网络中隐藏单元的部分作用。另一方面,在权值w^{[L]}变小之下,输入样本X随机的变化不会对神经网络模造成过大的影响,神经网络受局部噪音的影响的可能性变小。这就是正则化能够降低模型方差的原因。

    随机失活正则化

    随机失活(DropOut)正则化就是在一个神经网络中对每层的每个节点预先设置一个被消除的概率,之后在训练过程中随机决定将其中的某些节点给消除掉,得到一个被缩减的神经网络,以此来到达降低方差的目的。
    DropOut正则化较多地被使用在计算机视觉(Computer Vision)领域。
    假设网络中的每一层,每个节点得以保留和消除的概率都是 0.5,设置完节点概率,我们会消除一些节点,然后删除掉从该节点进出的连线,最后得到一个节点更少,规模更小的网络,然后用 backprop 方法进行训练。

    随机失活
    使用Python编程时可以用反向随机失活(Inverted DropOut)来实现DropOut正则化,对于一个神经网络第3层:
    其中的d3是一个随机生成,与第3层大小相同的的布尔矩阵,矩阵中的值为0或1。而keep.prob ≤ 1,它可以随着各层节点数的变化而变化,决定着失去的节点的个数。
    例如,将keep.prob设置为0.8时,矩阵d3中便会有20%的值会是0。而将矩阵a3和d3相乘后,便意味着这一层中有20%节点会被消除。需要再除以keep_prob的原因,是因为后一步求z4中将用到a3,而a3有20%的值被清零了,为了不影响下一层z4的最后的预期输出值,就需要这个步骤来修正损失的值,这一步就称为反向随机失活技术,它保证了a3的预期值不会因为节点的消除而收到影响,也保证了进行测试时采用的是DropOut前的神经网络。
    与之前的L2正则化一样,利用DropOut,可以简化神经网络的部分结构,从而达到预防过拟合的作用。另外,当输入众多节点时,每个节点都存在被删除的可能,这样可以减少神经网络对某一个节点的依赖性,也就是对某一特征的依赖,扩散输入节点的权重,收缩权重的平方范数。

    数据扩增法

    在无法获取额外的训练样本下,对已有的数据做一些简单的变换。例如对一张图片进行翻转、放大扭曲,以此引入更多的训练样本。


    数据扩增

    早停止

    早停止(Early Stopping)是分别将训练集和开发集进行梯度下降时成本变化曲线画在同一个坐标轴内,在箭头所指两者开始发生较大偏差时就及时进行纠正,停止训练。在中间箭头处,参数w将是一个不大不小的值,理想状态下就能避免过拟合的发生。然而这种方法一方面没有很好的降低成本函数,另一方面又想以此来避免过拟合,一个方法解决两个问题,但是哪个都不能很好解决。

    早停止

    标准化数据集

    训练神经网络,其中一个加速训练的方法就是归一化输入。
    归一化的两个步骤:

    • 零均值
    • 归一化方差
      对训练及测试集进行标准化的过程为:
      \bar{x} = \frac{1}{m} \sum_{i=1}^m x^{(i)}
      x^{(i)} := x^{(i)} - \bar{x}
      \sigma^2 = \frac{1}{m} \sum_{i=1}^m {x^{(i)}}^2
      x^{(i)}:= \frac{x^{(i)}}{\sigma^2}
      原始数据:
      原始数据
      零均值:
      零均值
      归一化方差后:
      归一化方差
      数据集未进行标准化时,成本函数的图像及梯度下降过程将是:
      未进行标准化
      代价函数就有点像狭长的碗一样,如果你在上图这样的代价函数上运行梯度下降法,你必须使用一个非常小的学习率。因为如果是在这个位置,梯度下降法可能需要多次迭代过程,直到最后找到最小值。
      数据标准化后:
      数据标准化后
      但如果函数是一个更圆的球形轮廓,那么不论从哪个位置开始,梯度下降法都能够更直接地找到最小值,你可以在梯度下降法中使用较大步长。
      所以如果输入特征处于不同范围内,可能有些特征值从 0 到 1,有些从 1 到 1000,那么归一化特征值就非常重要了。如果特征值处于相似范围内,那么归一化就不是很重要了。执行这类归一化并不会产生什么危害,我通常会做归一化处理。

    初始化权重

    在一个深层神经网络中,当w的值被初始化过大时,进入深层时呈指数型增长,造成梯度爆炸;过小时又会呈指数级衰减,造成梯度消失
    Python中将w进行随机初始化时,使用numpy库中的np.random.randn()方法,randn是从均值为0的单位标准正态分布(也称“高斯分布”)进行取样。随着对神经网络中的某一层输入的数据量n的增长,输出数据的分布中,方差也在增大。结果证明,可以除以输入数据量n的平方根来调整其数值范围,这样神经元输出的方差就归一化到1了,不会过大导致到指数级爆炸或过小而指数级衰减。也就是将权重初始化为:

    w = np.random.randn(layers_dims[l],layers_dims[l-1]) \* np.sqrt(1.0/layers_dims[l-1])
    

    这样保证了网络中所有神经元起始时有近似同样的输出分布。
    当激活函数为ReLU函数时,权重最好初始化为:

    w = np.random.randn(layers_dims[l],layers_dims[l-1]) \* np.sqrt(2.0/layers_dims[l-1])
    

    梯度检验

    梯度检验的实现原理,是根据导数的定义,对成本函数求导,有:
    J’(\theta) = \frac{\partial J(\theta)}{\partial \theta}= \lim_{\epsilon\rightarrow 0}\frac{J(\theta+\epsilon)-J(\theta-\epsilon)}{2\epsilon}
    则梯度检验公式:
    J’(\theta) = \frac{J(\theta+\epsilon)-J(\theta-\epsilon)}{2\epsilon}
    其中当ϵ越小时,结果越接近真实的导数也就是梯度值。可以使用这种方法,来判断反向传播进行梯度下降时,是否出现了错误。
    梯度检验的过程,是对成本函数的每个参数\theta_{[i]}加入一个很小的ϵ,求得一个梯度逼近值d\theta_{approx[i]}
    d\theta_{approx[i]} = \frac{J(\theta_{[1]},\theta_{[2]},…,\theta_{[i]}+\epsilon)-J(\theta_{[1]},\theta_{[2]},…,\theta_{[i]}-\epsilon)}{2\epsilon}
    以解析方式求得J′(θ)θ时的梯度值dθ,进而再求得它们之间的欧几里得距离:
    \frac{||d\theta_{approx[i]}-d\theta||_2}{||d \theta_{approx[i]}||_2+||dθ||_2}
    其中||x||_2表示向量x的2范数.
    当计算的距离结果与ϵ的值相近时,即可认为这个梯度值计算正确,否则就需要返回去检查代码中是否存在bug。
    需要注意的是:

    • 不要在训练模型时进行梯度检验.
    • 当成本函数中加入了正则项时,也需要带上正则项进行检验.
    • 不要在使用随机失活后使用梯度检验。

    三种初始化方式的python实现

    zeros初始化

    def initialize_parameters_zeros(layers_dims):
    
        parameters = {}
        L = len(layers_dims)           
    
        for l in range(1, L):
            parameters['W' + str(l)] = np.zeros((layers_dims[l],layers_dims[l-1]))
            parameters['b' + str(l)] = np.zeros((layers_dims[l],1))
        return parameters
    

    random初始化

    def initialize_parameters_random(layers_dims):
    
        np.random.seed(3)              
        parameters = {}
        L = len(layers_dims)           
    
        for l in range(1, L):
            parameters['W' + str(l)] = np.random.randn(layers_dims[l],layers_dims[l-1]) * 10
            parameters['b' + str(l)] = np.zeros((layers_dims[l],1))
        return parameters
    

    权重(Xavier)初始化

    def initialize_parameters_he(layers_dims):
    
        np.random.seed(3)
        parameters = {}
        L = len(layers_dims) - 1
    
        for l in range(1, L + 1):
            parameters['W' + str(l)] = np.random.randn(layers_dims[l],layers_dims[l-1]) * np.sqrt(1.0/(layers_dims[l-1]))
            parameters['b' + str(l)] = np.zeros((layers_dims[l],1))
        return parameters
    

    相关文章

      网友评论

          本文标题:神经网络优化1

          本文链接:https://www.haomeiwen.com/subject/xijrhqtx.html