Torch7入门续集补充(2)--- 每一层设置不同的学习率(finetuning有用)
2017-09-14 18:43
267 查看
总说
咋说来着,有时候你真的很想finetuning一个网络,想让网络前面固定或是学习率很小,但是你会发现,完全弄不来啊!虽然你可能发现了有一个叫nn.ZeroGrad的layer,看看代码:
local ZeroGrad, parent = torch.class('nn.ZeroGrad', 'nn.Module') function ZeroGrad:updateOutput(input) self.output:set(input) return self.output end -- the gradient is simply zeroed. -- useful when you don't want to backpropgate through certain paths. function ZeroGrad:updateGradInput(input, gradOutput) self.gradInput = nn.utils.recursiveResizeAs(self.gradInput, input) self.gradInput = nn.utils.recursiveFill(self.gradInput, 0) return self.gradInput end
就是直接将gradInput填0就行。这样,在 nn.ZeroGrad前面的层的梯度都是0,从而使得前面固定。
但是,你如果想要让前面的网络学习率不是0,这就难办了。
两种方法
第一种:针对使用 SGD的方法
最简单的就是直接用包,https://github.com/gpleiss/nnlr 。这种是自己设置lrs来进行:
我们先看sgd的代码:
lrs是learningRates, 其实在SGD中,可以为每个参数设置不同的学习率的。见下面代码:
function optim.sgd(opfunc, x, config, state) -- (0) get/update state local config = config or {} local state = state or config local lr = config.learningRate or 1e-3 local lrd = config.learningRateDecay or 0 local wd = config.weightDecay or 0 local mom = config.momentum or 0 local damp = config.dampening or mom local nesterov = config.nesterov or false local lrs = config.learningRates local wds = config.weightDecays state.evalCounter = state.evalCounter or 0 local nevals = state.evalCounter assert(not nesterov or (mom > 0 and damp == 0), "Nesterov momentum requires a momentum and zero dampening") -- (1) evaluate f(x) and df/dx local fx,dfdx = opfunc(x) -- (2) weight decay with single or individual parameters if wd ~= 0 then dfdx:add(wd, x) elseif wds then if not state.decayParameters then state.decayParameters = torch.Tensor():typeAs(x):resizeAs(dfdx) end state.decayParameters:copy(wds):cmul(x) dfdx:add(state.decayParameters) end -- (3) apply momentum if mom ~= 0 then if not state.dfdx then state.dfdx = torch.Tensor():typeAs(dfdx):resizeAs(dfdx):copy(dfdx) else state.dfdx:mul(mom):add(1-damp, dfdx) end if nesterov then dfdx:add(mom, state.dfdx) else dfdx = state.dfdx end end -- (4) learning rate decay (annealing) local clr = lr / (1 + nevals*lrd) -- (5) parameter update with single or individual learning rates if lrs then if not state.deltaParameters then state.deltaParameters = torch.Tensor():typeAs(x):resizeAs(dfdx) end -- deltaParameters是 梯度*每个参数的学习率 state.deltaParameters:copy(lrs):cmul(dfdx) -- 然后进行更新参数x x:add(-clr, state.deltaParameters) else x:add(-clr, dfdx) end -- (6) update evaluation counter state.evalCounter = state.evalCounter + 1 -- return x*, f(x) before optimization return x,{fx} end
关键看这个:
-- (5) parameter update with single or individual learning rates if lrs then if not state.deltaParameters then state.deltaParameters = torch.Tensor():typeAs(x):resizeAs(dfdx) end -- deltaParameters是 梯度*每个参数的学习率 state.deltaParameters:copy(lrs):cmul(dfdx) -- 然后进行更新参数x x:add(-clr, state.deltaParameters) else x:add(-clr, dfdx) end
可以看到,如果设置了lrs(我们在外面一般是传入optmState.learningRates, 注意是”learningRates”不是”learningRate”),那么就先将lrs乘以梯度,然后在将这个已经对每个参数的梯度乘了一个特定系数(这个系数是每个参数的学习率),再乘以一个总体的学习率 -clr,然后加上x,x参数就这样更新了。
-- deltaParameters是 梯度*每个参数的学习率 state.deltaParameters:copy(lrs):cmul(dfdx)
因此:我们可以通过改变optimState.learningRates的值来逐层设置学习率(weight和bias都可以单独设置),看例子:
-- suppose you have a model called model lrs_model = model:clone() lrs = lrs_model:getParameters() -- 为了让lrs的长度是所有参数的长度。并且是严格按照逐层逐参数展开的。因此lrs严格对应每个参数。 lrs:fill(1) -- setting the base learning rate to 1 -- 这里设置第5层的bias的学习率是2(都是相对值) lrs_model:get(5).bias:fill(2) -- 设置第2层的weight的相对学习率是3 lrs_model:get(2).weight:fill(3) -- now pass lrs_model to optimState, which was created previously optimState.learningRates = lrs
由于getParameters是对于每层的参数进行flaten, 所以一层的参数先展开weight, 再展开bias。因此这里
lrs_model:get(5).bias:fill(2)就是将lrs的某些值由1变成2,同理解释下面一行。最后我们将
lrs(按照上面的代码来讲是,大部分为1,有些变成了2,有些变成了3)作为 optimState.learningRates即可。
通用算法的学习率设置
核心:利用parameters而不是getParameters来获得参数!。这就有意思了,因为几乎所有的代码都是通过 getParameters来将网络的参数拉成一个向量,然后进行优化,这个你已经知道了的。但是如果你看源码:
function Module:getParameters() -- get parameters local parameters,gradParameters = self:parameters() local p, g = Module.flatten(parameters), Module.flatten(gradParameters) assert(p:nElement() == g:nElement(), 'check that you are sharing parameters and gradParameters') if parameters then for i=1,#parameters do assert(parameters[i]:storageOffset() == gradParameters[i]:storageOffset(), 'misaligned parameter at ' .. tostring(i)) end end return p, g end
这里可以看到调用了
parameters函数,然后再进行“压平”flatten。再看
parameters:
function Module:parameters() if self.weight and self.bias then return {self.weight, self.bias}, {self.gradWeight, self.gradBias} elseif self.weight then return {self.weight}, {self.gradWeight} elseif self.bias then return {self.bias}, {self.gradBias} else return end end
可以看到这里直接返回 table 类型。因此如果我们使用
parameters函数,则返回一个table,table中的每个tensor对应每层的weight/bias。因此我们需要对于每层都弄一个optimState就可以了。
local params, gradParams = model:parameters() -- 设置每层的学习率为0.01 local learningRates = torch.Tensor(#params):fill(0.01) -- 将第2层的设置为0.01 learningRates[2] = 0.001 optimState = {} for i = 1, #params do table.insert(optimState, { learningRate = learningRates[i], learningRateDecay = 0.0001, momentum = 0.9, dampening = 0.0, weightDecay = 5e-4 }) end for e = 1, epochs do -- Get MNIST batch X, Y = get_mnist_batch(batch_size) -- forward -> backward (outside of feval) model:zeroGradParameters() out = model:forward(X) err = criterion:forward(out, Y) gradOutputs = criterion:backward(out, Y) model:backward(X, gradOutputs) -- 对每一层的参数 params for i = 1, #params do local feval = function(x) return err, gradParams[i] end optim.sgd(feval, params[i], optimState[i]) end end
附加参考链接:
https://gist.github.com/szagoruyko/1e994e713fce4a41773e (第五问)
http://www.thedataware.com/post/the-torch-adventures-setting-layer-wise-training-parameters
https://gist.github.com/farrajota/f1b4616d67c07b17d817a7baf2659646
相关文章推荐
- Torch7入门续集补充--- nngraph包的使用
- Torch7入门续集(三)----Simple Layers的妙用
- Torch7入门续集(七)--- clone与net替换某一层
- Torch7入门续集(五)----进一步了解optim
- Torch7入门续集(八)---终结篇----不再写Torch博客了,反正就是难受
- Torch7入门续集(四)----利用Table layers构建灵活的网络
- Torch7入门续集(一)----- 更加深入理解Tensor
- 【tensorflow】在不同层上设置不同的学习率,fine-tuning
- Torch7入门续集(二)---- 更好的使用Math函数
- Torch7入门续集(六)----多GPU运行程序
- QMainWindow与QWidget和QDialog设置布局的不同之处
- 如何设置eclipse不同的workspace共享配置
- 服务器编程入门(13) Linux套接字设置超时的三种方法
- 最新增加功能 - "开发商"可以设置不同数量产品授权的不同价格
- Ext.net mvc 入门学习笔记 (二) MVC4 默认引导路径的设置
- 如何设置eclipse不同的workspace共享配置
- Sharepoint 的web.config两个小设置(非常有用)
- 【转载】8天入门wpf—— 第八天 最后的补充
- [转载]iptables入门教程--设置静态防火墙
- cocos2d-x项目在不同平台设置所使用的资源目录(多个Resources文件夹)