在Pytorch中為不同層設置不同學習率來提升性能,優化深度學習模型
在深度學習模型的訓練過程中,學習率作為一個關鍵的超參數,對模型的收斂速度和最終性能有著重大影響。傳統方法通常采用統一的學習率,但隨著研究的深入,我們發現為網絡的不同層設置不同的學習率可能會帶來顯著的性能提升。本文將詳細探討這一策略的實施方法及其在PyTorch框架中的具體應用。
層級學習率的理論基礎
深度神經網絡的不同層次在特征提取和信息處理上扮演著不同的角色。基于這一認知,我們可以合理推斷對不同層采用差異化的學習策略可能會更有效:
- 底層特征提取:網絡的前幾層通常負責捕獲通用的低級特征,如邊緣、紋理等。這些特征往往具有較強的通用性和可遷移性。
- 高層語義理解:網絡的后幾層則傾向于提取更為抽象和任務相關的高級特征。
- 任務特定層:如全連接分類層,直接與特定任務相關。
基于上述觀察我們可以制定相應的學習率策略:
- 對于預訓練的底層,使用較小的學習率以保持其已學到的通用特征。
- 對于中間層,可以采用適中的學習率。
- 對于任務特定的頂層,則可以使用較大的學習率以快速適應新任務。
PyTorch實現:以ResNet為例
下面我們將以ResNet18為例,演示如何在PyTorch中實現層級學習率設置。
1、模型定義
首先,我們加載預訓練的ResNet18模型,并修改其最后一層以適應新的分類任務:
import torch
import torch.nn as nn
import torchvision.models as models
# 加載預訓練的ResNet18模型
model = models.resnet18(pretrained=True)
# 修改最后的全連接層以適應新的分類任務
num_classes = 10 # 假設新任務有10個類別
model.fc = nn.Linear(model.fc.in_features, num_classes)
2、參數分組
接下來,我們將模型參數分組,為不同的層設置不同的學習率:
# 定義不同組的學習率
backbone_lr = 1e-4 # 較小的學習率用于預訓練的主干網絡
classifier_lr = 1e-3 # 較大的學習率用于新的分類器層
# 創建參數組
params = [
{'params': model.conv1.parameters(), 'lr': backbone_lr},
{'params': model.bn1.parameters(), 'lr': backbone_lr},
{'params': model.layer1.parameters(), 'lr': backbone_lr},
{'params': model.layer2.parameters(), 'lr': backbone_lr},
{'params': model.layer3.parameters(), 'lr': backbone_lr},
{'params': model.layer4.parameters(), 'lr': backbone_lr},
{'params': model.fc.parameters(), 'lr': classifier_lr}
]
此處我們對ResNet的各個組件進行了更細致的劃分,為不同的層組設置了相應的學習率。這種方法允許我們對模型的學習過程進行更精細的控制。
優化器配置與訓練過程
3、優化器設置
在確定了參數分組后,我們需要選擇合適的優化器并進行配置。這里我們簡單的選用Adam優化器。
optimizer = torch.optim.Adam(params)
這種分組策略同樣適用于其他PyTorch支持的優化器,PyTorch的優化器會自動識別并應用在參數分組中定義的不同學習率。這種設計使得實現層級學習率變得相對簡單。
4、訓練循環
實現了層級學習率后的訓練循環保持不變。PyTorch會在后臺自動處理不同參數組的學習率:
# 定義損失函數
criterion = nn.CrossEntropyLoss()
# 訓練循環
for epoch in range(num_epochs):
model.train()
for inputs, labels in train_loader:
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
# 在每個epoch結束后進行驗證
model.eval()
# ... [驗證代碼]
5、學習率調度
除了設置初始的層級學習率,我們還可以結合學習率調度器來動態調整學習率。PyTorch提供了多種學習率調度器,如StepLR、ReduceLROnPlateau等。以下是一個使用StepLR的示例:
from torch.optim.lr_scheduler import StepLR
scheduler = StepLR(optimizer, step_size=30, gamma=0.1)
# 在訓練循環中更新學習率
for epoch in range(num_epochs):
# ... [訓練代碼]
scheduler.step()
這將每30個epoch將所有參數組的學習率降低為原來的0.1倍。
高級學習率優化技巧
1、漸進式解凍
在微調預訓練模型時,一種有效的策略是漸進式解凍。我們可以先鎖定底層,只訓練頂層,然后逐步解凍更多的層:
# 初始階段:只訓練分類器
for param in model.parameters():
param.requires_grad = False
model.fc.requires_grad = True
# 訓練幾個epoch后
model.layer4.requires_grad = True
# 再過幾個epoch
model.layer3.requires_grad = True
以此類推,凍結其實意味著學習率為0,也就是不對任何參數進行更新。
2、層適應學習率
我們上面已經介紹了手動指定固定的學習率,其實我們還可以通過自定義優化器來實現,不同的層的不同的學習率范圍。我們可以實現一個自定義的優化器來自動調整每一層的學習率:
class LayerAdaptiveLR(torch.optim.Adam):
def __init__(self, params, lr=1e-3, betas=(0.9, 0.999), eps=1e-8, weight_decay=0):
super().__init__(params, lr, betas, eps, weight_decay)
self.param_groups = sorted(self.param_groups, key=lambda x: id(x['params'][0]))
def step(self, closure=None):
loss = None
if closure is not None:
loss = closure()
for group in self.param_groups:
for p in group['params']:
if p.grad is None:
continue
grad = p.grad.data
state = self.state[p]
# 根據梯度統計調整學習率
if len(state) == 0:
state['step'] = 0
state['exp_avg'] = torch.zeros_like(p.data)
state['exp_avg_sq'] = torch.zeros_like(p.data)
exp_avg, exp_avg_sq = state['exp_avg'], state['exp_avg_sq']
beta1, beta2 = group['betas']
state['step'] += 1
exp_avg.mul_(beta1).add_(grad, alpha=1 - beta1)
exp_avg_sq.mul_(beta2).addcmul_(grad, grad, value=1 - beta2)
denom = exp_avg_sq.sqrt().add_(group['eps'])
# 動態調整學習率
step_size = group['lr'] * (exp_avg.abs() / denom).mean().item()
p.data.add_(exp_avg, alpha=-step_size)
return loss
# 使用示例
optimizer = LayerAdaptiveLR(model.parameters(), lr=1e-3)
可以看到,上面我們繼承自Adam優化器,這里我們不用實現優化過程只針對于針對層的學習率變化即可。
總結
層級學習率設置是一種強大的優化技術,特別適用于遷移學習和微調預訓練模型的場景。通過精心設計的學習率策略,可以在保留預訓練模型通用特征的同時有效地適應新任務。結合其他高級技巧,如漸進式解凍、層適應學習率,可以進一步提升模型的訓練效率和性能。
在實際應用中,最佳的學習率配置往往需要通過實驗來確定。建議研究者根據具體任務和模型架構進行適當的調整和實驗,以獲得最佳的訓練效果。