02c 梯度下降法2 - cccbook/py2gpt GitHub Wiki

第二章 梯度下降法

本文為 ChatGPT 寫的,但有點不好懂,我有寫一個簡單的《梯度下降法》程式,然後利用 ChatGPT 來解說,請參考下文。

2.1 最優化問題

在深度學習中,我們通常會遇到最優化問題,即尋找一組參數,使得模型的預測結果與真實值之間的誤差最小化。這種問題可以表示為以下形式:

$$\min_{\theta}L(\theta)$$

其中, $\theta$ 表示模型的參數, $L(\theta)$ 表示目標函數,也就是模型預測結果與真實值之間的誤差。我們的目標是找到一組參數 $\theta^*$ ,使得目標函數的值最小化,即:

$$\theta^* = \arg\min_{\theta}L(\theta)$$

在深度學習中,常用的最優化方法是梯度下降法。

2.2 梯度下降法的基本原理

梯度下降法是一種常用的最優化方法,其核心思想是:將目標函數的參數沿著負梯度方向進行微小調整,使得目標函數的值不斷減小,最終收斂到局部最優解或全局最優解。

具體而言,梯度下降法的更新公式如下:

$$\theta_{t+1} = \theta_t - \eta \nabla L(\theta_t)$$

其中, $\theta_t$ 表示第t步的參數值, $\nabla L(\theta_t)$ 表示目標函數在 $\theta_t$ 處的梯度, $\eta$ 表示學習率,用於控制每一步更新的大小。

梯度下降法有多種變體,如批量梯度下降法(Batch Gradient Descent,BGD)、隨機梯度下降法(Stochastic Gradient Descent,SGD)和小批量梯度下降法(Mini-batch Gradient Descent,MGD)等。

2.3 梯度下降法的變形

2.3.1 Momentum

梯度下降法在更新參數時只考慮當前的梯度,因此容易受到噪聲和局部極小值的影響,導致收斂速度較慢。Momentum是一種基於梯度下降法的改進方法,可以加速模型的收斂速度。

Momentum的基本思想是累積之前的梯度信息,並在更新參數時將其考慮進去。具體來說,Momentum引入了一個動量項(momentum term)$v$,用來記錄之前的梯度信息,並在當前的梯度上加上動量項,進行更新。Momentum的更新公式為:

$$v_t=\beta v_{t-1}+(1-\beta)\nabla_{\theta} J(\theta_t)$$

$$\theta_{t+1}=\theta_t-\alpha v_t$$

其中, $v_t$ 表示時間步$t$的動量項, $\beta$ 為動量係數(通常設置為0.9), $\nabla_{\theta} J(\theta_t)$ 為時間步 $t$ 的梯度, $\theta_t$ 為時間步 $t$ 的參數, $\alpha$ 為學習率。動量項累積了之前的梯度信息,使得更新方向在梯度的方向上加上了一個動量,有助於跳出局部極小值,加速模型的收斂速度。

2.3.2 Adagrad

Adagrad是一種自適應學習率的梯度下降法,可以根據每個參數的歷史梯度信息調整學習率,有效地避免了學習率的手動調整。

Adagrad的基本思想是對每個參數維度調整學習率,使得在梯度變化較大的維度上使用較小的學習率,在梯度變化較小的維度上使用較大的學習率。具體來說,Adagrad引入了一個參數累加項(accumulator),用來記錄每個參數的歷史梯度平方和。在更新參數時,將學習率除以累加項的平方根,進行調整。Adagrad的更新公式為:

$$ \begin{aligned} g_{t,i} &= \frac{\partial J(\theta)}{\partial \theta_i} \\ r_{t,i} &= r_{t-1,i} + g_{t,i}^2 \\ \theta_{t+1,i} &= \theta_{t,i} - \frac{\eta}{\sqrt{r_{t,i}+\epsilon}}g_{t,i} \end{aligned} $$

其中, $g_{t,i}$ 是在時間 $t$$\theta_i$ 求偏導後的梯度, $r_{t,i}$ 是過去時間 $t$$\theta_i$ 上的梯度平方和, $\eta$ 是學習率, $\epsilon$ 是一個非常小的數,以避免分母為零。在更新參數時,Adagrad會對每個參數的梯度進行除以 $\sqrt{r_{t,i}+\epsilon}$ 的操作,以達到自適應學習率的效果。

