深度学习入门(四):与学习相关的技巧

与学习相关的技巧

本章介绍的方法,可以高效地进行神经网络(深度学习)的学习,提高识别精度

参数的更新

神经网络的学习的目的是找到使损失函数的值尽可能小的参数。这是寻找最优参数的问题,解决这个问题的过程称为最优化
在前几章中。为了找到最优参数,我们将参数的梯度作为线索,使用参数的梯度,沿梯度方向更新参数,并重复这个步骤若干次,从而靠近最优参数。这个过程称为SGD 随机梯度下降法
代码

SGD

class SGD:
	def __init__(self, lr = 0.01):
		self.lr = lr
	
	def update(self, params, grads):
		for key in params.keys():
				params[key] -= self.lr * grads[key] 

变量名optimizer表示“进行最优化的人”的意思。

SGD的缺点

  • 如果函数的形状非均向,比如呈延伸状,搜索的路径就会非常低效。

SGD低效的根本原因是,梯度的方向并没有指向最小值的方向。

Momentum

Momentum是“动量”的意思。
v ← α v − η ∂ L ∂ W v \leftarrow \alpha v - \eta \frac{\partial L}{\partial W} vαvηWL
W ← W + v W \leftarrow W + v WW+v
W表示要更新的权重参数, ∂ L ∂ W \frac{\partial L}{\partial W} WL表示损失函数关于W的梯度, η \eta η表示学习率。这里新出现了一个变量v,对应物理上的速度。表示了物体在梯度方向上受力,在这个力的作用下,物体的速度增加这一物理法则。

class Momentum:
    def __init__(self, lr=0.01, momentum=0.9):
        self.lr = lr
        self.momentum = momentum
        self.v = None
        
    def update(self, params, grads):
        if self.v is None:
            self.v = {}
            for key, val in params.items():
                self.v[key] = np.zeros_like(val)
            
        for key in params.key():
            self.v[key] = self.momentum*self.v[key] - self.lr*grads[key]
            params[key] += self.v[key]

实例变量v会保存物体的速度。初始化时,v中什么都不保存,但当第一次调用update时,v会以字典型变量的形式保存与参数结构相同的数据。

AdaGrad

在神经网络的学习中,学习率(数学中记为 η \eta η)的值很重要。学习率过小,会导致学习花费过多的时间;反过来,学习率过大,则会导致学习发散而不能正确进行。
在学习率的有效技巧中,有一种被称为学习率衰减的方法,即随着学习的进行,使学习率逐渐减少。
AdaGrad进一步发展了这个想法,针对“一个一个”的参数,赋予其“定制”的值。
AdaGrad会为参数的每个元素适当的调整学习率。
h ← h + ∂ L ∂ W ⨀ ∂ L ∂ W h \leftarrow h + \frac{\partial{L}}{\partial{W}}\bigodot \frac{\partial{L}}{\partial{W}} hh+WLWL
W ← W − η 1 h ∂ L ∂ W W \leftarrow W - \eta\frac{1}{\sqrt{h}}\frac{\partial{L}}{\partial{W}} WWηh 1WL
和SGD一样,W表示要更新的权重参数, ∂ L ∂ W \frac{\partial{L}}{\partial{W}} WL表示损失函数关于W的梯度, η \eta η表示学习率。h保存了以前的所有梯度值得平方和。然后再更新参数时,通过乘以 1 h \frac{1}{\sqrt{h}} h 1,就可以调整学习的尺度。也就是说,可以按照参数的元素进行学习率的衰减,使变动大的参数进行学习率衰减,使变动大的参数的学习率逐渐减少。
缺点:
AdaGrad会记录过去所有梯度的平方和。因此,学习越深入,更新的幅度就越小。实际上,如果永无止境地学习,更新量就会变为0,完全不更新。
代码实现

class AdaGrad:
    def __init__(self, lr=0.01):
        self.lr = lr
        self.h = None
        
    def update(self, params, grads):
        if self.h is None:
            self.h = {}
            for key, val in params.items():
                self.h[key] = np.zeros_like(val)
            
        for key in params.keys():
            self.h[key] += grads[key] * grads[key]
            params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key] + 1e-7))
            

注意:最后一行加上了微小值1e-7。这是为了防止当self.h[key]中有0时,将0用作除数的情况。

Adam

如果将AdamGrad和momentum融合在一起会怎么样?这就是Adam方法的基本思路。
Adam会设置3个超参数。一个是学习率,另外两个一个是momentum系数 β 1 \beta_1 β1和momentum系数 β 2 \beta_2 β2。这两个值分别设定为0.9和0.999。这里并没有详细介绍,如果有机会会补充。

权重的初始值

可以将权重初始值设为0吗

为什么不能将权重初始值设为0?严格地说,为什么不能将权重初始值设成一样的值呢?这是因为在误差反向传播中,所有的权重值都会进行相同的更新。因此,权重被更新为相同的值,并拥有了对称的值(重复的值)。这使得神经网络拥有许多不同的权重的意义丧失了。为了防止“权重均一化”,必须随机生成初始值。
注意
各层的激活值的分布都要求有适当的广度。为什么呢?因为通过在各层间传递多样性的数据,神经网络可以进行高效的学习。反过来,如果传递的有所偏向的数据,就会出现梯度消失或者“表现力受限”的问题,导致学习可能无法顺利进行。
接着,我们尝试使用Xavier初始值,为了使各层的激活值呈现出具有相同广度的分布,结论是,如果前一层的节点数为n,则初始值使用标准差为 1 n \frac{1}{\sqrt{n}} n 1的分布。
tanh函数和sigmoid函数同是S型曲线函数,但tanh函数是关于原点(0,0)对称的S型曲线,而sigmoid函数是关于(x, y) = (0, 0.5)对称的S型曲线。众所周知,用作激活函数的函数最好具有关于原点对称的性质。

