如果你的PyTorch優化器效果欠佳,試試這四種深度學習中的高級優化技術吧
在深度學習領域,優化器的選擇對模型性能至關重要。雖然PyTorch中的標準優化器如SGD、Adam和AdamW被廣泛應用,但它們并非在所有情況下都是最優選擇。本文將介紹四種高級優化技術,這些技術在某些任務中可能優于傳統方法,特別是在面對復雜優化問題時。
我們將探討以下算法:
- 序列最小二乘規劃(SLSQP)
- 粒子群優化(PSO)
- 協方差矩陣自適應進化策略(CMA-ES)
- 模擬退火(SA)
這些方法的主要優勢包括:
- 無梯度優化:適用于非可微操作,如采樣、取整和組合優化。
- 僅需前向傳播:通常比傳統方法更快,且內存效率更高。
- 全局優化能力:有助于避免局部最優解。
需要注意的是,這些方法最適合優化參數數量較少(通常少于100-1000個)的情況。它們特別適用于優化關鍵參數、每層特定參數或超參數。
實驗準備
在開始實驗之前,我們需要設置環境并定義一些輔助函數。以下是必要的導入和函數定義:
from functools import partial
from collections import defaultdict
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
import numpy as np
import scipy.optimize as opt
import matplotlib.pyplot as plt
# 設置隨機種子以確保結果可復現
torch.manual_seed(42)
np.random.seed(42)
# 輔助函數:在PyTorch模型和NumPy向量之間轉換權重
def set_model_weights_from_vector(model, numpy_vector):
weight_vector = torch.tensor(numpy_vector, dtype=torch.float64)
model[0].weight.data = weight_vector[0:4].reshape(2, 2)
model[2].weight.data = weight_vector[4:8].reshape(2, 2)
model[2].bias.data = weight_vector[8:10]
return model
def get_vector_from_model_weights(model):
return torch.cat([
model[0].weight.data.view(-1),
model[2].weight.data.view(-1),
model[2].bias.data]
).detach().numpy()
# 用于跟蹤和更新損失的函數
def update_tracker(loss_tracker, optimizer_name, loss_val):
loss_tracker[optimizer_name].append(loss_val)
if len(loss_tracker[optimizer_name]) > 1:
min_loss = min(loss_tracker[optimizer_name][-2], loss_val)
loss_tracker[optimizer_name][-1] = min_loss
return loss_tracker
這些函數將用于在不同的優化算法之間轉換模型權重,并跟蹤優化過程中的損失。
接下來定義目標函數和PyTorch優化循環:
def objective(x, model, input, target, loss_tracker, optimizer_name):
model = set_model_weights_from_vector(model, x)
loss_val = F.mse_loss(model(input), target).item()
loss_tracker = update_tracker(loss_tracker, optimizer_name, loss_val)
return loss_val
def pytorch_optimize(x, model, input, target, maxiter, loss_tracker, optimizer_name="Adam"):
set_model_weights_from_vector(model, x)
optimizer = optim.Adam(model.parameters(), lr=1.)
# 訓練循環
for iteration in range(maxiter):
loss = F.mse_loss(model(input), target)
optimizer.zero_grad()
loss.backward()
optimizer.step()
loss_tracker = update_tracker(loss_tracker, optimizer_name, loss.item())
final_x = get_vector_from_model_weights(model)
return final_x, loss.item()
最后設置實驗所需的通用變量:
model = nn.Sequential(nn.Linear(2, 2, bias=False), nn.ReLU(), nn.Linear(2, 2, bias=True)).double()
input_tensor = torch.randn(32, 2).double() # 隨機輸入張量
input_tensor[:, 1] *= 1e3 # 增加一個變量的敏感度
target = input_tensor.clone() # 目標是輸入本身(恒等函數)
num_params = 10
maxiter = 100
x0 = 0.1 * np.random.randn(num_params)
loss_tracker = defaultdict(list)
這些設置為我們的實驗創建了一個簡單的神經網絡模型、定義了輸入、目標和初始參數。
在下一部分中,我們將開始實現和比較不同的優化技術。
優化技術比較
1、PyTorch中的Adam優化器
作為基準,我們首先使用PyTorch的Adam優化器。Adam是一種自適應學習率優化算法,在深度學習中廣泛使用。
optimizer_name = "PyTorch Adam"
result = pytorch_optimize(x0, model, input_tensor, target, maxiter, loss_tracker, optimizer_name)
print(f'Adam優化器最終損失: {result[1]}')
運行此代碼后,我們得到以下結果:
Adam優化器最終損失: 91.85612831226527
考慮到初始損失值約為300,000,這個結果在100次優化步驟后已經有了顯著改善。
2、序列最小二乘規劃 (SLSQP)
序列最小二乘規劃(SLSQP)是一種強大的優化算法,特別適用于具有連續參數的問題。它通過在每一步構建二次近似來逼近最優解。
optimizer_name = "slsqp"
args = (model, input_tensor, target, loss_tracker, optimizer_name)
result = opt.minimize(objective, x0, method=optimizer_name, args=args, options={"maxiter": maxiter, "disp": False, "eps": 0.001})
print(f"SLSQP優化器最終損失: {result.fun}")
運行SLSQP算法,我們獲得以下結果:
SLSQP優化器最終損失: 3.097042282788268
SLSQP的性能明顯優于Adam,這表明在某些情況下,非傳統優化方法可能更有效。
3、粒子群優化 (PSO)
粒子群優化(PSO)是一種基于群體智能的優化算法,其靈感來自于鳥群和魚群的社會行為。PSO在非連續和非光滑的問題上表現尤為出色。
from pyswarm import pso
lb = -np.ones(num_params)
ub = np.ones(num_params)
optimizer_name = 'pso'
args = (model, input_tensor, target, loss_tracker, optimizer_name)
result_pso = pso(objective, lb, ub, maxiter=maxiter, args=args)
print(f"PSO優化器最終損失: {result_pso[1]}")
PSO的優化結果如下:
PSO優化器最終損失: 1.0195048385714032
PSO的表現進一步超越了SLSQP,這凸顯了在復雜優化問題中探索多種算法的重要性。
4、協方差矩陣自適應進化策略 (CMA-ES)
協方差矩陣自適應進化策略(CMA-ES)是一種高度復雜的優化算法,特別適用于難以處理的非凸優化問題。它通過自適應地學習問題的協方差結構來指導搜索過程。
from cma import CMAEvolutionStrategy
es = CMAEvolutionStrategy(x0, 0.5, {"maxiter": maxiter, "seed": 42})
optimizer_name = 'cma'
args = (model, input_tensor, target, loss_tracker, optimizer_name)
while not es.stop():
solutions = es.ask()
object_vals = [objective(x, *args) for x in solutions]
es.tell(solutions, object_vals)
print(f"CMA-ES優化器最終損失: {es.result[1]}")
CMA-ES的優化結果如下:
(5_w,10)-aCMA-ES (mu_w=3.2,w_1=45%) in dimension 10 (seed=42, Thu Oct 12 22:03:53 2024)
CMA-ES優化器最終損失: 4.084718909553896
雖然CMA-ES在這個特定問題上沒有達到最佳性能,但它在處理復雜的多模態優化問題時通常表現出色。
5、 模擬退火 (SA)
模擬退火(SA)是一種受冶金學啟發的優化算法,它模擬了金屬冷卻和退火過程。SA在尋找全局最優解方面特別有效,能夠避免陷入局部最優解。
from scipy.optimize import dual_annealing
bounds = [(-1, 1)] * num_params
optimizer_name = 'simulated_annealing'
args = (model, input_tensor, target, loss_tracker, optimizer_name)
result = dual_annealing(objective, bounds, maxiter=maxiter, args=args, initial_temp=1.)
print(f"SA優化器最終損失: {result.fun}")
SA的優化結果如下:
SA優化器最終損失: 0.7834294257939689
可以看到,針對我們的問題SA表現最佳,這突顯了其在復雜優化問題中的潛力。
下面我們來可視化這些優化器的性能,并討論結果的含義。
結果可視化與分析
為了更好地理解各種優化算法的性能,我們將使用matplotlib庫來可視化優化過程中的損失變化。
plt.figure(figsize=(10, 6))
line_styles = ['-', '--', '-.', ':']
for i, (optimizer_name, losses) in enumerate(loss_tracker.items()):
plt.plot(np.linspace(0, maxiter, len(losses)), losses,
label=optimizer_name,
linestyle=line_styles[i % len(line_styles)],
linewidth=5,
)
plt.xlabel("Iteration", fontsize=20)
plt.ylabel("Loss", fontsize=20)
plt.ylim(1e-1, 1e7)
plt.yscale('log')
plt.title("Loss For Different Optimizers", fontsize=20)
plt.grid(True, linestyle='--', alpha=0.6)
plt.legend(loc='upper right', fontsize=20)
plt.tight_layout()
plt.savefig('optimizers.png')
plt.show()
執行上述代碼后,我們得到了以下可視化結果:
結果分析
- Adam優化器:作為基準Adam表現穩定但收斂速度相對較慢。這反映了在某些復雜問題中,傳統梯度下降方法可能不是最優選擇。
- SLSQP:序列最小二乘規劃表現出快速的初始收斂,這表明它在處理具有連續參數的問題時非常有效。
- PSO:粒子群優化展示了良好的全局搜索能力,能夠迅速找到較好的解。這凸顯了其在非凸優化問題中的潛力。
- CMA-ES:雖然在本實驗中收斂較慢,但協方差矩陣自適應進化策略通常在處理高度復雜和多模態的問題時表現出色。其性能可能在更復雜的優化場景中更為突出。
- 模擬退火:我們這個特定問題SA表現最為出色,僅用幾次迭代就達到了最低損失。這突顯了其在避免局部最優解并快速找到全局最優解方面的優勢。
需要注意的是,每種算法的"迭代"定義可能不同,因此直接比較迭代次數可能不夠公平。例如SA的每次迭代可能包含多次目標函數評估。
總結
在特定問題上,非傳統優化方法可能比標準的梯度下降算法(如Adam)表現更好。然而,這并不意味著這些方法在所有情況下都優于傳統方法。選擇最適合的優化算法應基于具體問題的特性:
- 對于參數數量較少(100-1000個)的優化問題,考慮嘗試本文介紹的高級優化技術。
- 在處理非可微操作或復雜的損失景觀時,無梯度方法(如PSO、CMA-ES和SA)可能更有優勢。
- 對于需要滿足復雜約束的優化問題,SLSQP可能是一個很好的選擇。
- 在計算資源有限的情況下,考慮使用僅需前向傳播的方法,如PSO或SA。
- 對于高度非凸的問題,CMA-ES和SA可能更容易找到全局最優解。
最后,建議在實際應用中對多種優化方法進行比較和測試,以找到最適合特定問題的算法。同時要注意這些高級方法在大規模問題(參數數量超過1000)上可能面臨計算效率的挑戰。
未來研究方向
- 探索這些高級優化技術在更復雜的深度學習模型中的應用。
- 研究如何有效地將這些方法與傳統的梯度下降算法結合,以開發混合優化策略。
- 開發更高效的并行化實現,以提高這些算法在大規模問題上的適用性。
- 探索這些方法在特定領域(如強化學習、神經架構搜索)中的潛在應用。
通過深入理解和靈活運用這些高級優化技術,研究者和工程師可以在面對復雜優化問題時拓展解決方案的范圍,potentially unlocking新的性能水平和應用可能性。