Caffe学习:Solver

来源:互联网 发布:tk免费域名注册 编辑:程序博客网 时间:2024/05/22 06:27

目录:

  • 原文
  • Solver
  • Methods
    • SGD
    • AdaGrad
    • NAG
  • Scaffolding
  • Updating Parameters
  • Snapshotting and Resuming

原文


Solver

Solver通过协调Net的前向loss计算和反向梯度计算(forward inference and backward gradients),对参数进行更新,以减小loss。Caffe模型的学习被分为两个部分:由Solver进行优化、更新参数,由Net计算出loss和gradient。

Caffe提供了3种Solver方法:Stochastic Gradient Descent(SGD,随机梯度下降),Adaptive Gradient(ADAGRAD,自适应梯度下降)和Nesterov’s Accelerated Gradient(NESTEROV,Nesterov提出的加速梯度下降)。

Solve步骤:

  1. 设计优化对象、训练网络(用于学习)和测试网络(用于评估)
  2. 通过forward和backward迭代以优化和更新参数
  3. 周期性地评估测试网络
  4. 在优化过程中显示(snapshot)模型和solver的状态

每一次迭代中:

  1. 调用Net的forward pass计算出output 和loss
  2. 调用Net的backward pass计算出gradients
  3. 根据Solver方法,利用gradients更新参数
  4. 根据learning rate,history和method更新solver的状态

Caffe solvers有CPU / GPU两种模式。


Methods

Solver Methods用于最小化loss值。给定一个数据集D,优化的目标是D中所有数据的平均loss,即使平均loss取最小值。

计算公式

其中 fW(X(i))是data中X(i)项的loss,r(W)是正则项,权重为λ。当数据量很大时(直接采用普通的梯度下降方法计算量很大),在每一次迭代中,我们采用数据集的一个子集(mini-batch)来近似代替,其数据量远小于整个数据集:

计算公式

在forward pass中计算fW(即loss),在backward pass中计算∇fW(即gradient)。

SGD

  • 类型:SGD

随机梯度下降(Stochastic gradient)利用(负的)gradient ∇L(W) 上一次权重的更新值Vt的线性组合来更新权重W学习速率(learning rate)α是(负的)gradient的权重。动量(momentum )μ是上一次更新值得权重。

有如下公式,根据上一次计算的更新值Vt和当前权重来计算本次的更新值Vt+1和权重 Wt+1:

计算公式

学习的参数(α和μ)需要一定的调整才能达到最好的效果。如果你不是很清楚该怎样做的话,请看下面的“经验”,更多的细节请参考Leon Bottou的Stochastic Gradient Descent Tricks

设置学习速率( learning rate α)和动量(momentum μ)的经验:

一个比较好的建议是,将学习速率( learning rate α)初始化为α≈0.01,然后在训练(training)中当loss达到稳定时,将α除以一个常数(例如,10)。对于,动量(momentum μ)一般设置为μ=0.9,μ使weight的更新更为平缓,是学习过程更为稳定、快速。

这是Krizhevsky提出的技巧【文献1】,Caffe实现了SolverParameter让这个技巧易于实现。详见:./examples/imagenet/alexnet_solver.prototxt

要使用上述技巧,可以将下面的代码添加到自定义的solver prototxt文件中:

base_lr: 0.01     # 开始学习速率为:α = 0.01lr_policy: "step" # 学习策略: 每stepsize次迭代之后,将α乘以gammagamma: 0.1        # 学习速率变化因子stepsize: 100000  # 每100K次迭代,下降学习速率max_iter: 350000  # 训练的最大迭代次数momentum: 0.9     # momentum为:μ = 0.01

上面的例子中,我们将动量μ设为常数0.9。刚开始前100K次迭代时学习速率α设置为0.01,然后第100K~200K次迭代时将学习速率α乘以gamma (γ),即为α′=αγ=(0.01)*(0.1)=0.001,之后第200K~300K次迭代时学习速率为α′′=0.00001,最后第301K~350K次迭代时学习速率为α′′=0.000001.

上述例子中,当训练次数达到一定量后,计算量会扩大到1/(1-μ)倍,所以如果增加μ的话,最好是减少α值(反之相反)。举个例子, 设μ=0.9,则计算量会扩大1/(1-μ)=10倍。如果将μ扩大为0.99,那么计算量会扩大100倍,所以α应该除以10.

上述技巧也只是经验之谈,不保证绝对有用,甚至有副作用。如果训练过程中出现了发散现象(例如,loss或者output值非常大),试着减小开始学习速率(base_lr)再训练,重复这个过程,直到找到一个比较合适的学习速率。