2.4 Python + Pytorch實現

在深度學習中,梯度下降法是最常用的最優化算法之一。PyTorch是一個非常流行的深度學習框架,它提供了一系列內置的最優化算法,包括梯度下降法。在本節中,我們將使用Python和PyTorch來實現梯度下降法。

首先,我們需要定義一個模型和一個損失函數。這裡我們以線性回歸模型為例,並使用均方差(MSE)作為損失函數。

import torch
import torch.nn as nn

class LinearRegression(nn.Module):
    def __init__(self, input_dim, output_dim):
        super(LinearRegression, self).__init__()
        self.linear = nn.Linear(input_dim, output_dim)

    def forward(self, x):
        out = self.linear(x)
        return out

model = LinearRegression(input_dim=1, output_dim=1)
criterion = nn.MSELoss()

接下來,我們需要定義一個優化器。PyTorch提供了一個名為torch.optim的優化器模組,其中包含了多種最優化算法,例如梯度下降法、Adam、Adagrad等等。在這裡,我們使用梯度下降法作為最優化算法。

learning_rate = 0.01
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)

最後,我們可以開始訓練模型。在每個epoch中,我們需要進行以下步驟:

  1. 計算模型輸出。
  2. 計算損失函數值。
  3. 計算梯度。
  4. 更新模型參數
num_epochs = 1000
for epoch in range(num_epochs):
    # Forward pass
    y_pred = model(x_train)

    # Compute loss
    loss = criterion(y_pred, y_train)

    # Compute gradients
    loss.backward()

    # Update parameters
    optimizer.step()

    # Zero gradients
    optimizer.zero_grad()

    # Print loss
    if (epoch+1) % 100 == 0:
        print('Epoch [{}/{}], Loss: {:.4f}'.format(epoch+1, num_epochs, loss.item()))

在上述代碼中,x_train和y_train是訓練集的特徵和標籤。在每個epoch中,我們首先計算模型的輸出,然後計算損失函數的值。接著,我們使用loss.backward()計算梯度,並使用optimizer.step()更新模型參數。最後,我們使用optimizer.zero_grad()來清除之前的梯度。

接下來我們將使用PyTorch實現Adagrad。我們使用之前的簡單線性回歸模型來演示Adagrad的運作。假設我們有以下的訓練數據:

x_train = torch.tensor([[1.0], [2.0], [3.0], [4.0], [5.0]])
y_train = torch.tensor([[2.0], [4.0], [6.0], [8.0], [10.0]])

首先,我們需要定義模型和損失函數:

class LinearRegression(torch.nn.Module):
    def __init__(self, input_dim, output_dim):
        super(LinearRegression, self).__init__()
        self.linear = torch.nn.Linear(input_dim, output_dim)
        
    def forward(self, x):
        out = self.linear(x)
        return out

model = LinearRegression(1, 1)
criterion = torch.nn.MSELoss()

接下來,我們定義優化器並設置超參數。在這個例子中,我們將初始學習率設置為 0.1 ,並將 eps 設置為 1e-8。

optimizer = torch.optim.Adagrad(model.parameters(), lr=0.1, eps=1e-8)

最後,我們開始進行訓練:

num_epochs = 1000
for epoch in range(num_epochs):
    # Forward pass
    y_pred = model(x_train)
    loss = criterion(y_pred, y_train)
    
    # Backward pass
    optimizer.zero_grad()
    loss.backward()
    
    # Update parameters
    optimizer.step()
    
    # Print progress
    if (epoch+1) % 100 == 0:
        print('Epoch [{}/{}], Loss: {:.4f}'.format(epoch+1, num_epochs, loss.item()))

在這個例子中,我們訓練了1000個epoch。每100個epoch,我們輸出當前的損失值。由於Adagrad自適應地調整每個權重的學習率,因此不需要手動調整學習率,可以看到,即使在較高的學習率下,Adagrad也能夠成功地訓練模型,並得到與SGD相當的結果。

⚠️ **GitHub.com Fallback** ⚠️