caffe总结(六)
- 0、梯度下降
- 1、Stochastic gradient descent(SGD)
- 2、AdaDelta
- 3、AdaGrad
- 4、RMSprop
- 5、Adam
- 6、NAG
- 7、好的优化方式推荐
到目前为止,caffe总共提供了六种优化方法:
① Stochastic Gradient Descent (type: “SGD”),
② AdaDelta (type: “AdaDelta”),
③ Adaptive Gradient (type: “AdaGrad”),
④ Adam (type: “Adam”),
⑤ Nesterov’s Accelerated Gradient (type: “Nesterov”)
⑥ RMSprop (type: “RMSProp”)
- 更换优化方法的目的:
- 减少网络收敛时间
- 除了学习率(learning rate)还有更多其他的参数
- 比SGD到达更高的分类准确率
0、梯度下降
Solver就是用来使loss最小化的优化方法。对于一个数据集D,需要优化的目标函数是整个数据集中所有数据loss的平均值。
其中,fW(x(i))计算的是数据x(i)上的loss, 先将每个单独的样本x的loss求出来,然后求和,最后求均值。 r(W)是正则项(weight_decay),为了减弱过拟合现象。
如果采用这种Loss 函数,迭代一次需要计算整个数据集,在数据集非常大的这情况下,这种方法的效率很低,这个也是我们熟知的梯度下降采用的方法。
在实际中,通过将整个数据集分成几批(batches), 每一批就是一个mini-batch,其数量(batch_size)为N<<|D|,此时的loss 函数为:
有了loss函数后,就可以迭代的求解loss和梯度来优化这个问题。在神经网络中,用forward pass来求解loss,用backward pass来求解梯度。
对于小批量梯度下降,批量越大,梯度下降越快、曲线越平缓;
衍生出的optimizer都是通过改变学习率衰减和梯度方向变化来优化。
对于学习率衰减,一种是通过固定规则的学习率衰减,比如随时间衰减的逆时衰减,指数衰减,自然指数衰减等。其他AdaGrad, RMSprop,AdaDelta等是通过不同的参数设置不同学习率,自适应调整学习率。
在caffe中,默认采用的Stochastic Gradient Descent(SGD)进行优化求解。后面几种方法也是基于梯度的优化方法(like SGD),因此本文只介绍几种。其它的方法,有兴趣的同学,可以去看文献原文。
1、Stochastic gradient descent(SGD)
随机梯度下降(Stochastic gradient descent) 是在梯度下降法(gradient descent)的基础上发展起来的,梯度下降法也叫最速下降法,具体原理在网易公开课《机器学习》中,吴恩达教授已经讲解得非常详细。SGD在通过负梯度和上一次的权重更新值Vt的线性组合来更新W,迭代公式如下:
其中, α是负梯度的学习率(base_lr),μ是上一次梯度值的权重(momentum),用来加权之前梯度方向对现在梯度下降方向的影响。这两个参数需要通过tuning来得到最好的结果,一般是根据经验设定的。如果你不知道如何设定这些参数,可以参考相关的论文。
在深度学习中使用SGD,比较好的初始化参数的策略是把学习率设为0.01左右(base_lr: 0.01),在训练的过程中,如果loss开始出现稳定水平时,对学习率乘以一个常数因子(gamma),这样的过程重复多次。
对于momentum,一般取值在0.5–0.99之间。通常设为0.9,momentum可以让使用SGD的深度学习方法更加稳定以及快速。
关于更多的momentum,请参看Hinton的《A Practical Guide to Training Restricted Boltzmann Machines》。
实例:
[cpp] view plain copy
base_lr: 0.01
lr_policy: "step"
gamma: 0.1
stepsize: 1000
max_iter: 3500
momentum: 0.9
- lr_policy设置为step,则学习率的变化规则为 base_lr * gamma ^ (floor(iter / stepsize))
.
即前1000次迭代,学习率为0.01; 第1001-2000次迭代,学习率为0.001; 第2001-3000次迭代,学习率为0.00001,第3001-3500次迭代,学习率为10-5
上面的设置只能作为一种指导,它们不能保证在任何情况下都能得到最佳的结果,有时候这种方法甚至不work。如果学习的时候出现diverge(比如,你一开始就发现非常大或者NaN或者inf的loss值或者输出),此时你需要降低base_lr的值(比如,0.001),然后重新训练,这样的过程重复几次直到你找到可以work的base_lr。
- SGD的方法:W = lr * dW
其中我们有三个变量:
- W:权值矩阵
- lr:学习率
- dW:权值W的梯度
其中,我们的学习率是固定的,假设它很足够小,loss在训练的过程中就会下降。
2、AdaDelta
- AdaDelta是一种”鲁棒的学习率方法“,是基于梯度的优化方法(like SGD)。
- 具体的介绍文献:M. Zeiler ADADELTA: AN ADAPTIVE LEARNING RATE METHOD. arXiv preprint, 2012.
示例:
[cpp] view plain copy
net: "examples/mnist/lenet_train_test.prototxt"
test_iter: 100
test_interval: 500
base_lr: 1.0
lr_policy: "fixed"
momentum: 0.95
weight_decay: 0.0005
display: 100
max_iter: 10000
snapshot: 5000
snapshot_prefix: "examples/mnist/lenet_adadelta"
solver_mode: GPU
type: "AdaDelta"
delta: 1e-6
从最后两行可看出,设置solver type为Adadelta时,需要设置delta的值。
3、AdaGrad
- 自适应梯度(adaptive gradient)是基于梯度的优化方法(like SGD)
- 具体的介绍文献:Duchi, E. Hazan, and Y. Singer. Adaptive Subgradient Methods for Online Learning and Stochastic Optimization. The Journal of Machine Learning Research, 2011.
示例:
[cpp] view plain copy
net: "examples/mnist/mnist_autoencoder.prototxt"
test_state: { stage: 'test-on-train' }
test_iter: 500
test_state: { stage: 'test-on-test' }
test_iter: 100
test_interval: 500
test_compute_loss: true
base_lr: 0.01
lr_policy: "fixed"
display: 100
max_iter: 65000
weight_decay: 0.0005
snapshot: 10000
snapshot_prefix: "examples/mnist/mnist_autoencoder_adagrad_train"
# solver mode: CPU or GPU
solver_mode: GPU
type: "AdaGrad"
Adagrad在网络中会自适应的改变学习率,在参数改变不明显的时候学习率变化更频繁,在参数改变明显的地方学习率变化减弱。
我们来看看Adagrad的更新公式:
cache = (dW ** 2)
W = lr * dW / (np.sqrt(cache) + eps)
注意到的第一个参数是cache,这个参数表示每一个参数梯度平方的总和,会在训练过程每一次小的mini-batch进行更新。当观察cache时,我们可以发现哪些参数更新的快,哪些更新的慢。
接着使用lr*dW除以cache的平方(这里加一个epsilon的原因是为了防止除数为0)。发现当cache变化的很快时,cache的值会变得很大,接着在下一个公式有效的对学习率进行降低,同样,当变化的很慢时,学习率会相应的变大。
Adagrad最大的优点在于人们不需要手动的调节学习率——在大多数设置中,初始设定为0.01,然后让其自动在网络中进行调节。
但是,同样其缺点也非常的明显。在每一个mini-batch,梯度按照平方的形式在分母累计。因为梯度会平方(永远是正数),这个累积会在训练过程一直增加。我们都知道,当一个很小的数目除以一个很大的数目的时候,结果就会趋近于零,会导致更新非常的小而实际上什么都学习不到。这也是为什么我们不经常在深度学习中使用Adagrad的原因。
4、RMSprop
- RMSprop是Tieleman在一次 Coursera课程演讲中提出来的,也是一种基于梯度的优化方法(like SGD)
- 具体的介绍文献:T. Tieleman, and G. Hinton. RMSProp: Divide the gradient by a running average of its recent magnitude. COURSERA: Neural Networks for Machine Learning.Technical report, 2012.
示例:
[cpp] view plain copy
net: "examples/mnist/lenet_train_test.prototxt"
test_iter: 100
test_interval: 500
base_lr: 1.0
lr_policy: "fixed"
momentum: 0.95
weight_decay: 0.0005
display: 100
max_iter: 10000
snapshot: 5000
snapshot_prefix: "examples/mnist/lenet_adadelta"
solver_mode: GPU
type: "RMSProp"
rms_decay: 0.98
最后两行,需要设置rms_decay值。
- RMSprop是对于Adagrad的一个改进,可以减少因为cache带来的学习率单调递减的问题。
主要的方法是采用了指数加权滑动平均(EWMA)。 - 更新公式:
cache = decay_rate * cache + (1 - decay_rate) * (dW ** 2)
W = lr * dW / (np.sqrt(cache) + eps)
跟Adagrad的更新方式相似,主要的区别在于cache的变化。decay_rate,经常被设置为0.9。这种滑动平均的方式可以让cache丢掉老的梯度平方,从而用新的来代替。
所以,在深度学习中,RMSprop比Adagrad更有效,并且收敛速度比SGD更快。是现在第二流行使用的优化方式,接下来介绍最佳的优化方式Adam。
5、Adam
- 是一种基于梯度的优化方法(like SGD)。
- 具体的介绍文献:D. Kingma, J. Ba. Adam: A Method for Stochastic Optimization. International Conference for Learning Representations, 2015.
- 更新公式:
m = beta1 * m + (1 - beta1) * dW
v = beta2 * v + (1 - beta2) * (dW ** 2)
x += -lr * m / (np.sqrt(v) + eps)
其中,m和v的值和SGD的动量很相似,m代表第一时刻的梯度,v代表第二时刻。
W的跟新方式与RMSprop是一样的,一般情况下,beta1为0.9,beta2为0.999。
6、NAG
- Nesterov 的加速梯度法(Nesterov’s accelerated gradient)作为凸优化中最理想的方法,其收敛速度非常快。
- 具体的介绍文献: I. Sutskever, J. Martens, G. Dahl, and G. Hinton. On the Importance of Initialization and Momentum in Deep Learning. Proceedings of the 30th International Conference on Machine Learning, 2013.
示例:
[cpp] view plain copy
net: "examples/mnist/mnist_autoencoder.prototxt"
test_state: { stage: 'test-on-train' }
test_iter: 500
test_state: { stage: 'test-on-test' }
test_iter: 100
test_interval: 500
test_compute_loss: true
base_lr: 0.01
lr_policy: "step"
gamma: 0.1
stepsize: 10000
display: 100
max_iter: 65000
weight_decay: 0.0005
snapshot: 10000
snapshot_prefix: "examples/mnist/mnist_autoencoder_nesterov_train"
momentum: 0.95
# solver mode: CPU or GPU
solver_mode: GPU
type: "Nesterov"
7、好的优化方式推荐
有这么多的优化方式,对于我自己的模型而言,哪一个更好呢?其实,现在还没有一个明确的答案,最好的方法是,选择几个不同的方法在自己的模型上 尝试 ,找到最适合的。
这里特别要提醒的是,千万不要忽略SGD的重要性,你会发现,在AlexNet,VGGNet,SqueezeNet,Inception,ResNet中,都采用的是SGD的方法,为什么呢?
虽然SGD会比较的慢,但是对于网络来说,参数才是最重要的,如果你不能将优化参数调到最佳的话,你永远得不到好的准确率。SGD的使用时间将近60年,人们对它会更熟悉。
- tips:如果你可以使用SGD在指定的数据上达到很高的准确率,我建议你就使用SGD尽管会比RMSprop和Adam时间长一些。
- 所以必须要记住的三种优化方式为:
- SGD
- RMSprop
- Adam
一个模型建立时,首先使用SGD,一般情况下,结果会比较好。接着,再使用另外两种,建议直接使用Adam,因为在大多数情况下Adam会比RMSprop表现的好。