美文网首页工作生活
《改善深层神经网络:超参数调试、正则化以及优化》笔记(1)

《改善深层神经网络:超参数调试、正则化以及优化》笔记(1)

作者: wipping的技术小栈 | 来源:发表于2019-06-29 20:45 被阅读12次

    一、前言

    本文主要分为 3 周分别了讲解其中视频讲到的难重点,第一周分为 2 个重要的部分:正则化和梯度检验。笔者将自己在学习过程中的不理解的地方记录下俩,希望能够帮助到各位读者,也希望文章有错漏的地方,还请各位读者海涵指正。

    二、第一周——深度学习的使用方法

    2.1 模型性能判断方法

    还记得在西瓜书的第一章中,周志华老师就讲了可以使用测试集的性能来评估模型的预测性能,同时也说了模型的误差可以被分解为 偏差方差噪声这 3 个元素。
    假设有训练集误差 E1 和 验证集E2,那么通常的判断方法有 2 个:

    • 高方差:E1低、E2高
    • 高偏差:E1高、E2更高
      PS:其实这里推荐大家去看吴恩达的机器学习公开课,那里对偏方差的讲解更好一点
      那么如何解决因为这 2 个结果带来的影响呢?
    • 偏差:查看训练集的性能,如果此时偏差高一般选择新的网络
    • 方差:查看验证集的性能,如果此时方差偏高可以通过增加训练数据或者正则化来降低

    2.2 正则化

    2.2.1 正则化基础

    上面说到正则化可以降低方差,那么这一节来讲讲正则化。
    我们一般会在损失函数中加入正则化项,一般的正则化项有 L1范数L2范数,其实分别对应的就是曼哈顿距离欧式距离,读者自行百度就明白这 2 个范数的计算方式和表达方式了
    假设我们使用欧式距离来作为正则化,那么损失函数可以就是:
    J(w,b)=\frac{1}m\sum_{i=1}^m{L(\hat{y},y)} + \frac{\lambda}{2m}||W||_2^2
    其中,\lambda是正则化参数,||w||_2^2是矩阵 W 的L2范数,作者这里说不加上 b 的原因是因为 b 的影响很小,可加可不加。

    在神经网络中的损失函数中通过包含了第 1 层的W^{[1]}到第 l 层的W^{[l]},参数b同理,因为损失函数是以矩阵为单位来计算的,那么如果正则化项使用矩阵来表示的话是如下所示:
    \frac{\lambda}{2m}\sum_{1}^{L}|W^{[l]}|^2
    其中|W^{[l]}|^2我们称为范数平方,被定义为矩阵中所有元素的平方和。
    而矩阵W^{[l]}的维度通常是(n^{l}, n^{l-1}),这里对 n 定义不明的同学请看前面的文章,我们使用弗罗贝尼乌斯范数来称呼矩阵的范数平方,并用下标F著名,关于弗罗贝尼乌斯范数可在参数小节中查阅

    如果我们使用的是L2范数正则化,那么我们可以推导出一个L2正则化后的梯度公式。
    首先我们在不加正则化项前提下,计算出的对W^{[l]}导数成为dW^{[l]},那么增加了正则化项的导数我们表示为dW^{[l]}_L,那么有如下式子:
    dW^{[l]}_L = dW^{[l]} + \frac{\lambda}{m}W^{[l]}\\ W^{[l]} = W^{[l]} - \alpha dW^{[l]}_L = W^{[l]} - \alpha (dW^{[l]} + \frac{\lambda}{m}W^{[l]})
    最后有可得到
    W^{[l]} = (1 - \frac{\alpha \lambda}{m})W^{[l]} - \alpha dW^{[l]}
    从上式可以明显看得出,使用了正则化之后的梯度公式比原来多了系数(1 - \frac{\alpha \lambda}{m}),该系数小于 1,也就是先让矩阵W缩小在去减去梯度,所以L2正则化也叫权重衰减

    这里我们知道了如何进行L2正则化,但是从代码上看是怎么进行的呢?因为我们推出的公式无法直接用于代码,下面举个例子说明
    比如我们在要计算第 3 层dW_3,那么第三层的正则化项是a = \frac{\lambda}{m}W_3,那么在计算dW_3时我们就需要在等式后面加上a

    我们一般在验证集调试超参\lambda

    2.2.1 正则化原理

    我们可以直观的理解,当\lambda设置得足够大的时候,可以将矩阵W 衰减到足够小,假设此时使用的是tanh激活函数,那么因为W矩阵很小,所以输出Z也很小。由tanh函数的性质可知,如果输入是数值在很小的时候,相当于线性函数。而此时 Z 很小,那么此时激活函数相当于线性函数,多个线性函数叠加还是线性函数,所以此时模型拟合接近线性函数。
    当模型在过拟合和线性函数之间存在一个适当的矩阵W使之拟合程度最为适合,所以这就可以正则化其效果的原理

    2.2.2 dropout随机失活

    dropout是一种类似正则化的手段,但它并不是通过增加正则项,而是通过其他的方法来使模型达到正则化的效果。
    为了说明其步骤,视频中以某个神经网络的第 3 层为例子。
    在每一轮的前向计算中,我们对每一层的生成一个随机矩阵,第 3 层我们假设随机生成了一个矩阵

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

    该矩阵是一个只有 0 和 1 的矩阵,其中矩阵元素为 1 的概率是0.8, 为 0 的概率是 0.2,为超参 keep-porb 控制,那么在这里 keep-porb 等于 0.8

    通过 d3 * a3,我们可以消去一些神经网络节点的输出,从而使得网络更加精简,从结论上看,假设为 1 的概率是0.8,那么相乘只有 a3也是原来的0.8倍,即 d3 * a3 可以看做是 0.8a3,为了消除对下一层的影响,我们需要继续使用 a3 /= 0.8(这里是0.8是keep-porb )
    下层是z4 = w4 * a3 + b4,为了不影响 z4的期望值,即不想影响该层次的梯度下降,所以需要还原a3的值,不要让第三层加入dropout后影响该层的梯度

    那么问题来了,刚刚说的是前向传播如何使用dropout,那么在后向传播dropout又该如何处理,其实是一样,我们只需要使用在前向传播中生成的随机二值矩阵来乘dA即可,然后再让dA/keep-prob

    笔者在做作业的过程中发现如果按照给的keep-prob训练出来的模型准确率只有70%多,但在调整keep-prob在0.5以下的时候真确率提高到了90%,这可谓让笔者体验到了调参的重要性

    要注意的是。在测试时不使用dropout。因为我们不期望输出的结果是随机,如果输出结果不是随机的,那么就不会影响预测。我们在前面 a3 /= 0.8是确保在测试时不执行dropout也能让激活函数的期望输出在不会发生变化,这样就不用在测试阶段添加额外的参数,这段话是视频中给出的,但比较晦涩

    这里笔者说一下自己的通俗理解:笔者参考中《理解dropout》的说法,理解的dropout是让模型在训练时摆脱各个神经元之间的依赖性,不让模型的某个神经元只在特定的特征下起作用,这样就可以让每一个神经元都能够在所有的特征下都能起作用。那么这样的话,我们如果训练时加入了dropout,则无法体现出dropout的结果,因为测试时输出的跟训练时输出的一样

    2.2.3 其他正则化方法

    2.2.3.1 数据扩增
    1、将图片水平翻转并加入训练集
    2、随意缩放和裁剪图像并加入训练集

    2.2.3.2 提前停止
    在验证集是画出损失函数的下降图形,横轴是迭代次数,纵轴是损失函数,对训练集和验证集都画出图像,通常验证集是先降后升,我们可以在降到最低点时停止训练,此时获得一组参数W。但提前停止有缺点就是无法进行正交化,正交化在后面会讲解到

    2.3 归一化输入

    我们先看看归一化输入的执行过程,我们知道执行过程后比较容易理解归一化的原理及作用

    2.3.1 步骤

    归一化输入的步骤有 2 个,设我们数据集如下


    原始数据集
    1. 零均值化
      第一步是对数据集进行零均值化,设有参数
      u = \frac{1}{m}\sum^{m}_{i=1}x^{i}
      接着我们对每一个训练数据进行减去该参数,即
      x'^{i} = x^{i} - u
      其实就相当于移动数据集,使他们的均值为 0 ,也就是如下图

      零均值化
    2. 归一化方差
      第二部就是归一化方差,设有参数
      \sigma = \frac{1}{m}\sum^{m}_{i=1}(x'^i)^2
      我们上面已经完成了零均值化,那么可以得到如下
      \sigma = \frac{1}{m}\sum^{m}_{i=1}(x^{i} - u)^2
      从上式可知,\sigma是方差,然后我们让每一个数据都除以\sigma,得到:
      x''^i = \frac{x'^i}{\sigma}
      需要注意,如果用x''^i来调整训练数据,那么用相同的 μ 和 σ^2来归一化测试集。当然归一化测试集使用的参数u\sigma都是从训练集得来的

    2.3.2 原理

    在不归一化数据的情况下,如果特征x_1的范围是在[0, 1],而特征x_2在[0, 1000]。那么训练出来的w_1w_2的数量级就会相差非常远,从而看起来是类似的一个图形,图中的b和w应该分别是w_2w_1

    未归一化
    这样的话,如果我们学习率确定得不对,对模型的训练完全没有好处,比如有可能引起振荡之类的情况。
    如果我们归一化数据集后可以得到如下的图像
    归一化

    无论在哪个点,只要我们设置的学习率不太奇怪,我们总能够找到最优点

    2.4 梯度

    这一节将的是梯度,梯度如果在训练不好的情况下虎发生梯度消失和梯度爆炸,如果不理解这 2 个概念可以参考链接《梯度消失和梯度爆炸》
    这里简单说下引起梯度爆炸的 2 个原因

    2.4.1 解决方法

    初始化权值过大容易引起梯度爆炸,过小会引起梯度消失。
    一般的,我们有公式如下:
    z = w_1x_1 + w_2x_2 + w_3x_3 + ... + w_nx_n
    当n越多时,也就是特征越多时,希望w越小
    可以这样初始化,第 i 层一般有 n^{i-1} 个输入,我们可以用这样来初始化 W矩阵

    WL = np.random.randn(shape) * \sqrt {\frac{1}{n^{i-1}}}

    不同的激活函数有不同的初始化方法,对于tanh就有其他初始化公式 ,比如Xavier初始化

    如果是ReLu,设置为 \sqrt {\frac{2}{n^{i-1}}} 效果更好。此时也叫HE初始化

    这2个初始化的作用十分明显,笔者目前上了尚未理解,后面理解了再来讲述

    我们这里的随机分布一般使用的是高斯分布

    当然了,

    2.4.2 梯度检验

    梯度检验是为了确保后向传播的计算是正确的,保证我们的参数能够完成优化 。
    在讲梯度检验之前,先看看导数的单边公差跟双边公差

    1. 单边公差,公式如下:
      f'(x) = \lim_{ \Delta x\to\ 0} \frac{f(x + \Delta x) - f(x)}{\Delta x}
    2. 双边公差,公式如下:
      f'(x) = \lim_{ \Delta x\to\ 0} \frac{f(x+\Delta x) - f(x-\Delta x)}{2\Delta x}

    一般双边公差比单边公差更加接近梯度,在梯度检验时我们使用双边公差

    进行梯度检验时,我们需要将矩阵W^{[1]}b^{[1]}....W^{[l]}b^{[l]} 转换为向量\theta(视频中没有说到 b矩阵也需要转换,但这里必须的理解是需要转换b矩阵的)

    那么损失函数将变成
    J(W, b) \Rightarrow J(\theta)
    在运算完J(W, b)后我们得到dW^{[1]}db^{[1]}...dW^{[l]}db^{[l]},同样地我们需要将这些都转换为与\theta同维度的向量d\theta,因为dWW的维度是一样的,b矩阵同理。

    展开可得:
    J(\theta) = J(\theta _1, \theta _2, \theta _3,...)

    视频中没有说明如何转换,也没说明J(\theta)的具体公式是什么。举个例子说明一下笔者的理解:即W _1b_1一起转换成\theta _1,其余同理

    那么对于每一个\theta _i,我们去计算其双边公差:
    d\theta_{approx}[i] = \frac{J(\theta _1,\theta _2,...\theta _i+\varepsilon,...) - J(\theta _1,\theta _2,...\theta _i,...)}{2\varepsilon}

    所以计算出来的d\theta_{approx}是一个向量。从前面我们已经转换出了d\theta,那么我们可以用L2范数的计算方式计算
    a = \frac{||d\theta _{approx} - d\theta||_2}{||d\theta _{approx}||_2+||d\theta||_2}
    加入a的值小于或等于10^{-7},那么说明梯度是正确的,加入大于这个数,那么我们应该注意梯度是否有问题,当然这里的10^{-7}笔者理解是一个经验数值,大家可以按照各自的情况进行判断

    使用梯度检验时有一些注意事项 :

    • 训练时不要使用梯度检验,它只用于调试
    • 如果算法的梯度检验失败,检查所有项 ,如果 2 个向量之间值相差太大,要查看哪个 i 值相差最大
    • 如果在梯度检验的过程中使用正则化,需要将正则项加入梯度检验中
    • 梯度检验和dropout不能同时使用,因为dropuout会使代价函数很难计算,此时我们可以先将Keep-prob设置为1
    • 在随机初始化进行梯度检验,再训练网络

    2.4.3 转换方法

    我们上面说过,需要将参数矩阵W和b转换为一个一维向量,但视频中没有提高如何转换吗,这里笔者从作业中看到转换的代码,如下

    def dictionary_to_vector(parameters):
        """
        Roll all our parameters dictionary into a single vector satisfying our specific required shape.
        """
        keys = []
        count = 0
        for key in ["W1", "b1", "W2", "b2", "W3", "b3"]:
            
            # flatten parameter
            new_vector = np.reshape(parameters[key], (-1,1))
            keys = keys + [key]*new_vector.shape[0]
            
            if count == 0:
                theta = new_vector
            else:
                theta = np.concatenate((theta, new_vector), axis=0)
            count = count + 1
    
        return theta, keys
    

    可以很明显地看到,只是简单地讲参数展开为一维数据然后拼接起来

    2.4.4 第一周作业注意事项

    1. 关于错误
      因为库不兼容的原因,这里的作业画图时会出现错误

    c of shape (1, 211) not acceptable as a color sequence for x with size 211, y with size 211

    解决方法参考链接《课后作业错误解决》

    1. 二值化矩阵
      在dropout阶段,我们需要生成二值矩阵,可以使用下面的语句来实现,这里的大概意思就是D1中的元素如果小于keep_prob的话为 0 ,否则为 1
    np.where(D1 < keep_prob, 0, 1) 
    
    1. relu导数
      在这一周的课后作业中,给的代码使用了下面的语句来作为relu的导数
    dZ2 = np.multiply(dA2, np.int64(A2 > 0))
    

    其中 np.int64(A2 > 0) 应该是生成一个A2同纬度的矩阵,但是这个矩阵是A2中大于 0 的书保持不变,其余的数为 0

    参考

    弗罗贝尼乌斯范数:https://blog.csdn.net/david_jett/article/details/77040087
    理解dropout:http://www.pianshen.com/article/4175293217/
    梯度消失和梯度爆炸:https://www.cnblogs.com/XDU-Lakers/p/10553239.html
    课后作业错误解决:https://www.cnblogs.com/buchiyudemao/p/9028704.html

    相关文章

      网友评论

        本文标题:《改善深层神经网络:超参数调试、正则化以及优化》笔记(1)

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