(原)torch中微調某層參數


轉載請注明出處:

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
View Code

如果使用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
View Code

需要注意的是,如果使用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來達到固定某層參數的效果,不過沒有試過。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM