博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
神经网络初始化
阅读量:6349 次
发布时间:2019-06-22

本文共 7367 字,大约阅读时间需要 24 分钟。

本文通过对《Understanding the difficulty of training deep feedforward neural networks》文章翻译和解读,和知乎、CSDN几位博主的文章总结、分析深度网络初始化方法。

首先是《Understanding the difficulty of training deep feedforward neural networks》

敲黑板:这里有一个发文章技巧,行不行有待验证大家应该感觉到一般的深度学习文章上来就是实验,告诉读者这个实验结果好,然后由实验结果再反向给出一些无从验证的可能对可能不对的原因。而这篇文章虽然整体来看比较简单,但结构非常严谨:首先通过实验分析标准初始化方法的问题;然后根据两个目标——状态方差和梯度方差保持不变推导出参数的特点,给出Xavier初始化方法的具体形式;最后通过实验验证Xavier初始化的效果确实不错。

总结发文章方法:

  • 实验
  • 告诉读者实验结果好
  • 由结果反正无从验证的可能对与不对的原因,设立目标
  • 根据目标验证猜想效果不错

下面之说一下我认为的重点:

分析的前提:

    1. 网络在初始化处于线性条件下,即激活活函数的导数为1;
    1. 初始化的权值的mean 为0,且独立同分布的;
  • 3.输入特征 x 的 variance是相同的。经过一系列推导,得到了下面这样的结果:
    第一:
    第二:公式5有用哦:

第三:公式6有用;

推出这玩意来了以后呢, 下面是关键:

1.前向传播:用文中的话说:From a forward-propagation point of view, to keep information flowing we would like that:推出这玩意来了以后呢, 下面是关键:

1.前向传播:用文中的话说:From a forward-propagation point of view, to keep information flowing we would like that:

就是说,为了在前向传播过程中,可以让信息向前传播,做法就是让:激活单元的输出值的方差持不变。为什么要这样呢??有点小不理解。。

  1. 反向传播:在反向传播过程中,也是为了让梯度可以反向传播,让:对激活单元输入值的梯度 保持不变,即:
    最后得到的结论就是:

上面两个式子折衷一下,为:

所以呢,权值初始化时,服从这样的分布:
这个方法就叫做: normalized initialization.

在训练过程中,梯度问题:

这时,我们就不能单纯地用梯度的 variance 去分析了,因为已经不满足我们的假设条件了啊。

文章后面的一大堆基本没有什么重点的东西了吧,我觉得。写几个觉得有必要的总结吧:

  1. softsign激活函数与双曲正切函数相比,效果还 很不错的,

  2. normalized initialization 的方法很不错。

初始化比较

  • 把w初始化为0
  • 对w随机初始化
  • Xavier initialization
  • He initialization

1.把w初始化为0

我们在线性回归,logistics回归的时候,基本上都是把参数初始化为0,我们的模型也能够很好的工作。然后在神经网络中,把w初始化为0是不可以的。这是因为如果把w初始化0,那么每一层的神经元学到的东西都是一样的(输出是一样的),而且在bp的时候,每一层内的神经元也是相同的,因为他们的gradient相同。下面用一段代码来演示,当把w初始化为0:

def initialize_parameters_zeros(layers_dims):    """    Arguments:    layer_dims -- python array (list) containing the size of each layer.    Returns:    parameters -- python dictionary containing your parameters "W1", "b1", ..., "WL", "bL":                    W1 -- weight matrix of shape (layers_dims[1], layers_dims[0])                    b1 -- bias vector of shape (layers_dims[1], 1)                    ...                    WL -- weight matrix of shape (layers_dims[L], layers_dims[L-1])                    bL -- bias vector of shape (layers_dims[L], 1)    """    parameters = {}    np.random.seed(3)    L = len(layers_dims)  # number of layers in the network    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复制代码

我们可以看看cost function是如何变化的:

能够看到代价函数降到0.64(迭代1000次)后,再迭代已经不起什么作用了。

2.对w随机初始化

目前常用的就是随机初始化,即W随机初始化。随机初始化的代码如下:

def initialize_parameters_random(layers_dims):    """    Arguments:    layer_dims -- python array (list) containing the size of each layer.    Returns:    parameters -- python dictionary containing your parameters "W1", "b1", ..., "WL", "bL":                    W1 -- weight matrix of shape (layers_dims[1], layers_dims[0])                    b1 -- bias vector of shape (layers_dims[1], 1)                    ...                    WL -- weight matrix of shape (layers_dims[L], layers_dims[L-1])                    bL -- bias vector of shape (layers_dims[L], 1)    """    np.random.seed(3)  # This seed makes sure your "random" numbers will be the as ours    parameters = {}    L = len(layers_dims)  # integer representing the number of layers    for l in range(1, L):        parameters['W' + str(l)] = np.random.randn(layers_dims[l], layers_dims[l - 1])*0.01        parameters['b' + str(l)] = np.zeros((layers_dims[l], 1))    return parameters复制代码

乘0.01是因为要把W随机初始化到一个相对较小的值,因为如果X很大的话,W又相对较大,会导致Z非常大,这样如果激活函数是sigmoid,就会导致sigmoid的输出值1或者0,然后会导致一系列问题(比如cost function计算的时候,log里是0,这样会有点麻烦)。

随机初始化后,cost function随着迭代次数的变化示意图为:

能够看出,cost function的变化是比较正常的。但是随机初始化也有缺点,np.random.randn()其实是一个均值为0,方差为1的高斯分布中采样。当神经网络的层数增多时,会发现越往后面的层的激活函数(使用tanH)的输出值几乎都接近于0,如下图所示:

