一、参数的更新

 

神经网络的学习的目的是找到使损失函数的值尽可能小的参数。这是寻找最优参数的问题,解决这个问题的过程称为最优化(optimization)。

SGD:

为了找到最优参数,我们将参数的梯度(导数)作为了线索。使用参数的梯度,沿梯度方向更新参数,并重复这个步骤多次,从而逐渐靠近最优参数,这个过程称为随机梯度下降法(stochastic gradient descent ),简称SGD。

神经网络中的w是计算出来的还是不计算的 神经网络sgd_最小值

←表示用右边的值更新左边的值。

SGD是一个简单的方法,不过比起胡乱地搜索参数空间,也算是“聪明”的方法。但是,根据不同的问题,也存在比SGD更加聪明的方法。

虽然SGD简单,并且容易实现,但是在解决某些问题时可能没有效率。这里,在指出SGD的缺点之际,我们来思考一下求下面这个函数的最小值的问题。

神经网络中的w是计算出来的还是不计算的 神经网络sgd_神经网络_02

 

 

神经网络中的w是计算出来的还是不计算的 神经网络sgd_最小值_03

 

神经网络中的w是计算出来的还是不计算的 神经网络sgd_神经网络中的w是计算出来的还是不计算的_04

 

 

这个梯度的特征是,y轴方向上大,x轴方向上小。换句话说,就是y轴方向的坡度大,而x轴方向的坡度小。

神经网络中的w是计算出来的还是不计算的 神经网络sgd_神经网络中的w是计算出来的还是不计算的_05

1 class SGD:
2     def __init__(self, lr=0.01)
3         self.lr=lr
4     def update(self, params, grads):
5         for key in params.keys():
6             params[key]-=self.lr*grad[key]

神经网络中的w是计算出来的还是不计算的 神经网络sgd_神经网络中的w是计算出来的还是不计算的_06

 这种形状的函数应用SGD。从(z, y))=(一7.0,2.0)处(初始值)开始搜索。基于SGD的最优化的更新路径:呈“之”字形朝最小值(0,0)移动,效率低。

右图的SGD呈“之”字形移动。这是一个相当低效的路径。也就是说,SGD的缺点是,如果函数的形状非均向( anisotropic),比如呈延伸状,搜索的路径就会非常低效。因此,我们需要比单纯朝梯度方向前进的SGD更聪明的方法。SGD低效的根本原因是,梯度的方向并没有指向最小值的方向。

 

Momentum:

Momentum是“动量”的意思,和物理有关。用数学式表示Momentum方法,如下所示。

 

神经网络中的w是计算出来的还是不计算的 神经网络sgd_权重_07

 

 

和前面的SGD一样,W表示要更新的权重参数, 表示损失函数关于W的梯度,η 表示学习率。这里新出现了一个变量v,对应物理上的速度。

1 class Momentum:
 2     def __init__(self, lr=0.01, momentum=0.9):
 3         self.lr=lr
 4         self.momentum=momentum
 5         self.v=None
 6     def update(self, params, grads):
 7         if self.v is None:
 8             self.v={}
 9             for key,val in params.items():
10                 self.v[key]=np.zeros_like(val)
11             for key in params.keys():
12                 self.v[key]=self.momentum*self.v[key]-self.lr*grads[key]
13                 params[key]+=self.v[key]

神经网络中的w是计算出来的还是不计算的 神经网络sgd_神经网络中的w是计算出来的还是不计算的_08

更新路径就像小球在碗中滚动一样。和SGD相比,我们发现“之”字形的“程度”减轻了。这是因为虽然α轴方向上受到的力非常小,但是一直在同一方向上受力,所以朝同一个方向会有一定的加速。反过来,虽然y轴方向上受到的力很大,但是因为交互地受到正方向和反方向的力,它们会互相抵消,所以y轴方向上的速度不稳定。因此,和SGD时的情形相比,可以更快地朝z轴方向靠近,减弱“之”字形的变动程度。

 

AdaGrad:

在关于学习率的有效技巧中,有一种被称为学习率衰减(learning ratedecay )的方法,即随着学习的进行,使学习率逐渐减小。实际上,一开始“多”学,然后逐渐“少”学的方法,在神经网络的学习中经常被使用。
逐渐减小学习率的想法,相当于将“全体”参数的学习率值一起降低。而AdaGrad进一步发展了这个想法,针对“一个一个”的参数,赋予其“定制”的值。

AdaGrad会为参数的每个元素适当地调整学习率,与此同时进行学习AdaGrad 的Ada来自英文单词Adaptive,即“适当的”的意思)。下面,让我们用数学式表示AdaGrad的更新方法。

神经网络中的w是计算出来的还是不计算的 神经网络sgd_深度学习_09

 

 现在来实现AdaGrad:

