1)该文章整理自网上的大牛和机器学习专家无私奉献的资料,具体引用的资料请看参考文献。
2)本文仅供学术交流,非商用。因此每一部分具体的参考资料并无详细对应。若是某部分不当心侵犯了你们的利益,还望海涵,并联系博主删除。
3)博主才疏学浅,文中若有不当之处,请各位指出,共同进步,谢谢。
4)此属于初版本,如有错误,还需继续修正与增删。还望你们多多指点。你们都共享一点点,一块儿为祖国科研的推动添砖加瓦。html
早些时间写过一个博客——深度学习100问之深刻理解Vanishing/Exploding Gradient(梯度消失/爆炸),感兴趣的小伙伴能够看一下。前端
训练神经网络,尤为是深度神经网络所面临的一个问题就是 梯度消失或梯度爆炸,那么什么是 梯度消失或梯度爆炸?node
其实就是训练神经网络时,导数或坡度 有时会变得很是大,或者很是小,甚至于以指数方式变小,这加大了训练的难度。下面经过一个例子来详细的讲解:python
假设正在训练这样一个 极深 的神经网络,为了简化问题,假设神经网络每层只有两个隐藏单元,可是由于 极深,因此还有不少参数,如 , , 等等,直到 。为了简单起见,假设使用线性激活函数 ,同时忽略偏置 ,即假设 =0,这样的话,输出:程序员
web
若是你是数学帕金森患者或者想考验个人数学水平,那么就简单说一下推导过程:算法
根据前向传播中的公式 ,又由于 ,因此 , ,而因为使用的事线性激活函数 ,因此第一项 ,经过推理。。。得出 ,由于 ,第一项 ,故能够用 替换 ,因此 。依次类推,可得 。编程
吴恩达老师手稿以下:
假设每一个权重矩阵
,从技术上来说,最后一项有不一样维度,可能它就是余下的权重矩阵,好比这里就是(None,1),因此根据上面推导的公式,能够获得
。又由于
,是1.5倍的单位矩阵(注意:网络的输出是
而不是
),因此计算结果是
。
若是对于一个深度神经网络来讲,它的
值明显较大,那么
的值也会很是大。在数学上分析的话,实际上它就是一个指数函数,所以是呈指数级增加的。该函数的增加比率是
,其实就是
,至关于下图中
的状况,是爆炸式增加的趋势。所以对于一个深度神经网络,输出值将爆炸式增加。
相反的,若是权重是
,即
,这项也就变成了
,矩阵
,再次忽略
,所以每一个矩阵都小于1,至关于上图中
的状况。如今咱们假设
和
都是1,通过激活函数的输出将变成 (
,
),(
,
),(
,
)等等,直到最后一项变成
,也就是指数降低的状况,由于它是与网络层数数量
相关的函数,
越大,通过激活函数的输出越小,甚至接近于0。所以对于一个深度神经网络,输出值将爆炸式减小。
小结一下,直观理解上,分两种状况:网络
在深度神经网络中,激活函数与 呈指数级增加或呈指数递减,在这样一个深度神经网络中,若是梯度函数也与 相关的指数增加或递减,它们的值将会变得极大或极小,从而致使训练难度上升,尤为是梯度指数小于 时,梯度降低算法的步长会很是很是小,梯度降低算法将花费很长时间来学习。在很长一段时间内,它曾是训练深度神经网络的阻力,虽然有一个不能完全解决此问题的解决方案,可是仍是有一些方法能够提供帮助。app
针对深度神经网络产生梯度消失和梯度爆炸的问题,咱们想出了一个不完整的解决方案,虽然不能完全解决问题,却颇有用,即为神经网络更谨慎地选择随机初始化参数。除此以外,初始化还对模型的收敛速度和性能有着相当重要的影响,由于说白了,神经网络其实就是对权重参数 w
的不停迭代更新,以期达到较好的性能。
那么神经元初始化的方式有哪些?
目前最常使用的就是随机初始化权重,好比常数初始化、正态分布初始化、均匀分布初始化、截断正态分布初始化、正交矩阵初始化等等。然而这是有弊端的,一旦随机分布选择不当,就会致使网络优化陷入困境,因此不少时候是调参去解决这个问题,避免陷入局部最优,会出现损失函数不收敛等状况。
首先建立了一个10层的神经网络,非线性变换为 tanh
,每一层的参数都是随机正态分布。
W = tf.Variable(np.random.randn(node_in, node_out))
随着层数的增长,输出值迅速向0靠拢,在后几层中,几乎全部的输出值 x
都很接近0!根据反向传播算法的链式法则,梯度等于当前函数的梯度乘之后一层的梯度,这意味着输出值是计算梯度的一个乘法因子,输出值接近于0将直接致使梯度很小,使得参数难以被更新。若是把初始值调大一些:W = tf.Variable(np.random.randn(node_in, node_out))
。
几乎全部的值集中在-1或1附近,神经元saturated了!注意到tanh在-1和1附近的梯度都接近0,这一样致使了梯度过小,参数难以被更新。
论文地址:Understanding the difficulty of training deep feedforward neural networks
Xavier
初始化能够解决上面的问题!其初始化方式也并不复杂,保持输入和输出的方差一致,这样就避免了全部输出值都趋向于0。
W = tf.Variable(np.random.randn(node_in, node_out)) / np.sqrt(node_in)
不过在应用 RELU
激活函数时:
后面的趋势倒是愈来愈接近0。。。
论文地址:Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification
He
初始化的思想是:在 ReLU
网络中,假定每一层有一半的神经元被激活,另外一半为0,因此,要保持 variance
不变,只须要在 Xavier
的基础上再除以2。
W = tf.Variable(np.random.randn(node_in,node_out)) / np.sqrt(node_in/2)
看起来效果很是好,RELU
激活函数中效果不错。
x = tf.get_variable('x', shape, initializer=tf.constant_initializer(1))
x = tf.get_variable('x', shape, initializer=tf.random_normal_initializer( mean=0.0, stddev=1.0, seed=None, dtype=tf.float32)) y = tf.get_variable('y', shape, initializer=tf.truncated_normal_initializer( mean=0.0, stddev=1.0, seed=None, dtype=tf.float32))
x = tf.get_variable('x', shape, initializer=tf.random_uniform_initializer( minval=0, maxval=10, seed=None, dtype=tf.float32)) # 或 x = tf.get_variable('x', shape, initializer=tf.uniform_unit_scaling_initializer( factor=1.0, seed=None, dtype=tf.float32))
x = tf.get_variable('x', shape, initializer=tf.truncated_normal_initializer( mean=0.0, stddev=1.0, seed=None, dtype=tf.float32))
x = tf.get_variable('x', shape, initializer=tf.orthogonal_initializer( gain=1.0, seed=None, dtype=tf.float32))
在上面给出了具体的代码,还有:
tf.glorot_uniform_initializer() # 或 tf.glorot_normal_initializer()
RELU
激活函数初始化推荐使用 He 初始化,tanh
初始化推荐使用 Xavier 初始化。
不过我我的目前用的比较多的是截断正态分布初始化,其余也都有在用,可是提高不是太明显,须要尝试才能肯定针对不一样问题时是否是能有效的提高,也多是由于专业不是前端精密行业,仍是须要斟酌。