2023.1.22
在深度学习的学习中,权重的初始值特别重要。这关系到神经网络的学习能否成功;
在以前误差反向传播法和神经网络学习的算法实现这两篇文章中,对权重的初始值的确定是这样的:
class TwoLayerNet: def __init__(self, input, hidden, output, weight__init__std=0.01): # 权重的初始化 假设一个权重 # 通过本函数可以返回一个或一组服从标准正态分布的随机样本值。平均数为0,方差为1 self.params = {} self.params['w1'] = weight__init__std * np.random.randn(input, hidden) self.params['b1'] = np.zeros(hidden) self.params['w2'] = weight__init__std * np.random.randn(hidden, output) self.params['b2'] = np.zeros(output)
神经网络学习算法的实现:https://blog.csdn.net/m0_72675651/article/details/128671496
误差反向传播法的算法实现:https://blog.csdn.net/m0_72675651/article/details/128729159
都是通过一个np.random.randn( )函数去返回一个或一组服从标准正态分布的随机样本值,平均数为0,方差为1。
先说结论:有一种抑制过拟合、提高泛化能力的技巧,称为“权值衰减”。顾名思义,就是一个让权重值减小的方法去抑制过拟合、提高泛化能力;
所以我们一开始就先设置一个较小的权重都是0.01*np.random.randn( )这样的。(标准差为0.01的高斯分布)
但是,我们不能将权重设置为0,否则神经网络将无法进行学习,因为在神经网络推理处理中,输入层正向传播的权重为0,而传递给下一层神经网络的值也是0,这也意味着在反向传播更新数值时权重更新的内容全是相同的值,就代表着神经网络模型无法学习;
接下来参照教材作一个实验:
观察权重初始值时如何影响隐藏层的激活函数输入值的分布的:假设神经网络有有5层,每层100个神经元,先研究 激活函数使用sigmoid(),
import numpy as np import matplotlib.pyplot as plt # sigmoid函数的实现 def sigmoid(x): return 1 / (1 + np.exp(-x)) x = np.random.randn(1000, 100) node_num = 100 hidden_layer_size = 5 activations = {} for i in range(hidden_layer_size): if i != 0: x = activations[i - 1] w = np.random.randn(node_num, node_num) * 1 z = np.dot(x, w) a = sigmoid(z) 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)) # 在matplotlib中,hist方法用于绘制直方图 # flatten是numpy.ndarray.flatten的一个函数,即返回一个折叠成一维的数组。但是该函数只能适用于numpy对象,即array或者mat,普通的list列表是不行的。 plt.show() # subplot(nrows, ncols, index, **kwargs) # subplot(pos, **kwargs) # subplot(**kwargs) # subplot(ax)
输出结果:
因为假设有1000个数据,其排列方式是1000×100的矩阵,权重是100×100的排列分布的矩阵,所以z是1000×100的矩阵,所以a.flatten()后是一个(1×100000)的一个向量;
w = np.random.randn(node_num, node_num) * 1
”1“代表标准差为1的高斯分布,实验的目的就是通过改变这个尺度,观察权重初始值对激活函数的变化;
a经过sigmoid函数处理后成为i一个(0,1)的值;
如上图所示,他的输出不断地靠近0,1;所以他的导数逐渐接近0,在反向传播中他的梯度会不断的减小,最后消失,这样的现象,称他为“梯度消失”;层次加深的深度学习中梯度消失的问题会更加严重。
接着将标准差设置为0.01继续:
w = np.random.randn(node_num, node_num) * 1
更换成
w = np.random.randn(node_num, node_num) * 0.01
再观察结果
这次a的输出值向0.5集中,并不会发生梯度消失的问题,但是,这样的激活值向一个方向偏向也是有问题的,因为如果全部神经元输出相同的值那么就没有价值了,就好比权重初始值设置为0,权重更新为相同的值,出现了权重结构对称的现象,也称为 表现力受损 的问题。
(激活函数值需要有一点的广度,神经网络2才能高效率的学习,如果是有所偏向的数值就会出现“表现力受损”、“梯度消失”的问题)
然后,现在学习Xavier值,先说结论:Xavier值是已经被作为标准使用。比如Caffe框架中,通过设定权重值时,赋予Xavier值参数,以此实现Xavier值得初始化;
简单得来说他得做法是:如果前一层得节点是n,那么初始值设置使用得标准差为 得分布;
该方法来自有Xavier得相关论文,它不仅考虑了前一层得输出节点得数量,也考虑了后一层得输出节点数量,但是,Caffe等框架得现实中进行了简化,只用前一层得输出节点数量即可。
# 因为这个实验代码每一层得节点数量都是100,所以可以简化实现过程
w = np.random.randn(node_num, node_num) / np.sqrt(node_num)
观察输出结果:
可以看出,越是后面得层,图像越是倾斜,也呈现了广度,sigmoid函数得表现力不受限制,能够高效得进型学习。
但是,会不会就觉得后面得图像有点抽象,所以学习了一个与sigmoid函数相同得tanh函数;
def tanh(x): return np.tanh(x)
使用tanh函数得输出结果:
;
tanh(x)得图像:关于原点对称;
tanh图像代码实现:
import numpy as np
import matplotlib.pyplot as plt
def tanh(x):
return np.tanh(x)
x = np.arange(-10, 10)
y = tanh(x)
plt.plot(x, y)
plt.hlines(0, -10, 10, colors='red', linestyles='--')
plt.title('tanh')
plt.show()
tanh函数和sigmoid函数一样都是S型曲线函数,而sigmoid函数是关于(0,0.5)对称得S型曲线。总所周知,用作激活函数得函数最好具有关于原点对称得性质。
( Xavier初始值是以激活函数是线性函数为前提而推出来的,因为sigmoid函数和tanh函数左右对称,且中央附近可以视为线性函数,所以适合Xavier初始值。)
当激活函数使用ReLU的权重初始值:
当即或函数用的ReLU函数时,一般推荐时用kaiming He 提出的“He初始值”。当前一个函数节点数为n时,He初始值使用的标准差为的高斯分布;分别通过标准差为0.01,Xavier值,He值观察参数分布;
拿代码运行看结果。
import numpy as np
import matplotlib.pyplot as plt
# sigmoid函数的实现
def sigmoid(x):
return 1 / (1 + np.exp(-x))
def tanh(x):
return np.tanh(x)
def ReLU(x):
return np.maximum(0, x)
x = np.random.randn(1000, 100)
node_num = 100
hidden_layer_size = 5
activations = {}
for i in range(hidden_layer_size):
if i != 0:
x = activations[i - 1]
w = np.random.randn(node_num, node_num) / np.sqrt(node_num) # Xavier值
# w = np.random.randn(node_num, node_num) * 0.01 # std=0.01
# w = (np.random.randn(node_num, node_num) * np.sqrt(2)) / np.sqrt(node_num)# He值
z = np.dot(x, w)
# a = tanh(z)
# a=sigmoid(z)
a = ReLU(z)
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)) # 在matplotlib中,hist方法用于绘制直方图
# flatten是numpy.ndarray.flatten的一个函数,即返回一个折叠成一维的数组。但是该函数只能适用于numpy对象,即array或者mat,普通的list列表是不行的。
plt.show()
# subplot(nrows, ncols, index, **kwargs)
# subplot(pos, **kwargs)
# subplot(**kwargs)
# subplot(ax)
当标准差为0.01时:
各层的激活值非常小,神经网络传递的时非常小的值,反向传播的值也很小,说明权重该参数不会怎么更新,学习效果差;
当标准差为Xavier值:
随着层的加深,偏向越来越大,会出现 梯度消失现象 ;
当标准差为He值时:
随着层数的加深,各层中分布的广度相同,因此其逆向传播的效果也很好;