1. 梯度下降
梯度下降是常用的神经网络模型参数求解方法,根据每次参数更新使用样本数量的多少,可以分为以下三类:
- 批量梯度下降(batch gradient descent);
- 小批量梯度下降(mini-batch gradient descent);
- 随机梯度下降(stochastic gradient descent);
批量梯度下降计算全部训练集样本梯度的平均,然后更新梯度,伪码如下:
while True:
weights_grad = evaluate_gradient(loss_fun, data, weights)
weights += - step_size * weights_grad # perform parameter update
对于大规模数据库来说,比如ILSVRC,可以包含几百万个样本,若采用批量梯度下降的话,需要计算几百万次梯度计算,然后计算平均,才能更新一次参数,无论从时间效率还是内存占用来说都是不可取的,一种常用的做法将训练数据分批(batch)进行训练,称为小批量梯度下降。比如对于卷积神经网络来说,每次在训练集中选择包含256个样本的一批数据,然后使用这批数据计算梯度,完成参数更新,代码如下:
while True:
data_batch = sample_training_data(data, 256) # sample 256 examples
weights_grad = evaluate_gradient(loss_fun, data_batch, weights)
weights += - step_size * weights_grad # perform parameter update
通常来说,通过小批量样本计算出来的梯度是对使用整个训练集计算出来的梯度一个非常好的近似,使用小批量梯度下降,可以极大地提高算法收敛速度。
小批量梯度下降的极端是每批数据只包含一个样本,这时算法称为随机梯度下降,也称为在线梯度下降(on-line gradient descent)。其伪码为:
while True:
data_batch = sample_training_data(data, 1) # use a single example
weights_grad = evaluate_gradient(loss_fun, data_batch, weights)
weights += - step_size * weights_grad # perform parameter update
在神经网络训练过程中,很少使用随机梯度下降,因为小批量梯度下降可以利用矩阵和向量计算进行加速。通常我们讲随机梯度下降(SGD)时,也可以指代小批量梯度下降。
2. 随机梯度下降加速
如果把要优化的目标函数看成山谷的话,可以把要优化的参数看成滚下山的石头,参数随机化为一个随机数可以看做在山谷的某个位置以0速度开始往下滚。目标函数的梯度可以看做给石头施加的力,由力学定律知: F=m∗a ,所以梯度与石头下滚的加速度成正比。因而,梯度直接影响速度,速度的累加得到石头的位置,对这个物理过程进行建模,可以得到参数更新过程为:
# Momentum update
v = momentum * v - learning_rate * dx # integrate velocity
x += v # integrate position
代码中v指代速度,其计算过程中有一个超参数momentum,称为动量(momentum)。虽然名字为动量,其物理意义更接近于摩擦,其可以降低速度值,降低了系统的动能,防止石头在山谷的最底部不能停止情况的发生。动量的取值范围通常为[0.5, 0.9, 0.95, 0.99],一种常见的做法是在迭代开始时将其设为0.5,在一定的迭代次数(epoch)后,将其值更新为0.99。
在实践中,一般采用SGD+momentum的配置,相比普通的SGD方法,这种配置通常能极大地加快收敛速度。
3. 学习率的更新
在算法迭代过程中逐步降低学习率(step_size)通常可以加快算法的收敛速度。常用的用来更新学习率的方法有三种:
- 逐步降低(Step decay),即经过一定迭代次数后将学习率乘以一个小的衰减因子。典型的做法包括经过5次迭代(epoch)后学习率乘以0.5,或者20次迭代后乘以0.1。
- 指数衰减(Exponential decay),其数学表达式可以表示为: