轉載請注明出處:
http://www.cnblogs.com/darkknightzh/p/6221664.html
參考網址:
https://github.com/torch/nn/issues/873
http://stackoverflow.com/questions/37459812/finetune-a-torch-model
https://github.com/torch/nn/blob/master/doc/module.md
https://github.com/torch/torch7/blob/master/doc/utility.md
=====================================================
170928更新(可以微調層):
參考網址:
http://www.thedataware.com/post/the-torch-adventures-setting-layer-wise-training-parameters
https://github.com/NVIDIA/DIGITS/tree/master/examples/fine-tuning
https://github.com/NVIDIA/DIGITS/blob/master/examples/fine-tuning/lenet-fine-tune.lua#L56
https://stackoverflow.com/questions/37459812/finetune-a-torch-model
https://www.zhihu.com/question/44376850
說明:目前就第一個網址的能finetune參數。
深度學習中目前有參數的為:卷積層-conv(weight+bias),batchnorm層:bn(weight+bias),全連接層-linear(weight+bias)。
因而在torch中使用local params, gradParams = model:parameters()的話,默認得到的#params為上面這三種類型的層的數量之和再乘以2。如果對應沒有bias,則該層參數參數數量為1。
使用http://www.thedataware.com/post/the-torch-adventures-setting-layer-wise-training-parameters的方法,可以更新某個層。該文章是每個層設置不同的學習率,如果只某些特定的層學習率不為0,其它層學習率均為0(或者先定義fineTuneLayerIdx={10,11,12},而后for i = 1, #params改成for i = 1, #fineTuneLayerIdx來減少計算量),則會只更新這些層的參數。需要注意的是,如果fine tune最后幾層還好,可以print(params),來看一下參數,然后計算一下哪些參數是需要更新的,如果更新中間的層。。。只能自己去對應了(特別是如Inception,Resnet這種網絡中間層的參數,對應起來更加蛋疼了吧)。
該網址中對每層都設置學習率的代碼如下:

