深度学习入门基于python的的理论与实现(学习笔记).第六章 与学习相关的技巧(第二部分)
6.2权重初始值:
在神经网络的学习中,权重的初始值特别重要。实际上,设定什么样的权重初始值,经常关系到神经网络的学习能否成功
权值衰减(weight decay)可以抑制过拟合。如果想减小权重的值,一开始就将初始值设为较小的值才是正途。实际上,在这之前的权重初始值都是像0.01 * np.random.randn(10, 100)这样,使用标准差为0.01 的高斯分布。
然而,如果将权重初始值设为0的话,将无法正确进行学习,或者说,将权重初始值设成一样的值,也无法正确进行学习。
这是因为在误差反向传播法中,所有的权重值都会进行相同的更新。比如,在2 层神经网络中,假设第1 层和第2 层的权重为0。这样一来,正向传播时,因为输入层的权重为0,所以第2 层的神经元全部会被传递相同的值。第2 层的神经元中全部输入相同的值,这意味着反向传播时第2 层的权重全部都会进行相同的更新。因此,权重被更新为相同的值,并拥有了对称的值(重复的值)。这使得神经网络拥有许多不同的权重的意义丧失了。为了防止“权重均一化”(严格地讲,是为了瓦解权重的对称结构),必须随机生成初始值。
- 隐藏层的激活值的分布:
我们将激活函数的输出数据称为“激活值”,但是有的文献中会将在层之间流动的数据也成为“激活值”。
下面一个实验,向一个5层神经网络(激活函数使用sigmoid)传入随机生成的输入数据,用直方图绘制各层激活值的数据分布。
import numpy as np import matplotlib.pyplot as plt def sigmoid(x): return 1 / (1 + np.exp(-x)) x = np.random.randn(1000, 100) # 1000个数据 node_num = 100 # 各隐藏层的节点数 hidden_layer_size = 5 # 隐藏层有5层 activations = {} # 保存每层的临时输出 for i in range(hidden_layer_size): if i != 0: x = activations[i-1] # 注意这里采用标准差为1的高斯分布 w = np.random.randn(node_num, node_num) * 1 z = np.dot(x, w) a = sigmoid(z) # sigmoid函数 activations[i] = a # 绘制直方图 for i, a in activations.items(): plt.subplot(1, len(activations), i+1) plt.title(str(i+1) + "-layer") plt.hist(a.flatten(), 30, range=(0, 1)) plt.show()
使用标准差为1的高斯分布作为权重初始值时的各层激活值的分布:
从上图可知,各层的激活值呈偏向0和1的分布。
这里使用的sigmoid函数是S型函数,随着输出不断地靠近0(或者靠近1),它的导数的值逐渐接近0。因此,偏向0和1的数据分布会造成反向传播中梯度的值不断变小,最后消失。这个问题称为梯度消失(gradient vanishing)。层次加深的深度学习中,梯度消失的问题可能会更加严重。
使用标准差为0.01的高斯分布作为权重初始值时的各层激活值的分布:
这次呈集中在0.5附近的分布。因为不像刚才的例子那样偏向0和1,所以不会发生梯度消失的问题。但是,激活值的分布有所偏向,说明在表现力上会有很大问题。为什么这么说呢?因为如果有多个神经元都输出几乎相同的值,那它们就没有存在的意义了。比如,如果100个神经元都输出几乎相同的值,那么也可以由1个神经元来表达基本相同的事情。因此,激活值在分布上有所偏向会出现“表现力受限”的问题。
由上述实验可以看出,各层的激活值的分布都要求有适当的广度。如果传递的是有所偏向的数据,就会出现梯度消失或者“表现力受限”的问题,导致学习可能无法顺利进行。
- Xavier初始值:
Xavier的论文中,为了使各层的激活值呈现出具有相同广度的分布,推导了合适的权重尺度。推导出的结论是,如果前一层的节点数为n,则初始值使用标准差为1n\frac{1}{\sqrt{n}}n1的高斯分布。
注:当本层为卷积层时,n= 上层通道数 ×本层滤波器 W×H
import numpy as np import matplotlib.pyplot as plt def sigmoid(x): return 1 / (1 + np.exp(-x)) x = np.random.randn(1000, 100) # 1000个数据 node_num = 100 # 各隐藏层的节点数 hidden_layer_size = 5 # 隐藏层有5层 activations = {} # 保存每层的临时输出 for i in range(hidden_layer_size): if i != 0: x = activations[i-1] # 这里使用Xavier初始值 # w = np.random.randn(node_num, node_num) * 1 # w = np.random.randn(node_num, node_num) * 0.01 w = np.random.randn(node_num, node_num) / np.sqrt(node_num) z = np.dot(x, w) a = sigmoid(z) # sigmoid函数 activations[i] = a # 绘制直方图 for i, a in activations.items(): plt.subplot(1, len(activations), i+1) plt.title(str(i+1) + "-layer") plt.hist(a.flatten(), 30, range=(0, 1)) plt.show()
使用Xavier 初始值后,前一层的节点数越多,要设定为目标节点的初始值的权重尺度就越小。
使用Xavier初始值作为权重初始值时的各层激活值的分布:
如果用tanh函数代替sigmoid函数,这个稍微歪斜的问题就能得到改善。实际上,使用tanh函数后,会呈漂亮的吊钟型分布。tanh函数和sigmoid函数同是S型曲线函数,但tanh函数是关于原点(0, 0)对称的S型曲线,而sigmoid函数是关于(x, y)=(0, 0.5) 对称的S型曲线。众所周知,用作激活函数的函数最好具有关于原点对称的性质。
- Relu的权重初始值:
Xavier 初始值是以激活函数是线性函数为前提而推导出来的。因为sigmoid函数和tanh函数左右对称,且中央附近可以视作线性函数,所以适合使用Xavier 初始值。但当激活函数使用ReLU时,一般推荐使用ReLU专用的初始值,即“He初始值”
当前一层的节点数为n时,He初始值使用标准差为
的高斯分布。
直观上可以解释为,因为ReLU的负值区域的值为0,为了使它更有广度,所以需要2倍的系数。下面将激活函数改为Relu,并改变权重初始值的标准差,比较隐藏层激活值的分布:
import numpy as np import matplotlib.pyplot as plt def ReLU(x): return np.maximum(0, x) x = np.random.randn(1000, 100) # 1000个数据 node_num = 100 # 各隐藏层的节点数 hidden_layer_size = 5 # 隐藏层有5层 activations = {} # 保存每层的临时输出 for i in range(hidden_layer_size): if i != 0: x = activations[i-1] # 标准差为1的高斯分布 # w = np.random.randn(node_num, node_num) * 1 # 标准差为0.01的高斯分布 # w = np.random.randn(node_num, node_num) * 0.01 # Xavier初始值, 标准差为sqrt(1/n)的高斯分布 # w = np.random.randn(node_num, node_num) / np.sqrt(node_num) # He初始值, 标准差为sqrt(2/n)的高斯分布 w = np.random.randn(node_num, node_num) * np.sqrt(2/node_num) z = np.dot(x, w) a = ReLU(z) # sigmoid函数 activations[i] = a # 绘制直方图 for i, a in activations.items(): plt.subplot(1, len(activations), i+1) plt.title(str(i+1) + "-layer") plt.hist(a.flatten(), 30, range=(0, 1)) plt.show()
前两张图可以看到各层激活值非常小(标准差为1的几乎没有激活值)。神经网络上传递的值也非常小,说明逆向传播时的梯度也非常小。这会导致学习几乎没有进展。
然后是Xavier初始值的结果,这种情况下,随着层的加深,偏向一点点变大。实际上,层加深后,激活值的偏向变大,学习时会出现梯度消失的问题。
而He初始值,各层中分布广度相同。由于即便层加深,数据的广度也能保持不变,因此逆向传播时,也会传递合适的值。
总结一下,当激活函数使用ReLU时,权重初始值使用He初始值,当激活函数为sigmoid或tanh等s型函数时,初始值使用Xavier初始值。这是目前的最佳实践。
- 深度学习入门基于python的的理论与实现(学习笔记).第六章 与学习相关的技巧(第四部分)
- 深度学习笔记-第4章-《深度学习入门——基于Python的理论与实现》
- 深度学习笔记-第3章-《深度学习入门——基于Python的理论与实现》的代码解说
- 在线下载MNIST数据集(深度学习入门基于Python的理论与实现——源代码)
- 《深度学习入门:基于Python的理论与实现》学习与总结(二)
- 深度学习入门:基于python的理论与实现
- 机器学习、深度学习的区别(from 深度学习入门:基于 Python 的理论与实现)
- 深度学习入门基于python的理论与实现
- 分享《深度学习入门:基于Python的理论与实现》高清中文版PDF+源代码
- 《深度学习入门:基于Python的理论与实现》高清中文版PDF+源代码
- 深度学习笔记-第2章-《深度学习入门——基于Python的理论与实现》
- 深度学习入门书籍推荐:《深度学习入门:基于Python的理论与实现》高清中文版PDF+源代码)
- [图灵程序设计丛书].深度学习入门:基于Python的理论与实现
- 深度学习笔记-第3章-《深度学习入门——基于Python的理论与实现》
- 《深度学习入门:基于Python的理论与实现》高清中文版PDF+源代码
- 深度学习入门基于python的理论与实现 4章gradient_simplenet.py 完全解析
- 深度学习入门基于python的理论与实现 第四章two_layer_net.py完全解析
- 深度学习入门-基于Python的理论与实现-斋藤康毅(待续)
- 分享《深度学习入门:基于Python的理论与实现》+PDF+源码+斋藤康毅+陆宇杰
- 分享《深度学习入门:基于Python的理论与实现》高清中文版PDF+源代码