1 class AdaGrad:
 2     def __init__(self, lr=0.01):
 3         self.lr=lr
 4         self.h=None
 5     def update(self, params, grads):
 6         if self.h is None:
 7         self.h={}
 8         for key,val in params.items():
 9             self.h[key]=np.zeros_like(val)
10         for key,val in params.keys():
11             self.h[key]+=grads[key]*grads[key]
12             params[key]-=self.lr*grads[key]/(np.sqrt(self.h[key])+1e-7)

神经网络中的w是计算出来的还是不计算的 神经网络sgd_神经网络中的w是计算出来的还是不计算的_10

 

 由图结果可知,函数的取值高效地向着最小值移动。由于y轴方向上的梯度较大,因此刚开始变动较大,但是后面会根据这个较大的变动按比例进行调整,减小更新的步伐。因此,y轴方向上的更新程度被减弱,“之”字形的变动程度有所衰减。

 

Adam:

Momentum参照小球在碗中滚动的物理规则进行移动,AdaGrad为参数的每个元素适当地调整更新步伐。如果将这两个方法融合在一起会怎么样?

Adam是2015年提出的新方法。它的理论有些复杂,直观地讲,就是融合了Momentum和 AdaGrad的方法。通过组合前面两个方法的优点,有望实现参数空间的高效搜索。此外,进行超参数的“偏置校正”也是Adam的特征。

神经网络中的w是计算出来的还是不计算的 神经网络sgd_神经网络中的w是计算出来的还是不计算的_11

 

 

神经网络中的w是计算出来的还是不计算的 神经网络sgd_权重_12

 

神经网络中的w是计算出来的还是不计算的 神经网络sgd_权重_13

我们以手写数字识别为例,比较前面介绍的SGD、Momentum ,AdaGrad、Adam这4种方法,并确认不同的方法在学习进展上有多大程度的差异。

 

 

二、权重的初始值

在神经网络的学习中,权重的初始值特别重要。实际上,设定什么样的权重初始值,经常关系到神经网络的学习能否成功。如果想减小权重的值,一开始就将初始值设为较小的值才是正途。

我们可以设置初始值像0.01*np.random.randn(10,100)这样,使用由高斯分布生成的值乘以0.01后得到的值(标准差为0.01的高斯分布)。

如果我们把权重初始值全部设为0以减小权重的值,会怎么样呢?从结论来说,将权重初始值设为0不是一个好主意。事实上,将权重初始值设为0的话,将无法正确讲行学习。

 

观察隐藏层的激活值(激活函数的输出数据的分布),可以获得很多启发。这里,我们来做一个简单的实验,观察权重初始值是如何影响隐藏层的激活值的分布的。这里要做的实验是,向一个5层神经网络(激活函数使用sigmoid函数)传入随机生成的输入数据,用直方图绘制各层激活值的数据分布。

神经网络中的w是计算出来的还是不计算的 神经网络sgd_权重_14

神经网络中的w是计算出来的还是不计算的 神经网络sgd_最小值_15

1 import numpy as np
 2 import matplotlib.pyplot as plt
 3 
 4 
 5 def sigmoid(x):
 6     return 1 / (1 + np.exp(-x))
 7 
 8 
 9 def ReLU(x):
10     return np.maximum(0, x)
11 
12 
13 def tanh(x):
14     return np.tanh(x)
15     
16 input_data = np.random.randn(1000, 100)  # 1000个数据
17 node_num = 100  # 各隐藏层的节点(神经元)数
18 hidden_layer_size = 5  # 隐藏层有5层
19 activations = {}  # 激活值的结果保存在这里
20 
21 x = input_data
22 
23 for i in range(hidden_layer_size):
24     if i != 0:
25         x = activations[i-1]
26 
27     # 改变初始值进行实验!
28     w = np.random.randn(node_num, node_num) * 1
29     # w = np.random.randn(node_num, node_num) * 0.01
30     # w = np.random.randn(node_num, node_num) * np.sqrt(1.0 / node_num)
31     # w = np.random.randn(node_num, node_num) * np.sqrt(2.0 / node_num)
32 
33 
34     a = np.dot(x, w)

View Code

神经网络中的w是计算出来的还是不计算的 神经网络sgd_深度学习_16

 

 使用标准差为1 的高斯分布作为权重初始值时的各层激活值的分布

 

各层的激活值呈偏向0和1的分布。这里使用的sigmoid函数是S型函数,随着输出不断地靠近0(或者靠近1),它的导数的值逐渐接近0。因此,偏向О和1的数据分布会造成反向传播中梯度的值不断变小,最后消失。这个问题称为梯度消失(gradient vanishing)。层次加深的深度学习中,梯度消失的问题可能会更加严重。