什麼是優化器?

在深度學習中,優化器就像一位“智能嚮導”,幫助模型從大量參數中找到讓損失函數(比如模型預測與真實標籤的差距)最小化的方向。想象你在爬山,想從山頂(高損失)走到山谷(低損失),優化器就是決定你每一步往哪走、走多遠的“導航系統”。它的核心任務是更新模型參數,讓模型在訓練數據上的表現越來越好。

爲什麼需要不同的優化器?

不同的優化器針對不同的問題設計,各有優缺點:

  • SGD(隨機梯度下降):最基礎的優化器,每次用一個樣本的梯度更新參數。
  • 優點:簡單、內存佔用小。
  • 缺點:收斂慢,對學習率敏感(學習率太大易震盪,太小收斂慢)。
  • 改進:加入“動量”(Momentum)可加速收斂,類似物理中的“慣性”,讓參數更新更平滑。

  • Adam(自適應矩估計):目前最流行的優化器,結合了動量和自適應學習率。

  • 優點:默認參數表現好,收斂快,對學習率不敏感,適合大多數場景。
  • 特點:會根據參數的歷史梯度自動調整學習率,讓不同參數有不同的更新速度。

  • AdamW:Adam的改進版,加入了權重衰減(L2正則化),能有效防止過擬合。

PyTorch中的優化器概覽

PyTorch的torch.optim模塊提供了多種優化器,以下是初學者最常用的幾種:

優化器 核心特點 適用場景
SGD 基礎隨機梯度下降,需手動調學習率和動量 簡單模型、需要嚴格控制參數時
SGD+Momentum 加入動量,加速收斂,減少震盪 訓練波動大的模型(如RNN)
Adam 自適應學習率+動量,默認參數效果優異 大多數深度學習任務(CNN、Transformer等)
AdamW Adam+權重衰減,避免過擬合 數據量小或模型複雜時

實戰:用PyTorch實現優化器對比

下面我們用一個簡單的線性迴歸模型,對比SGD和Adam的優化效果。目標是讓模型學習到y = 2x + 3的線性關係(加入噪聲模擬真實數據)。

步驟1:準備數據

生成帶噪聲的線性數據:

import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import matplotlib.pyplot as plt

# 設置隨機種子,保證結果可復現
torch.manual_seed(42)

# 生成100個樣本,每個樣本1個特徵,標籤爲 y = 2x + 3 + 噪聲
x = torch.randn(100, 1) * 10  # 輸入特徵
y = 2 * x + 3 + torch.randn(100, 1) * 1.5  # 真實關係+噪聲

步驟2:定義模型

用PyTorch的nn.Module定義簡單線性模型:

class LinearRegression(nn.Module):
    def __init__(self):
        super().__init__()
        self.linear = nn.Linear(in_features=1, out_features=1)  # 輸入1維,輸出1維

# 初始化兩個模型,分別用SGD和Adam優化
model_sgd = LinearRegression()
model_adam = LinearRegression()

步驟3:定義損失函數和優化器

損失函數用均方誤差(MSE),優化器分別用SGD和Adam:

# 損失函數:均方誤差
criterion = nn.MSELoss()

# SGD優化器:學習率設爲0.01,需手動控制學習率
optimizer_sgd = optim.SGD(model_sgd.parameters(), lr=0.01)

# Adam優化器:默認參數,無需手動調參
optimizer_adam = optim.Adam(model_adam.parameters(), lr=0.01)

步驟4:訓練模型

訓練循環:前向傳播→計算損失→反向傳播→更新參數

def train(optimizer, model, x, y, epochs=1000):
    losses = []
    for epoch in range(epochs):
        # 前向傳播:模型預測
        pred = model(x)
        loss = criterion(pred, y)
        losses.append(loss.item())

        # 反向傳播+參數更新
        optimizer.zero_grad()  # 清空梯度(避免累積)
        loss.backward()        # 計算梯度
        optimizer.step()       # 更新參數

        # 每100輪打印一次損失
        if (epoch + 1) % 100 == 0:
            print(f'Epoch {epoch+1}, Loss: {loss.item():.4f}')
    return losses

# 分別訓練兩個模型
losses_sgd = train(optimizer_sgd, model_sgd, x, y)
losses_adam = train(optimizer_adam, model_adam, x, y)

步驟5:對比結果

訓練後,我們可以通過損失曲線最終參數觀察效果:

  1. 損失曲線
   plt.figure(figsize=(10, 5))
   plt.plot(losses_sgd, label='SGD')
   plt.plot(losses_adam, label='Adam')
   plt.xlabel('Epoch')
   plt.ylabel('Loss')
   plt.title('Loss Curve Comparison')
   plt.legend()
   plt.show()

(直觀感受:Adam收斂更快,損失下降更平穩;SGD可能震盪且收斂慢)

  1. 最終參數
   print("SGD訓練後的參數:")
   print(f"權重: {model_sgd.linear.weight.item():.2f}, 偏置: {model_sgd.linear.bias.item():.2f}")

   print("\nAdam訓練後的參數:")
   print(f"權重: {model_adam.linear.weight.item():.2f}, 偏置: {model_adam.linear.bias.item():.2f}")

(輸出應爲接近真實值 權重≈2,偏置≈3,Adam參數更穩定)

總結與建議

  • 初學者首選Adam:默認參數幾乎適用於所有場景,收斂快且穩定性高,不用手動調學習率。
  • SGD的適用場景:若需要嚴格控制參數(如小數據集),或想嘗試動量、學習率調整等技巧。
  • 關鍵技巧:訓練時若損失不下降,嘗試增大學習率(lr=0.1)或換AdamW(防過擬合)。

通過實戰,你可以發現:優化器就像工具,沒有絕對“最好”的,只有“最適合”的。先從Adam開始,再根據任務需求嘗試其他優化器吧!

小夜