import numpy as npimport matplotlib.pyplot as pltdef initialize_parameters(layer_dims):    """    :param layer_dims: list,每一层单元的个数(维度)    :return:dictionary,存储参数w1,w2,...,wL,b1,...,bL    """    np.random.seed(3)    L = len(layer_dims)#the number of layers in the network    parameters = {}    for l in range(1,L):        parameters["W" + str(l)] = np.random.randn(layer_dims[l],layer_dims[l-1])*0.01        parameters["b" + str(l)] = np.zeros((layer_dims[l],1))    return parametersdef forward_propagation():    data = np.random.randn(1000, 100000)    # layer_sizes = [100 - 10 * i for i in range(0,5)]    layer_sizes = [1000,800,500,300,200,100,10]    num_layers = len(layer_sizes)    parameters = initialize_parameters(layer_sizes)    A = data    for l in range(1,num_layers):        A_pre = A        W = parameters["W" + str(l)]        b = parameters["b" + str(l)]        z = np.dot(W,A_pre) + b #计算z = wx + b        A = np.tanh(z)        #画图        plt.subplot(2,3,l)        plt.hist(A.flatten(),facecolor='g')        plt.xlim([-1,1])        plt.yticks([])    plt.show()复制代码

3.Xavier initialization Xavier initialization是 Glorot 等人为了解决随机初始化的问题提出来的另一种初始化方法,他们的思想倒也简单,就是尽可能的让输入和输出服从相同的分布,这样就能够避免后面层的激活函数的输出值趋向于0。他们的初始化方法为:

def initialize_parameters_he(layers_dims):    """    Arguments:    layer_dims -- python array (list) containing the size of each layer.    Returns:    parameters -- python dictionary containing your parameters "W1", "b1", ..., "WL", "bL":                    W1 -- weight matrix of shape (layers_dims[1], layers_dims[0])                    b1 -- bias vector of shape (layers_dims[1], 1)                    ...                    WL -- weight matrix of shape (layers_dims[L], layers_dims[L-1])                    bL -- bias vector of shape (layers_dims[L], 1)    """    np.random.seed(3)    parameters = {}    L = len(layers_dims)  # integer representing the number of layers    for l in range(1, L):        parameters['W' + str(l)] = np.random.randn(layers_dims[l], layers_dims[l - 1]) * np.sqrt(1 / layers_dims[l - 1])        parameters['b' + str(l)] = np.zeros((layers_dims[l], 1))    return parameters复制代码

来看下Xavier initialization后每层的激活函数输出值的分布:

能够看出,深层的激活函数输出值还是非常漂亮的服从标准高斯分布。虽然Xavier initialization能够很好的 tanH 激活函数,但是对于目前神经网络中最常用的ReLU激活函数,还是无能能力,请看下图:

4.He initialization

为了解决上面的问题,提出了一种针对ReLU的初始化方法,一般称作 He initialization。初始化方式为:

def initialize_parameters_he(layers_dims):    """    Arguments:    layer_dims -- python array (list) containing the size of each layer.    Returns:    parameters -- python dictionary containing your parameters "W1", "b1", ..., "WL", "bL":                    W1 -- weight matrix of shape (layers_dims[1], layers_dims[0])                    b1 -- bias vector of shape (layers_dims[1], 1)                    ...                    WL -- weight matrix of shape (layers_dims[L], layers_dims[L-1])                    bL -- bias vector of shape (layers_dims[L], 1)    """    np.random.seed(3)    parameters = {}    L = len(layers_dims)  # integer representing the number of layers    for l in range(1, L):        parameters['W' + str(l)] = np.random.randn(layers_dims[l], layers_dims[l - 1]) * np.sqrt(2 / layers_dims[l - 1])        parameters['b' + str(l)] = np.zeros((layers_dims[l], 1))    return parameters复制代码

来看看经过He initialization后,当隐藏层使用ReLU时,激活函数的输出值的分布情况:

全文(废话)总结:

1.激活函数:

tanh、softsign好于sigmoid用

初始化 2.rule:
用He initialization初始化

感谢

参考文献

  1. Xavier Glorot et al., Understanding the Difficult of Training Deep Feedforward Neural Networks
  2. Kaiming He et al., Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classfication
  3. Andrew ng coursera 《deep learning》课
  4. 夏飞 《聊一聊深度学习的weight initialization》
    5.
    6.

转载地址:http://qgtla.baihongyu.com/

你可能感兴趣的文章
谷歌Pixel 3吸引三星用户, 但未动摇iPhone地位
查看>>
VUE中使用vuex,cookie,全局变量(少代码示例)
查看>>
grep -w 的解析_学习笔记
查看>>
量化交易之启航
查看>>
TX Text Control文字处理教程(3)打印操作
查看>>
CENTOS 7 如何修改IP地址为静态!
查看>>
MyCat分片算法学习(纯转)
查看>>
IO Foundation 3 -文件解析器 FileParser
查看>>
linux学习经验之谈
查看>>
mysqld_multi实现多主一从复制
查看>>
中介模式
查看>>
JS中将变量转为字符串
查看>>
servlet笔记
查看>>
JVM(五)垃圾回收器的前世今生
查看>>
CentOS 7 下安装 Nginx
查看>>
Spring Boot 自动配置之@EnableAutoConfiguration
查看>>
web前端笔记
查看>>
import 路径
查看>>
使用optimizely做A/B测试
查看>>
finally知识讲解
查看>>