ReLU的权重初始值

Xavier初始值是以激活函数是线性函数为前提而推导出来的。因为sigmoid函数和tanh函数左右对称,且中央附近可以视作线性函数,所以适合使用Xavier初始值。但不适合ReLU。一般推荐使用ReLU专用的初始值,“He初始值”.当前一层的节点数为n时,He初始值使用标准差为 2 n \sqrt{\frac{2}{n}} n2 的高斯分布。当Xavier初始值是 1 n \sqrt{\frac{1}{n}} n1 可以解释为:因为ReLU的负值区域的值为0,为了使它更具有广度,所以需要2倍的系数。

总结当激活函数使用ReLU时,权重初始值使用He初始值,当激活函数为sigmoid或tanh等S型曲线函数时,初始值使用Xavier初始值。

Batch Normalization

Batch Normalization 的算法

优点:

  • 可以使学习快速进行(可以增大学习率)
  • 不那么依赖初始值(对于初始值不用那么神经质)
  • 抑制过拟合(降低Dropout等的必要性)
    Batch Norm的思路是调整各层的激活值分布使其拥有适当的广度。
    Batch Norm,顾名思义,以进行学习时的mini-batch为单位,按mini-batch进行正规化。具体而言,就是进行使数据分布的均值为0,方差为1的正规化。
    I n e w = I − I m e a n s t d ( I ) I_{new} = \frac{I - I_{mean}}{std(I)} Inew=std(I)IImean
    将这个处理插入到激活函数的前面(或者后面),可以减少数据分布的偏向。
    接着,Batch Norm层会对正规化后的数据进行缩放和平移的变换, y i ← γ x i ^ + β y_i \leftarrow\gamma\hat{x_{i}} + \beta yiγxi^+β
    这里, γ 和 β \gamma和\beta γβ是参数一开始 γ = 1 β = 0 \gamma=1\beta = 0 γ=1β=0,然后通过学习调整到合适的值。

正则化

过拟合指的是只能拟合训练数据,但不能很好地拟合不包括在训练数据中的其他数据的状态。

权值衰减

权值衰减是一直以来经常被使用的一种抑制过拟合的方法。该方法通过在学习的过程中对大的权重进行惩罚,来抑制过拟合。
L2范数是指向量各元素的平方和然后求平方根。
对于所有权重,权值衰减方法都会为损失函数加上 1 2 λ ω 2 \frac{1}{2}\lambda\omega^2 21λω2。因此,在求权重梯度的计算中,要为之前的误差反向传播法的结果加上正则化的导数 λ ω \lambda\omega λω

Dropout

Droput是一种在学习的过程中随机删除神经元的方法。在训练时,随机选出隐藏层的神经元,然后将其删除。

import numpy as np

class Dropout:
    def __init__(self, dropout_ratio=0.5):
        self.dropout_ratio = dropout_ratio
        self.mask = None

    def forward(self, x, train_flg=True):
        if train_flg:
            self.mask = np.random.rand(*x.shape) > self.dropout_ratio
            return x * self.mask
        else:
            return x * (1.0 - self.dropout_ratio)

    def backward(self, dout):
        return dout * self.mask

每次正向传播时,self.mask中都会以False的形式保存要删除的神经元。self.mask会随机生成和x形状相同的数组,并将值比dropout_ratio大的元素设为True.反向传播时按原样传递信号;正向传播时没有传递信号的神经元,反向传播会停在那里。
机器学习中经常使用集成学习。就是让多个模型单独进行学习,推理时再取多个模型的输出的平均值。这个集成学习与Dropout有密切的关系。可以理解为通过再学习过程中随机删除神经元,从而每一次都让不同的模型进行学习。

超参数的验证

验证数据

训练数据用于参数(权重和偏置)的学习,验证数据用于超参数的性能评估。

超参数的最优化

进行超参数的最优化时,逐渐减少超参数的“好值”的存在范围非常重要。
超参数的范围只要“大致地指定”就可以了。所谓“大致地确定”,是指像0.001到1000这样,以“10的阶乘”的尺度指定范围。
超参数的最优化的内容:

  • 设定超参数的范围
  • 从设定的超参数范围中随机采样
  • 使用步骤1中采样到的超参数的值进行学习,通过验证数据评估识别精度(但是要将epoch设置得很小)
  • 重复步骤1和步骤2(100次等),根据它们得识别精度的结果,缩小超参数的范围
    在缩小到一定程度时,从该范围中选出有个超参数的值,这就是进行超参数的最优化的一种方法。
    如果需要更精炼的方法,可以使用贝叶斯定理为中心的数学理论,能够更加严密、高效地进行最优化。

超参数最优化的实现

weight_decay = 10** np.random.uniform(-8,-4)
lr = 10 ** np.random.uniform(-6,-2)

像这样进行随机采样后,再使用那些值进行学习。之后,多次使用各种超参数的值重复进行学习,观察合乎逻辑的超参数在哪里。

说明

此为本人学习《深度学习入门》的学习笔记,详情请阅读原书.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

evil心安

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值