美文网首页
机器学习入门之 — 神经网络权值初始化

机器学习入门之 — 神经网络权值初始化

作者: DayDayUpppppp | 来源:发表于2018-04-08 11:59 被阅读0次

    在初始化权值的时候,常用的一个函数是 np.random.randn() 函数。这个函数会产生一个均值是0,方差是1的的分布。

    import numpy as np
    import matplotlib.pyplot as plt
    
    w= np.random.randn(10000)   #产生1*1w的数组
    print(w.mean())
    print(w.var())
    
    plt.hist(w,bins=100)  #绘制数据分布的直方图
    plt.show()
    

    数据分布直方图,如下图所示:
    绝大多数的随机数都产生在0附近,从0开始到+4和-4的区间上面,数据量越来越小。(服从正态分布)


    产生的数据基本服从正态分布

    但是,这样的初始化方式,放入神经网络训练的时候,在比较深的网络里面,往往效果一般。

    举个例子,分析一下。

    Z = weight * X+bias

    我们来看一下Z的分布:
    z分布在范围是(-100 -- +100 )之间。绝大多数数据分布在[-50,+50]之间。

    z的分布

    但是,如果我们的激活函数是sigmod的话,那么就会遇到这样一个问题。也就是梯度消失的问题。

    sigmod函数

    sigmod函数的导数:


    image.png

    以sigmoid函数为例,当z的绝对值变大时,函数值越来越平滑,趋于饱和,这个时候函数的导数趋于0。

    例如,在z=2时,函数的导数约为1/10,而在z=10时,函数的导数已经变成约为1/22000,也就是说,激活函数的输入是10的时候比2的时候神经网络的学习速率要慢2200倍!

    为了神经网络保持一个很好的性能,我们希望z的值绝大多数分布在[-5,+5]之间。

    对于梯度消失,有很多种解决办法,比如:

    • batch normalization
    • 使用relu
    • 更改初始化的方式

    这里我们只分析更改初始化的方式。有一个很有意思的trick:
    一种简单的做法是修改w的分布,使得z服从均值为0、方差为1的标准正态分布。根据正太分布期望与方差的特性,将w除以sqrt(n = 输入的结点个数) 即可。

    这个可以简单的理解,在正常初始化weight之后,然后给它除以权值个数的平方根。
    weight= np.random.randn(inputnode_num)/np.sqrt(inputnode_num)
    我的理解是,这样做的方法是将输出重新归一化到均值是0,方差是1。

    调整weight的分布之后z的分布.png

    如果把它放到之前的图的坐标系上面,即[-100,100]上面。效果更加明显。返回z的值的分布更加集中。

    如果把它放到之前的图的坐标系上面.png
    import numpy as np
    import matplotlib.pyplot as plt
    
    def show_weight_distribution():
        w= np.random.randn(10000)
        print(w.mean())
        print(w.var())
        plt.hist(w,bins=100)
        plt.show()
    
    def train():
        train_num=1000
        z_output=[]
        for i in range(train_num):
            bias=0
            x=np.ones(1000)
            weight= np.random.randn(1000)
            z=np.sum(weight * x)+bias
            z_output.append(z)
        print (" mean : ",np.mean(z_output))     #均值是1
        print (" var  : ",np.var(z_output))      #方差是1000
        plt.hist(z_output,bins=100)
        plt.show()
    
    
    def update_train():
        train_num=1000
        z_output=[]
        for i in range(train_num):
            inputnode_num=50001
            bias=0
            x=np.ones(inputnode_num)
            weight= np.random.randn(inputnode_num)/np.sqrt(inputnode_num)   #修改的地方
            z=np.sum(weight * x)+bias
            z_output.append(z)
        print (" mean : ",np.mean(z_output))    #均值是0
        print (" var  : ",np.var(z_output))     #方差是1
        plt.hist(z_output,bins=100)
        plt.show()
    
    def update_train2():
        train_num=1000
        z_output=[]
        for i in range(train_num):
            inputnode_num=5000
            bias=0
            x=np.ones(inputnode_num)
            weight= np.random.randn(inputnode_num)/np.sqrt(inputnode_num)  #修改的地方
            z=np.sum(weight * x)+bias
            z_output.append(z)
        print (" mean : ",np.mean(z_output))  #均值是0
        print (" var  : ",np.var(z_output))   #方差是1
        plt.xlim([-100,100])
        plt.hist(z_output,bins=100)
        plt.show()
    
    if __name__ =="__main__":
        update_train2()
    

    关于梯度消失和梯度爆炸的问题:

    梯度消失的表现:

    对于下图所示的含有3个隐藏层的神经网络,梯度消失问题发生时,接近于输出层的hidden layer 3等的权值更新相对正常,但前面的hidden layer 1的权值更新会变得很慢,导致前面的层权值几乎不变,仍接近于初始化的权值。

    这就导致hidden layer 1相当于只是一个映射层,对所有的输入做了一个同一映射,这是此深层网络的学习就等价于只有后几层的浅层网络的学习了。

    为什么会产生这样的情况?
    以下图的反向传播为例(假设每一层只有一个神经元且对于每一层)
    如下公式所示:


    网络结构如图所示:


    可以推到出:


    而sigmoid的导数如下图所示:


    这样,梯度爆炸问题的出现原因就显而易见了
    sigmod的导数的最大值是1/4,如果w的权值小于1 的话,那么 | sigmod‘ * w | 会小于1。如果网络有很多层的话,那么这个导数会指数倍减小。也就是前几层的权值基本上不改变。因而导致梯度消失的情况出现。

    如果 | sigmod‘ * w | > 1 , (也就是w比较大的情况)(虽然这种情况很少出现)这样就是梯度爆炸的情况了。

    so,小结一下:
    其实梯度爆炸和梯度消失问题都是因为网络太深,网络权值更新不稳定造成的,本质上是因为梯度反向传播中的连乘效应。对于更普遍的梯度消失问题,可以考虑用ReLU激活函数取代sigmoid激活函数。

    • 由于网络太深,导致反向传播出现一个连乘的效应。梯度指数倍减小。
    • sigmod的导数两边都很小

    参考:

    1. 斯坦福 cs231n

    相关文章

      网友评论

          本文标题:机器学习入门之 — 神经网络权值初始化

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