参考连接:http://sebastianruder.com/optimizing-gradient-descent/html
若是熟悉英文的话,强烈推荐阅读原文,毕竟翻译过程当中由于我的理解有限,可能会有谬误,还望读者能不吝指出。另外,因为原文太长,分了两部分翻译,本篇主要是梯度降低优化算法的总结,下篇将会是随机梯度的并行和分布式,以及优化策略的总结。git
梯度降低是优化中最流行的算法之一,也是目前用于优化神经网络最经常使用到的方法。同时,每一个优秀的深度学习库都包含了优化梯度降低的多种算法的实现(好比, lasagne 、 caffe 和 keras 的文档)。然而,这些算法通常被封装成优化器,如黑盒通常,所以很可贵到它们实际能力和缺点的解释。github
本篇博客的目标是为读者提供不一样梯度降低优化算法的直观解释,但愿读者能够学以至用。咱们会先了解下梯度降低的不一样变种。而后会对训练过程的问题进行简单总结。接着,咱们会介绍最经常使用的优化算法,展现它们解决这些问题的动机,以及它们对应更新规则变化的缘由。咱们也就会简单回顾在并行和分布式的状况下,梯度降低优化的算法和架构。最后,咱们也会聊聊有助于优化梯度降低的其余策略。算法
梯度降低是最小化以模型参数 θ∈Rdθ∈Rd 构建的目标函数 J(θ)J(θ) 的一种方法,它经过按目标函数 ∇θJ(θ)∇θJ(θ) 在参数梯度的相反方向更新参数。学习率 ηη 决定了咱们到达(局部)最小所需的步数的大小。换成通俗的话说,咱们会沿着目标函数所构建的表面坡度的方向往下走,直到咱们到达一个谷底。若是你还不熟悉梯度降低,你能够参考这篇 优化神经网络的入门介绍 。网络
一共有三种不一样版本的梯度降低,它们的不一样之处字啊与咱们计算目标函数梯度时使用数据的多少。根据数据量的大小,咱们会在参数更新的准确度和更新花费的时间之间进行权衡。架构
最普通的梯度降低,即批量梯度降低,使用整个训练数据根据参数 θθ 计算目标函数的梯度:dom
θ=θ−η⋅∇θJ(θ)θ=θ−η⋅∇θJ(θ)分布式
由于咱们须要计算完整个数据集的梯度才能更新,批量梯度降低很是的耗时,并且面对没法彻底放入内容的数据集,处理起来也很棘手。批量梯度更新也没法让咱们 在线 ,即在运行时加入新的样本进行模型更新。ide
以代码的形式,批量梯度降低的形式以下:函数
for i in range(nb_epochs): params_grad = evaluate_gradient(loss_function, data, params) params = params - learning_rate * params_grad
对于预先设定好的训练迭代次数,咱们首先对于整个数据集根据参数矢量 params
计算损失函数的梯度矢量 weight_grad
。注意最新的深度学习库提供了自动微分的方法,能够根据参数高效计算梯度。若是你本身作梯度的微分,那么最好作一下梯度检查。(从 这篇文章 能够获取一些合理检查梯度的技巧。)
SGD 的代码片断仅仅在训练样本时添加了一个循环,根据每一个样本进行梯度估计。注意咱们会在每次更新训练时会对训练数据进行随机洗牌处理,这会在后面进行解释:
for i in range(nb_epochs): np.random.shuffle(data) for example in data: params_grad = evaluate_gradient(loss_function, example, params) params = params - learning_rate * params_grad
然而, 传统的 mini-batch 梯度降低,并没有法保证好的收敛,但却有一些须要强调的挑战:
接下来,咱们将会罗列一些深度学习社区普遍用于处理前面提到的挑战的算法。咱们将不会讨论那些没法实际处理高维数据集的算法,即二阶方法,如 牛顿法 。
2、关于solver.prototxt中相关参数的解释:
epoch:1个epoch就是将全部的训练图像所有经过网络训练一次
例如:假若有1280000张图片,batchsize=256,则1个epoch须要1280000/256=5000次iteration
它的max-iteration=450000,则共有450000/5000=90个epoch
而lr何时衰减与stepsize有关,减小多少与gamma有关,即:若stepsize=500, base_lr=0.01, gamma=0.1,则当迭代到第一个500次时,lr第一次衰减,衰减后的lr=lr*gamma=0.01*0.1=0.001,之后重复该过程,因此
stepsize是lr的衰减步长,gamma是lr的衰减系数。
在训练过程当中,每到必定的迭代次数都会测试,迭代次数是由test-interval决定的,如test_interval=1000,则训练集每迭代1000次测试一遍网络,而
test_size, test_iter, 和test图片的数量决定了怎样test, test-size决定了test时每次迭代输入图片的数量,test_iter就是test全部的图片的迭代次数,如:500张test图片,test_iter=100,则test_size=5, 而solver文档里只须要根据test图片总数量来设置test_iter,以及根据须要设置test_interval便可。
3、关于train.prototxt中相关系数解释:
name: "LeNet" layer { name: "mnist" type: "Data" //这里的type还有MemoryData(内存读取)HDF5Date,HDF5output,ImageDta等 top: "data" //bottom表明输入,top表明输出 top: "label" include { phase: TRAIN //该层参数只在训练阶段有效 } transform_param { scale: 0.00390625 //数据变换使用的数据缩放因子,用于数据预处理如剪均值,尺寸变换,随机剪,镜像等 } data_param { //数据层参数 source: "./examples/mnist/mnist_train_lmdb" #lmdb路径 batch_size: 64 //批量数目,一次读取64张图 backend: LMDB } } layer { //一个新的数据层,但只在测试阶段才有效 name: "mnist" type: "Data" top: "data" top: "label" include { phase: TEST //该层参数只在测试阶段有效 } transform_param { scale: 0.00390625 } data_param { source: "./examples/mnist/mnist_test_lmdb" batch_size: 100 backend: LMDB } } layer { //定义接下来的卷积层 name: "conv1" type: "Convolution" bottom: "data" //输入为data top: "conv1" //输出为conv1 param { lr_mult: 1 //weights的学习率和全局相同 } param { lr_mult: 2 //biases的学习率是全局的2倍 } convolution_param { //卷积计算参数 num_output: 20 //输出特征图数量为20,即有20个filters kernel_size: 5 //卷积核的尺寸为5*5,这里的卷积核即为filter stride: 1 //卷积核移动尺寸 weight_filler { //权值使用xavier填充器 type: "xavier" //一种初始化方法 } bias_filler { //bias使用常数填充器,默认为0 type: "constant" } } } layer { //定义接下来的池化层(又称下采样层) name: "pool1" type: "Pooling" bottom: "conv1" top: "pool1" pooling_param { pool: MAX //使用最大值下采样法 kernel_size: 2 //下采样窗口尺寸为2*2 stride: 2 //窗口移动尺寸为2 } } layer { name: "conv2" type: "Convolution" bottom: "pool1" top: "conv2" param { lr_mult: 1 } param { lr_mult: 2 } convolution_param { num_output: 50 kernel_size: 5 stride: 1 weight_filler { type: "xavier" } bias_filler { type: "constant" } } } layer { name: "pool2" type: "Pooling" bottom: "conv2" top: "pool2" pooling_param { pool: MAX kernel_size: 2 stride: 2 } } layer { //全链接层 name: "ip1" type: "InnerProduct" bottom: "pool2" top: "ip1" param { lr_mult: 1 } param { lr_mult: 2 } inner_product_param { num_output: 500 //该层输出元素个数为500个 weight_filler { type: "xavier" } bias_filler { type: "constant" } } } layer { //非线性层,使用RELU激活函数 name: "relu1" type: "ReLU" bottom: "ip1" top: "ip1" } layer { name: "ip2" type: "InnerProduct" bottom: "ip1" top: "ip2" param { lr_mult: 1 } param { lr_mult: 2 } inner_product_param { num_output: 10 weight_filler { type: "xavier" } bias_filler { type: "constant" } } } layer { //分类准确率层,只在TEST阶段有效,输出为accuracy,该层用于计算准确率 name: "accuracy" type: "Accuracy" bottom: "ip2" bottom: "label" top: "accuracy" include { phase: TEST } } layer { //损失层,采用softmax分类器计算loss值 name: "loss" type: "SoftmaxWithLoss" bottom: "ip2" bottom: "label" top: "loss" }
momentum:动量(又称动量衰减系数)
weight_decay:正则化惩罚项的系数
总结:
(1)SGD
优势:简单方便;
缺点:一、选择一个合适的学习率很难;二、对于稀疏数据,每一个都是同等的更新,但可能存在就是某些数据须要更多更新,某些只要fintune便可;三、容易陷入局部最小值;
(2)SGD with momentum
优势:更新时能够保留以前更新的方向,因此降低时具备惯性更快,且有必定概率逃出局部最小值;
(3)Nexterov 动量降低
优势:不像传统的momentum那样根据惯性来降低,而是根据参数的近似将来位置来更新,更加准确智能;
(4)Adagrad降低
优势:根据参数自适应地更新学习率,对于不断频繁更新的参数做较大更新,对于不多更新的参数做做较小更新,因此咱们不须要手动更新学习率,通常初始化为0.01;
缺点:因为分母上累积了梯度的平方,且每一个加上的值都是整数,随着不断训练累积和会不断增大,致使学习率不断减少,学习能力愈来愈弱;
(5)Adadelta降低
优势:它是对Adagrad的扩展,解决了Adagrad激进单调递减学习率的缺点,它的梯度递归的由前面全部梯度的平方的均值来替代,同时还不须要设置初始学习率了;
(6)RMSprop降低:也是用来解决Adagrad学习率降低过快的问题;
(7)ADAM降低:比其余降低策略更好,具体看上面;
比较:
Karpathy作了一个这几个方法在MNIST上性能的比较,其结论是:
adagrad相比于sgd和momentum更加稳定,即不须要怎么调参。而精调的sgd和momentum系列方法不管是收敛速度仍是precision都比adagrad要好一些。在精调参数下,通常Nesterov优于momentum优于sgd。而adagrad一方面不用怎么调参,另外一方面其性能稳定优于其余方法。