1:对于随机梯度下降SGD可能大家都比较了解,也很熟悉,说起来也很简单,在使用中我们一般用的是带mini batch的SGD。这个也描述起来很简单,但是在使用中还是有一些trick才可以的,最近在做一个实验,由于L-BFGS的速度太慢所以选择了带Mini-batch的SGD,我来说下我的心得。
2:其实梯度下降算法,在使用的时候无非是要考虑到2个方面,一个是方向,一个是步长,方向决定你是否走在了优化的道路上还是优化道路的负方向,步长是决定你要走多久才能到最优的地方。对于第一个问题很好解决,就是求梯度,梯度的负方向就是了。难的是求步长,如果步子太小,则需要很长的时间才能走到目的地,如果步子过大可能在目的地的周围来走震荡。所以重点在于如何选择步长。
3:对于随机梯度中,步长的选择方法有很多,最简单的莫过于设置一个比较小的步长,让算法慢慢去运行去就是了,也有别的方法就是可以计算步长的算法,这个我也试过了,反正不好弄,我就选择了最简单的小步长。但是何时算法自己知道差不多了可以停止了呢?我主要想说下这个问题:很多人都说设置迭代一定的次数或者比较两次梯度的变化,或者两次cost的变化,这个地方我不是特别同意,因为尤其是设置了一个小步长的时候,迭代一定次数当然可以,但是这个次数到底多少可以?没办法知道,所以如果设置了一定次数,次数过小的话肯定此时并没有达到最优的或者很接近最优点的地方,如果过大理论上是可以的,但是多少才算过大?你觉得10万次很多,但是不一定10万次算法可以达到,所以这个我觉得不太靠谱,对于比较两次梯度变化或者cost变化,同样存在这个问题,如果步长很小的话,那么同样连续两次之间的梯度和cost变化很小也是无法保证此时接近最优点的啊。
4:这里我介绍一个方法叫做early-stop,其实也是很成熟的方法了,大概思路是在训练的过程中,使用验证集周期性的来测试当前计算出来的参数,在验证集上测试当前参数对验证集的效果,如果效果可以,就保存起来,当很长一段时间都是此效果的话那么就迭代停止,该组参数就认为是不错的参数。这个方法叫做交叉验证,但是我看到有的地方写的是交叉验证用于超参的选择,而这个地方我不是选取的超参,所以不知道到底用对了没有。
5:我在下面贴出来我的整个sgd的matlab的代码,来大概说下这个early-stop。
function [ optParams ] = SGD( funObj,theta,data,labels,options ) % Runs stochastic gradient descent with momentum to optimize the % parameters for the given objective. % % Parameters: % funObj - function handle which accepts as input theta, % data, labels and returns cost and gradient w.r.t % to theta. % theta - unrolled parameter vector % data - stores data in m x n x numExamples tensor % labels - corresponding labels in numExamples x 1 vector % options - struct to store specific options for optimization % % Returns: % opttheta - optimized parameter vector % % Options (* required) % epochs* - number of epochs through data % alpha* - initial learning rate % minibatch* - size of minibatch % momentum - momentum constant, defualts to 0.9 %% Setup assert(all(isfield(options,{'epochs','alpha','minibatch'})),... 'Some options not defined'); if ~isfield(options,'momentum') options.momentum = 0.9; end; epochs = options.epochs; alpha = options.alpha; minibatch = options.minibatch; m = length(labels); % training set size % Setup for momentum mom = 0.5; momIncrease = 20; velocity = zeros(size(theta)); %%====================================================================== %% SGD loop patience = options.patience; patienceIncreasement = options.patienceIncreasement; improvement = options.improvement; validationHandler = options.validationHandler; bestParams = []; bestValidationLoss = inf; validationFrequency = min(ceil(m/minibatch), patience/2); doneLooping = false; it = 0; e = 0; while (e < epochs) && (~doneLooping) e = e + 1; % randomly permute indices of data for quick minibatch sampling rp = randperm(m); for s=1:minibatch:(m-minibatch+1) it = it + 1; % increase momentum after momIncrease iterations if it == momIncrease mom = options.momentum; end; % get next randomly selected minibatch mb_data = data(:,rp(s:s+minibatch-1)); mb_labels = labels(rp(s:s+minibatch-1)); % evaluate the objective function on the next minibatch [cost grad] = funObj(theta,mb_data,mb_labels); % early stop if mod(it, validationFrequency) == 0 validationLoss = validationHandler(theta); if validationLoss < bestValidationLoss fprintf('validate=====================================current cost:%f, last cost:%f\n', validationLoss, bestValidationLoss); if validationLoss < bestValidationLoss*improvement patience = max(patience, it* patienceIncreasement); bestParams.param = theta; bestParams.loss = validationLoss; end bestValidationLoss = validationLoss; end end if patience < it doneLooping = true; fprintf('stop due to patience[%d] greater than iterate[%d]\n', patience, it); break; end % Instructions: Add in the weighted velocity vector to the % gradient evaluated above scaled by the learning rate. % Then update the current weights theta according to the % sgd update rule %%% YOUR CODE HERE %%% velocity = mom*velocity + alpha*grad; theta = theta - velocity; % fprintf('Epoch %d: Cost on iteration %d is %f\n',e,it,cost); end; % aneal learning rate by factor of two after each epoch alpha = alpha/2.0; end; optParams = theta; end
patience