local params, gradParams = model:parameters() -- Set the learning rate to 0.01 local learningRates = torch.Tensor(#params):fill(0.01) -- Set the learning rate of the second layer to 0.001 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) -- layer-wise optimization for i = 1, #params do local feval = function(x) return err, gradParams[i] end -- run optimizer optim.sgd(feval, params[i], optimState[i]) end end -- model trained
如果使用fineTuneLayerIdx,即只微調部分層,代碼如下:

local params, gradParams = model:parameters() -- 需要finetune的參數層(不是網絡層。網絡層:內部可能還有更小的網絡,比如densenet,resnext等; -- 參數層:正常情況下,一個conv,bn,linear等各有2個參數層,所以參數曾可能比網絡成多很多) local fineTuneLayerIdx = {30,34,35} -- Set the learning rate to 0.01 local learningRates = torch.Tensor(#fineTuneLayerIdx):fill(0.01) -- Set the learning rate of the second layer to 0.001 learningRates[2] = 0.001 optimState = {} for i = 1, #fineTuneLayerIdx 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) -- layer-wise optimization for i = 1, #fineTuneLayerIdx do local feval = function(x) return err, gradParams[fineTuneLayerIdx[i]] end -- run optimizer optim.sgd(feval, params[fineTuneLayerIdx[i]], optimState[i]) end end -- model trained
需要注意的是,如果使用model:parameters(),需要optimState為多個table,不能為下面這樣簡單的一個table:
optimState = { -- 使用model:parameters()時,使用這種optimState有問題 learningRate = learningRates, learningRateDecay = 0.0001, momentum = 0.9, dampening = 0.0, weightDecay = 5e-4 }
否則在第二次運行到optim.sgd(feval, params[fineTuneLayerIdx[i]], optimState[i])時,可能會提示維度不一樣。
另外,https://www.zhihu.com/question/44376850中“知乎用戶”的回答也和這個類似,只不過不知道那個網址中的和這個網址中的誰先誰后吧。
如果使用https://stackoverflow.com/questions/37459812/finetune-a-torch-model中的方法,即:
for i=1, x do c = model:get(i) c.updateGradInput = function(self, inp, out) end c.accGradParameters = function(self,inp, out) end end
我這邊有conv、bn,linear這三種層,會提示下面bn層的錯誤,不清楚是我這邊程序的問題,還是怎么回事。
如果使用https://github.com/NVIDIA/DIGITS/blob/master/examples/fine-tuning/lenet-fine-tune.lua#L56這種方法,其實和上面的類似,只不過沒有設置每層的updateGradInput這個。只設置一個的話,同樣的輸入,每次輸出不一樣(我把所有的conv,bn,linear都設置了= function(self, inp, out) end,為了看一下輸出是否一致。理論上如果這些層參數都不更新,同樣的輸入,最終的輸出應該相同),即感覺沒能fine tune特定的層。
170928更新結束
=====================================================
161229更新:
感謝@linzhineng 。
即便按照本文這樣設置,實際上在微調時,其它層的參數還是會變化。現在凌亂了,不清楚如何微調了/(ㄒoㄒ)/~~
難道只能手動修改更新過程嗎?
161229更新結束:
=====================================================
由於torch每個模塊均有train參數,當其為true時進行訓練,當期為false時進行測試。因而,如果要對訓練好的模型進行微調,如只對某模塊調整參數,其他模塊參數固定,則可以使用第一個參考網址中soumith的方法(該方法固定某模塊,和本文目的是反的):
model:training() model:apply(function(m) if torch.type(m):find("BatchNormalization") then m:evaluate() end end)
說明:一般來說,在訓練時,需要設置model:training(),在測試時,需要設置model:evaluate()。因而微調參數時,上面代碼加在訓練代碼中model:training()后面就可以了(需要適當的修改)。
第四個網址給出了[string] torch.type(object)。因而,對上面的代碼修改如下:如果要達到微調某一模塊參數(如全連接層Linear),只需要使用:
model:evaluate() model:apply(function(m) if torch.type(m):find('Linear') then m:training() end end)
說明:上面代碼測試后成功。但是遇到了一個很詭異的問題。如果第一行改為model:training(),在找到對應的層后,改為m: evaluate (),沒有成功(對應的torch.type(m):find('Linear')==nil),所以才使用了上面的代碼。還有一點,如果判斷torch.type(m):find('Linear')==nil,最后沒有成功改了m的train變量的值,具體不太清楚,最終使用了上面給出的代碼。
上面torch.type(m)會返回模塊的名字,如:
nn.Sequential
nn.SpatialConvolution
nn.SpatialBatchNormalization
nn.ReLU
nn.SpatialMaxPooling
nn.SpatialConvolution
nn.SpatialBatchNormalization
nn.ReLU
上面torch.type(m):find("BatchNormalization"),如果在某層找到了BatchNormalization,則返回找到的起始和結束位置,否則返回nil。
還有,微調時,一般都只微調某一層,但是torch中很多層名字相同,如果要改特定的一層,如conv層,還要繼續修改代碼,判斷是否是需要的那個conv層,否則會將所有的conv層參數都修改。
注意:如果網絡定義使用了Inception層,此處不光返回Inception,還會返回Inception里面各個層(如nn.Sequential,nn.InceptionHisign,nn.DepthConcat等)。
在torch/install/share/lua/5.1/nn/Module.lua中,有如下代碼:
function Module:training() self.train = true end function Module:evaluate() self.train = false end
直覺上,torch中這種方式不如caffe的fine tuning時,設置對應層lr_mult=0容易。
第三個網址有對apply,training,evaluate的較詳細的說明。
此外,第二個網址通過updateGradInput和accGradParameters來達到固定某層參數的效果,不過沒有試過。