【文献1】:A. Krizhevsky, I. Sutskever, and G. Hinton. ImageNet Classification with Deep Convolutional Neural Networks. Advances in Neural Information Processing Systems, 2012.

AdaGrad

  • 类型:ADAGRAD

自适应梯度下降方法( Adaptive gradient)【文献1】跟随机梯度下降(Stochastic gradient)一样是基于梯度的优化方法。给定之前更新的信息:之前的更新信息,按照下面的公式更新参数:
计算公式

实际操作中,对于权重权重,自适应梯度(AdaGrad )只需要存储空间额外空间存储历史信息,而不是存储空间的存储空间来存储所有的历史纪录。

【文献1】: Duchi, E. Hazan, and Y. Singer. Adaptive Subgradient Methods for Online Learning and Stochastic Optimization. The Journal of Machine Learning Research, 2011.

NAG

  • 类型:NESTEROV

Nesterov提出的加速梯度下降(Nesterov’s accelerated gradient)是Nesterov提出的凸优化的最佳方法【文献1】,其收敛速度可以达到收敛速度,而不是收敛速度。尽管在Caffe中并不是一定能够满足收敛于收敛速度的条件(例如,由于非平滑non-smoothness和non-convexity非凸)实际中NAG对于某类特定结构的深度学习模型是一个非常有效的方法【文献2】。

权重weight更新参数与随机梯度下降(Stochastic gradient)非常相似:

计算公式

与SGD的不同之处在于梯度∇L(W)项中取值不同:在NAG中,对于权重W梯度的计算要机上动量=>∇L(Wt+μVt);在SGD中,只是简单的计算当前权重的动量=>∇L(Wt)。

【文献1】:Y. Nesterov. A Method of Solving a Convex Programming Problem with Convergence Rate O(1/k√). Soviet Mathematics Doklady, 1983.
【文献2】: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.


Scaffolding

Solver::Presolve()方法:准备用于优化的方法及初始化模型。

> caffe train -solver examples/mnist/lenet_solver.prototxtI0902 13:35:56.474978 16020 caffe.cpp:90] Starting OptimizationI0902 13:35:56.475190 16020 solver.cpp:32] Initializing solver from parameters:test_iter: 100test_interval: 500base_lr: 0.01display: 100max_iter: 10000lr_policy: "inv"gamma: 0.0001power: 0.75momentum: 0.9weight_decay: 0.0005snapshot: 5000snapshot_prefix: "examples/mnist/lenet"solver_mode: GPUnet: "examples/mnist/lenet_train_test.prototxt"

Net initialization(网络初始化)

I0902 13:35:56.655681 16020 solver.cpp:72] Creating training net from net file: examples/mnist/lenet_train_test.prototxt[...]I0902 13:35:56.656740 16020 net.cpp:56] Memory required for data: 0I0902 13:35:56.656791 16020 net.cpp:67] Creating Layer mnistI0902 13:35:56.656811 16020 net.cpp:356] mnist -> dataI0902 13:35:56.656846 16020 net.cpp:356] mnist -> labelI0902 13:35:56.656874 16020 net.cpp:96] Setting up mnistI0902 13:35:56.694052 16020 data_layer.cpp:135] Opening lmdb examples/mnist/mnist_train_lmdbI0902 13:35:56.701062 16020 data_layer.cpp:195] output data size: 64,1,28,28I0902 13:35:56.701146 16020 data_layer.cpp:236] Initializing prefetchI0902 13:35:56.701196 16020 data_layer.cpp:238] Prefetch initialized.I0902 13:35:56.701212 16020 net.cpp:103] Top shape: 64 1 28 28 (50176)I0902 13:35:56.701230 16020 net.cpp:103] Top shape: 64 1 1 1 (64)[...]I0902 13:35:56.703737 16020 net.cpp:67] Creating Layer ip1I0902 13:35:56.703753 16020 net.cpp:394] ip1 <- pool2I0902 13:35:56.703778 16020 net.cpp:356] ip1 -> ip1I0902 13:35:56.703797 16020 net.cpp:96] Setting up ip1I0902 13:35:56.728127 16020 net.cpp:103] Top shape: 64 500 1 1 (32000)I0902 13:35:56.728142 16020 net.cpp:113] Memory required for data: 5039360I0902 13:35:56.728175 16020 net.cpp:67] Creating Layer relu1I0902 13:35:56.728194 16020 net.cpp:394] relu1 <- ip1I0902 13:35:56.728219 16020 net.cpp:345] relu1 -> ip1 (in-place)I0902 13:35:56.728240 16020 net.cpp:96] Setting up relu1I0902 13:35:56.728256 16020 net.cpp:103] Top shape: 64 500 1 1 (32000)I0902 13:35:56.728270 16020 net.cpp:113] Memory required for data: 5167360I0902 13:35:56.728287 16020 net.cpp:67] Creating Layer ip2I0902 13:35:56.728304 16020 net.cpp:394] ip2 <- ip1I0902 13:35:56.728333 16020 net.cpp:356] ip2 -> ip2I0902 13:35:56.728356 16020 net.cpp:96] Setting up ip2I0902 13:35:56.728690 16020 net.cpp:103] Top shape: 64 10 1 1 (640)I0902 13:35:56.728705 16020 net.cpp:113] Memory required for data: 5169920I0902 13:35:56.728734 16020 net.cpp:67] Creating Layer lossI0902 13:35:56.728747 16020 net.cpp:394] loss <- ip2I0902 13:35:56.728767 16020 net.cpp:394] loss <- labelI0902 13:35:56.728786 16020 net.cpp:356] loss -> lossI0902 13:35:56.728811 16020 net.cpp:96] Setting up lossI0902 13:35:56.728837 16020 net.cpp:103] Top shape: 1 1 1 1 (1)I0902 13:35:56.728849 16020 net.cpp:109]     with loss weight 1I0902 13:35:56.728878 16020 net.cpp:113] Memory required for data: 5169924

