神经网络权重初始化问题
之前看Andrew大神的视频有介绍到神经网络权重需要随机初始化而不是全初始化为0的问题,其真正深层次的含义没有弄明白,所以结合一些资料(cs231n课程)希望能让自己之后再想到这个问题的时候能够快速地明白过来。 另外这篇文章其实是一篇译文,所以翻译不是很确定的地方也将原文中的英文语句复制在句后,如果有更合适的翻译也请留言告知一下,谢谢! 参考文献: CS231n Convolutional Neural Networks for Visual Recognition
权重初始化
我们已经知道了如何构建神经网络结构,也知道了怎么预处理数据。在我们真正开始训练网络之前,我们必须要知道如何去初始化神经网络的参数。
陷阱: 都初始化为0。
首先介绍一下我们不应该做的事情(即初始化为0)。需要注意的是我们并不知道在训练神经网络中每一个权重最后的值,但是如果进行了恰当的数据归一化后,我们可以有理由认为有一半的权重是正的,另一半是负的。令所有权重都初始化为0这个一个听起来还蛮合理的想法也许是一个我们假设中最好的一个假设了。但结果正确是一个错误(的想法),因为如果神经网络计算出来的输出值都一个样,那么反向传播算法计算出来的梯度值一样,并且参数更新值也一样((w=w-α*dw))。更一般地说,如果权重初始化为同一个值,网络就不可能不对称(即是对称的)。
为什么不能是对称的? 答案参考【知乎:为什么神经网络在考虑梯度下降的时候,网络参数的初始值不能设定为全0,而是要采用随机初始化思想?】 设想你在爬山,但身处直线形的山谷中,两边是对称的山峰。 由于对称性,你所在之处的梯度只能沿着山谷的方向,不会指向山峰;你走了一步之后,情况依然不变。 结果就是你只能收敛到山谷中的一个极大值,而走不到山峰上去。
初始化为小的随机数
既然不能都初始化为0,那么很自然的我们会想到将权重初始化为非常接近0的小数(正如我们上面所讨论的不能等于0)。将权重初始化为很小的数字是一个普遍的打破网络对称性的解决办法。这个想法是,神经元在一开始都是随机的、独一无二的,所以它们会计算出不同的更新,并将自己整合到整个网络的各个部分。一个权重矩阵的实现可能看起来像(W=0.01 * np.random.randn(D,H)),其中randn是从均值为0的单位标准高斯分布进行取样。通过这个公式(函数),每个神经元的权重向量初始化为一个从多维高斯分布取样的随机向量,所以神经元在输入空间中指向随机的方向(so the neurons point in random direction in the input space.应该是指输入空间对于随机方向有影响)。其实也可以从均匀分布中来随机选取小数,但是在实际操作中看起来似乎对最后的表现并没有太大的影响。
警告:并不是数字越小就会表现的越好。比如,如果一个神经网络层的权重非常小,那么在反向传播算法就会计算出很小的梯度(因为梯度gradient是与权重成正比的)。在网络不断的反向传播过程中将极大地减少“梯度信号”,并可能成为深层网络的一个需要注意的问题。
用1/sqrt(n)校准方差
上述建议的一个问题是,随机初始化神经元的输出的分布有一个随输入量增加而变化的方差。结果证明,我们可以通过将其权重向量按其输入的平方根(即输入的数量)进行缩放,从而将每个神经元的输出的方差标准化到1。也就是说推荐的启发式方法(heuristic)是将每个神经元的权重向量按下面的方法进行初始化:(w=np.random.randn(n)/sqrt(n)),其中(n)表示输入的数量。这保证了网络中所有的神经元最初的输出分布大致相同,并在经验上提高了收敛速度。
(w=np.random.randn(n)/sqrt(n))推导过程大致如下: 令权重(w)和输入(x)的内积表达式为:(s=sum_{i}^{n}w_ix_i),这就使得神经元在非线性之前得到了原始的激活。 计算(s)的方差:
[ begin{align} Var(s) &= Var(sum_{i}^{n}w_ix_i) \ &= sum_{i}^{n}Var(w_ix_i) \ &= sum_{i}^{n}{[E(w_i)]^2}Var(x_i) + E{[x_i]}^2Var(w_i) + Var(x_i)Var(w_i) \ &= sum_{i}^{n}Var(x_i)Var(w_i) \ &= (nVar(w))Var(x) end{align} ] 在最开始的两步我们使用了方差的性质。 在第三步我们我们假设输入值和权重均值为0,所以(E[x_i]=E[w_i]=0)。注意到这不是一般的情况:比如ReLU的单元会有一个正的均值。 在最后一步我们假设所有的(w_i,x_i)都是同分布的(即(w_1,w_2...)是同分布,(x_1,x_2,...)是同分布,但是(w)和(x)不是同分布)。 从这个推导中我们可以看到如果我们想让(s)和所有的输入都有相同的方差,那么需要保证在初始化的时候每个权重(w)的方差是(frac{1}{n})。并且因为(Var(aX)=a^2Var(X))((a)是一个标量,(X)是一个随机变量),这就意味着我们可以从单位高斯分布中取样,然后通过(a=sqrt{frac{1}{n}})进行缩放来使得权重的方差为(frac{1}{n})。也就是得到了最开始所介绍的初始化方法:(w = np.random.randn(n) / sqrt(n))
Glorot等人所写的文章Understanding the difficulty of training deep feedforward neural networks中有类似的分析。在这篇论文中,作者的结论是建议初始化的形式是(Var(w)=frac{2}{(n_{in}+n_{out})}),其中(n_in,n_out)分别是上一层和下一层神经元的数量。这是基于一个折中的选择和对反向传播梯度等价分析后具有积极作用的办法。(This is motivated by based on a compromise and an equivalent analysis of the backpropagated gradients)。关于这个话题的最新论文,由He等人所写的Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification推导了ReLU神经元的权重初始化问题,得出的结论是神经元的方差需要是(frac{2.0}{n}),即(w = np.random.randn(n) / sqrt(2.0/n)),这是目前在神经网络中使用相关神经网络的建议。
稀疏初始化(Sparse Initialazation)
另一种解决未校准方差问题的方法是把所有的权重矩阵都设为零,但是为了打破对称性,每个神经元都是随机连接地(从如上面所介绍的一个小的高斯分布中抽取权重)到它下面的一个固定数量的神经元。一个典型的神经元连接的数目可能是小到10个。
初始化偏差
将偏差初始化为零是可能的,也是很常见的,因为非对称性破坏是由权重的小随机数导致的。因为ReLU具有非线性特点,所以有些人喜欢使用将所有的偏差设定为小的常数值如0.01,因为这样可以确保所有的ReLU单元在最开始就激活触发(fire)并因此能够获得和传播一些梯度值。然而,这是否能够提供持续的改善还不太清楚(实际上一些结果表明这样做反而使得性能更加糟糕),所以更通常的做法是简单地将偏差初始化为0.
实际操作
通常的建议是使用ReLU单元以及 He等人 推荐的公式(w = np.random.randn(n) * sqrt(2.0/n))
批量标准化
loffe和Ioffe最近开发的一项技术,称为“Batch Normalization”,在训练开始的时候,通过显式地迫使网络上的激活函数让整个网络上采用高斯分布来初始化神经网络,从而缓解了许多头痛的问题。(A recently developed technique by Ioffe and Szegedy called Batch Normalization alleviates a lot of headaches with properly initializing neural networks by explicitly forcing the activations throughout a network to take on a unit gaussian distribution at the beginning of the training.)。通过核心观察证明这是可能的,因为标准化是一个简单的可微分的操作(The core observation is that this is possible because normalization is a simple differentiable operation.)。在实际操作中,运用这项技术相当于在全连接层(或者卷积层,我们很快将会看到)后面嵌入BatchNorm层,并嵌在非线性(层)前。这里我们不会展开来讲解这项技术,因为它已经在上面那提供链接的论文中详细的介绍了,但是请注意,在神经网络中使用批量标准化已经成为一种非常常见的做法。在实践中,使用批量标准化的网络对糟糕的初始化更加健壮。还要需要提到的是,批量标准化可以解释为在网络的每一层进行预处理,但它以可微分的方式整合到网络中。完美!
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 霸气!开源项目名字就叫BAT,具有语法高亮的Cat类命令
- 【Python基础系列】常见的数据预处理方法(附代码)
- 机器学习基础:类别不平衡问题处理方法汇总及实际案例解析
- 机器学习与统计学:R方代表什么?和P值的关系是什么?
- 机器学习基础:缺失值的处理技巧(附Python代码)
- 七夕限定,致程序员的专属浪漫
- 使用 Docker Multi-stage 高效构建镜像
- vue(typescript) 使用webpack-bundle-analyzer
- 2020年学习Python-爬取百度图片
- 自动化运维 | Ansible lookup
- 【TBase开源版测评】Hello, TBase
- linux定位问题常用命令
- 聊聊claudb的zset command
- 腾讯云语音识别v1签名算法详解
- MySQL案例:关于JSON的一个bug