神经网络优化1

2018-12-10  本文已影响0人  疯了个魔

数据划分

数据集分类

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

数据集划分原则

小数据量(10000以内):

模型估计

分类问题
左图,对图中的数据采用一个简单的模型,例如线性拟合,并不能很好地对这些数据进行分类,分类后存在较大的偏差(Bias),称这个分类模型欠拟合(Underfitting)
右图,采用复杂的模型进行分类,例如深度神经网络模型,当模型复杂度过高时变容易出现过拟合(Overfitting),使得分类后存在较大的方差(Variance)
中间的图中,采用一个恰当的模型,才能对数据做出一个差不多的分类。
通常采用开发集来诊断模型中是否存在偏差或者时方差:
偏差和方差

预防过拟合

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将是一个不大不小的值,理想状态下就能避免过拟合的发生。然而这种方法一方面没有很好的降低成本函数,另一方面又想以此来避免过拟合,一个方法解决两个问题,但是哪个都不能很好解决。

早停止

标准化数据集

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

初始化权重

在一个深层神经网络中,当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
上一篇下一篇

猜你喜欢

热点阅读