pytorch指定層凍結。初始化權重

  • PyTorch的Module.modules()和Module.children()

 在PyTorch中,所有的neural network module都是class torch.nn.Module的子類,在Modules中可以包含其它的Modules,以一種樹狀結構進行嵌套。

當需要返回神經網絡中的各個模塊時,Module.modules()方法返回網絡中所有模塊的一個iterator,而Module.children()方法返回所有直接子模塊的一個iterator。具體而言:

list ( nn.Sequential(nn.Linear(10, 20), nn.ReLU()).modules() )
Out[9]:
[Sequential (
(0): Linear (10 -> 20)
(1): ReLU ()
), Linear (10 -> 20), ReLU ()]

In [10]: list( nn.Sequential(nn.Linear(10, 20), nn.ReLU()) .children() )
Out[10]: [Linear (10 -> 20), ReLU ()]

參考:https://www.jianshu.com/p/7a7d45b8e0ee這個寫的很好

  • 選擇特定的層進行finetune
    • 先使用Module.children()方法查看網絡的直接子模塊,將不需要調整的模塊中的參數設置爲param.requires_grad = False,同時用一個list收集需要調整的模塊中的參數。具體代碼爲:
count = 0
    para_optim = []
    for k in model.children():
        count += 1
        # 6 should be changed properly
        if count > 6:
            for param in k.parameters():
                para_optim.append(param)
        else:
            for param in k.parameters():
                param.requires_grad = False
optimizer = optim.RMSprop(para_optim, lr)

 固定部分參數訓練

# 只有True的才訓練
optimizer.SGD(filter(lambda p: p.requires_grad, model.parameters()), lr=1e-3)

也就是我build.py裏的

def make_optimizer(cfg, model):
    params = []
    for key, value in model.named_parameters():
        if not value.requires_grad:                 #value.requires_grad 那麼not value.requires_grad false 跳過
            continue
        lr = cfg.SOLVER.BASE_LR                                     #   Adam比較小 0.00001/0.0003 或者 SGD 0.0025
        weight_decay = cfg.SOLVER.WEIGHT_DECAY                      #  0.0001
        if "bias" in key:                                           #  false here  'backbone.fpn.fpn_inner1.bias' 'backbone.fpn.fpn_layer1.bias'纔會'rpn.head.conv.bias'
            lr = cfg.SOLVER.BASE_LR * cfg.SOLVER.BIAS_LR_FACTOR     #  BIAS_LR_FACTOR:2  2e-05 # lr = 0.0025* cfg.SOLVER.BIAS_LR_FACTOR   不可以 不好
            weight_decay = cfg.SOLVER.WEIGHT_DECAY_BIAS             # 0
        params += [{"params": [value], "lr": lr, "weight_decay": weight_decay}]

    #optimizer = torch.optim.SGD(params, lr, momentum=cfg.SOLVER.MOMENTUM) 
    return optimizer
  • 另外一個小技巧就是在nn.Module裏,可以在中間插入這個,這樣前面的參數就是False,而後面的不變
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 6, 5)
        self.conv2 = nn.Conv2d(6, 16, 5)

        for p in self.parameters():
            p.requires_grad=False

        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

訓練特定層,凍結其它層
另一種使用預訓練模型的方法是對它進行部分訓練。具體做法是,將模型起始的一些層的權重保持不變,重新訓練後面的層,得到新的權重。在這個過程中,可多次進行嘗試,從而能夠依據結果找到frozen layers和retrain layers之間的最佳搭配。
如何使用預訓練模型,是由數據集大小和新舊數據集(預訓練的數據集和自己要解決的數據集)之間數據的相似度來決定的。
下圖表展示了在各種情況下應該如何使用預訓練模型:

場景一:數據集小,數據相似度高(與pre-trained model的訓練數據相比而言)

在這種情況下,因爲數據與預訓練模型的訓練數據相似度很高,因此我們不需要重新訓練模型。我們只需要將輸出層改制成符合問題情境下的結構就好。我們使用預處理模型作爲模式提取器。

比如說我們使用在ImageNet上訓練的模型來辨認一組新照片中的小貓小狗。在這裏,需要被辨認的圖片與ImageNet庫中的圖片類似,但是我們的輸出結果中只需要兩項——貓或者狗。在這個例子中,我們需要做的就是把dense layer和最終softmax layer的輸出從1000個類別改爲2個類別。

場景二:數據集小,數據相似度不高

在這種情況下,我們可以凍結預訓練模型中的前k個層中的權重,然後重新訓練後面的n-k個層,當然最後一層也需要根據相應的輸出格式來進行修改。

因爲數據的相似度不高,重新訓練的過程就變得非常關鍵。而新數據集大小的不足,則是通過凍結預訓練模型的前k層進行彌補。

場景三:數據集大,數據相似度不高

在這種情況下,因爲我們有一個很大的數據集,所以神經網絡的訓練過程將會比較有效率。然而,因爲實際數據與預訓練模型的訓練數據之間存在很大差異,採用預訓練模型將不會是一種高效的方式。

因此最好的方法還是將預處理模型中的權重全都初始化後在新數據集的基礎上重頭開始訓練。

場景四:數據集大,數據相似度高

這就是最理想的情況,採用預訓練模型會變得非常高效。最好的運用方式是保持模型原有的結構和初始權重不變,隨後在新數據集的基礎上重新訓練。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章