Loss(损失)

I0902 13:35:56.728893 16020 net.cpp:170] loss needs backward computation.I0902 13:35:56.728909 16020 net.cpp:170] ip2 needs backward computation.I0902 13:35:56.728924 16020 net.cpp:170] relu1 needs backward computation.I0902 13:35:56.728938 16020 net.cpp:170] ip1 needs backward computation.I0902 13:35:56.728953 16020 net.cpp:170] pool2 needs backward computation.I0902 13:35:56.728970 16020 net.cpp:170] conv2 needs backward computation.I0902 13:35:56.728984 16020 net.cpp:170] pool1 needs backward computation.I0902 13:35:56.728998 16020 net.cpp:170] conv1 needs backward computation.I0902 13:35:56.729014 16020 net.cpp:172] mnist does not need backward computation.I0902 13:35:56.729027 16020 net.cpp:208] This network produces output lossI0902 13:35:56.729053 16020 net.cpp:467] Collecting Learning Rate and Weight Decay.I0902 13:35:56.729071 16020 net.cpp:219] Network initialization done.I0902 13:35:56.729085 16020 net.cpp:220] Memory required for data: 5169924I0902 13:35:56.729277 16020 solver.cpp:156] Creating test net (#0) specified by net file: examples/mnist/lenet_train_test.prototxt

Completion(完成)

I0902 13:35:56.806970 16020 solver.cpp:46] Solver scaffolding done.I0902 13:35:56.806984 16020 solver.cpp:165] Solving LeNet

Updating Parameters

权重更新是在Solver::ComputeUpdateValue()中完成的。ComputeUpdateValue方法利用权重衰减项(weight decay)计算出权重梯度(weight gradients)(现阶段还只有误差梯度),进而计算出每个权重最后的梯度。然后经过与学习速率α相乘后,该值被存储于每一个参数(Blob的diff域)中。最后,调用每个参数的Blob::Update()方法进行最后的参数更新(Blob的data值减去diff值)。


Snapshotting and Resuming

Solver使用Solver::Snapshot()方法保存权重的值,使用Solver::SnapshotSolverState()保存状态。保存的权重表示当前学习到的模型,并且允许从该状态继续进行学习。训练可以通过Solver::Restore()方法和Solver::RestoreSolverState()方法继续进行。

权重保存文件没有拓展名,状态保存文件的拓展名是.solverstate extension,每种文件都有一个_iter_N后缀,表明保存时的迭代次数。

在Solver定义文件(*_solver.prototxt)中,保存方法是如下定义:

# 保存间隔(单位:迭代次数)snapshot: 5000# 保存文件的前缀,注意:是相对于caffe根目录,而不是具体Solver定义文件目录snapshot_prefix: "/path/to/model"# 是否保存Blob的diff域(存储梯度值),有助于debug,但是耗费更多存储空间snapshot_diff: false# 训练完成后是否保存最后的状态,默认是Truesnapshot_after_train: true
原创粉丝点击