返回文章列表

梯度下降法蒙特卡羅模擬與效能最佳化

本文探討梯度下降法(Gradient Descent, GD)的效能評估與最佳化,利用蒙特卡羅模擬(Monte Carlo Simulation, MCS)分析不同初始條件下的收斂行為,並使用 NumPy 加速歐幾裡得距離計算,提升計算效率。進一步探討隨機梯度下降(Stochastic Gradient

機器學習 演算法

梯度下降法是一種重要的最佳化演算法,廣泛應用於機器學習模型訓練。本文首先介紹如何使用蒙特卡羅模擬評估梯度下降法的穩定性,藉由多次模擬不同初始條件下的收斂過程,觀察迭代次數的變化,進而分析演算法的穩健性。接著,我們探討如何利用 NumPy 的向量化運算加速歐幾裡得距離的計算,有效提升梯度下降法的執行效率。此外,本文也深入研究隨機梯度下降法,及其在殘差平方和最小化和最大化問題中的應用,並透過 Python 程式碼展示如何實作不同變體的 SGD 演算法,以及如何調整學習率等引數以獲得最佳效能。最後,我們比較了不同 SGD 變體的收斂速度和最終結果,提供實際應用中的參考。

梯度下降法的蒙特卡羅模擬與效能最佳化

梯度下降法(Gradient Descent, GD)是一種廣泛應用於機器學習和資料科學的最佳化演算法,用於最小化或最大化目標函式。在實際應用中,GD 的效能會受到初始條件、步長(step size)和迭代次數等因素的影響。為了評估 GD 的穩定性和效能,我們可以利用蒙特卡羅模擬(Monte Carlo Simulation, MCS)進行大量實驗,以統計分析其行為。

蒙特卡羅模擬在梯度下降法中的應用

蒙特卡羅模擬是一種根據隨機抽樣的數值分析技術,可以用來模擬具有不確定性的系統。在梯度下降法的背景下,MCS 可以用來評估不同初始條件下的收斂行為。

import random
import numpy as np
from scipy.spatial import distance

def step(v, direction, step_size):
    """根據給定的方向和步長更新向量 v"""
    return [v_i + step_size * direction_i for v_i, direction_i in zip(v, direction)]

def sigmoid_gradient(v):
    """計算 sigmoid 函式的梯度"""
    return [v_i * (1 - v_i) for v_i in v]

def mod_vector(v):
    """修正向量中的無窮大值"""
    for i, v_i in enumerate(v):
        if v_i == float("inf") or v_i == float("-inf"):
            v[i] = random.randint(-1, 1)
    return v

if __name__ == "__main__":
    trials = 10  # 實驗次數
    sims = 10    # 每次實驗的模擬次數
    avg_its = []  # 用於儲存每次實驗的平均迭代次數
    
    for _ in range(trials):
        its = []  # 用於儲存每次模擬的迭代次數
        for _ in range(sims):
            v = [random.randint(-10, 10) for _ in range(3)]  # 隨機初始化向量
            tolerance = 0.0000001  # 收斂閾值
            iterations = 0  # 迭代次數
            
            while True:
                gradient = sigmoid_gradient(v)  # 計算梯度
                next_v = step(v, gradient, -0.01)  # 更新向量
                v = mod_vector(v)  # 修正向量中的無窮大值
                next_v = mod_vector(next_v)  # 修正下一個向量中的無窮大值
                test_v = distance.euclidean(v, next_v)  # 計算向量間的歐幾裡得距離
                
                if test_v < tolerance:  # 檢查是否收斂
                    break
                
                v = next_v
                iterations += 1  # 增加迭代次數
            
            its.append(iterations)  # 儲存迭代次數
        
        a = round(np.mean(its))  # 計算平均迭代次數
        avg_its.append(a)  # 儲存平均迭代次數
    
    gap = np.max(avg_its) - np.min(avg_its)  # 計算最大與最小平均迭代次數的差距
    print(trials, '次實驗,每次', sims, '次模擬:')
    print('差距:', gap)
    print('平均迭代次數:', round(np.mean(avg_its)))

內容解密:

  1. step函式:根據給定的方向和步長更新向量,用於梯度下降法的迭代更新。
  2. sigmoid_gradient函式:計算 sigmoid 函式的梯度,用於梯度下降法的梯度計算。
  3. mod_vector函式:修正向量中的無窮大值,避免在計算過程中出現無窮大值導致的錯誤。
  4. 主程式:進行多次實驗,每次實驗包含多次模擬。每次模擬隨機初始化一個向量,並使用梯度下降法進行最佳化,直到收斂。統計每次實驗的平均迭代次數,並計算最大與最小平均迭代次數的差距。

使用 NumPy 加速歐幾裡得距離計算

在梯度下降法的實作中,使用 NumPy 可以加速歐幾裡得距離的計算,從而提高整體效能。

import numpy as np

def step(v, direction, step_size):
    return [v_i + step_size * direction_i for v_i, direction_i in zip(v, direction)]

def sigmoid_gradient(v):
    return [v_i * (1 - v_i) for v_i in v]

if __name__ == "__main__":
    v = [random.randint(-10, 10) for _ in range(3)]
    tolerance = 0.0000001
    iterations = 0
    
    while True:
        gradient = sigmoid_gradient(v)
        next_v = step(v, gradient, -0.01)
        norm_v = np.linalg.norm(v)
        norm_next_v = np.linalg.norm(next_v)
        test_v = norm_v - norm_next_v
        
        if test_v < tolerance:
            break
        
        v = next_v
        iterations += 1
    
    print('最小值:', test_v, '\n迭代次數:', iterations)

內容解密:

  1. np.linalg.norm函式:使用 NumPy 的 linalg.norm 函式計算向量的範數(norm),這裡用於計算歐幾裡得距離。
  2. 效能改進:使用 NumPy 的向量化運算可以顯著提高計算效率,減少迭代次數。

梯度下降法的隨機最佳化與極值尋找

梯度下降法(Gradient Descent, GD)是一種常見的最佳化演算法,用於最小化或最大化目標函式。在實際應用中,GD 可以進一步分為批次梯度下降(Batch Gradient Descent)、隨機梯度下降(Stochastic Gradient Descent, SGD)等不同變體。本文將探討 SGD 在 RSS(殘差平方和)最小化與最大化問題中的應用,並透過程式碼範例展示其運作機制。

隨機梯度下降法的基本原理

SGD 的核心思想是在每次迭代中隨機選擇一個樣本來計算梯度,並根據該梯度更新引數。這種方法的優點是計算效率高,尤其是在處理大規模資料集時。

程式碼實作:RSS 最小化

import matplotlib.pyplot as plt
import random
import numpy as np

def rnd():
    return [random.randint(-10, 10) for _ in range(3)]

def random_vectors(n):
    return [[random.randint(-10, 10) for _ in range(3)] for _ in range(n)]

def sos(v):
    return sum(v_i ** 2 for v_i in v)

def sos_gradient(v):
    return [2 * v_i for v_i in v]

def in_random_order(data):
    indexes = list(range(len(data)))
    random.shuffle(indexes)
    for i in indexes:
        yield data[i]

if __name__ == "__main__":
    v = rnd()
    x = random_vectors(3)
    y = random_vectors(3)
    data = list(zip(x, y))
    theta = v
    alpha = 0.01
    min_theta = None
    min_value = float("inf")
    iterations_with_no_improvement = 0
    n = 30
    x_plot = 1
    
    for i in range(n):
        y_plot = np.linalg.norm(theta)
        plt.scatter(x_plot, y_plot, c='r')
        x_plot += 1
        
        s = [sos(theta), sos(x_i), sos(y_i) for x_i, y_i in data]
        value = sum(s)
        
        if value < min_value:
            min_theta = theta
            min_value = value
            iterations_with_no_improvement = 0
            alpha = 0.01
        else:
            iterations_with_no_improvement += 1
            alpha *= 0.9
        
        g = []
        for x_i, y_i in in_random_order(data):
            g.extend([sos_gradient(theta), sos_gradient(x_i), sos_gradient(y_i)])
        for v_grad in g:
            theta = np.around(np.subtract(theta, alpha * np.array(v_grad)), 3)
        
        g = []
    
    print('最小值:', np.around(min_theta, 4), '迭代次數:', i+1)
    print('無改善迭代次數:', iterations_with_no_improvement)
    print('最小向量幅度:', np.linalg.norm(min_theta))
    plt.show()

內容解密:

  1. rndrandom_vectors 函式:生成隨機整數列表,用於初始化引數與生成樣本資料。
  2. sossos_gradient 函式:計算殘差平方和及其梯度。
  3. in_random_order 函式:將輸入資料隨機排序,以實作 SGD 的隨機性。
  4. 主程式邏輯
    • 初始化引數 theta、學習率 alpha 及其他變數。
    • 在每次迭代中,計算當前 theta 的 RSS,並根據 RSS 更新 theta
    • 使用 SGD 更新 theta,並繪製收斂過程圖。

改進的 SGD 方法

在原始的 SGD 方法中,theta 的更新依賴於所有樣本的梯度。改進方法是在所有樣本梯度中選擇幅度最小的梯度進行更新。

程式碼實作:改進的 RSS 最小化

# 省略與前述相同部分
if __name__ == "__main__":
    # 省略初始化部分
    for i in range(n):
        # 省略部分程式碼
        g = []
        t = []
        m = []
        for x_i, y_i in in_random_order(data):
            g.extend([sos_gradient(theta), sos_gradient(x_i), sos_gradient(y_i)])
        m = np.around([np.linalg.norm(x) for x in g], 2)
        for v_grad in g:
            theta_new = np.around(np.subtract(theta, alpha * np.array(v_grad)), 3)
            t.append(theta_new)
        mm = np.argmin(m)
        theta = t[mm]
        # 省略部分程式碼

內容解密:

  1. 改進更新邏輯:在所有樣本梯度中選擇幅度最小的梯度對應的 theta 更新值。
  2. 最佳化效果:透過選擇最優梯度,改進方法的收斂效果更好,且結果更穩定。

最大化 RSS 的 SGD 方法

為了最大化 RSS,只需對梯度取負值,即可將最小化問題轉換為最大化問題。

程式碼實作:RSS 最大化

def negate(function):
    def new_function(*args, **kwargs):
        return np.negative(function(*args, **kwargs))
    return new_function

neg_gradient = negate(sos_gradient)

if __name__ == "__main__":
    # 省略初始化部分
    for i in range(n):
        # 省略部分程式碼
        g = []
        for x_i, y_i in in_random_order(data):
            g.extend([neg_gradient(theta), neg_gradient(x_i), neg_gradient(y_i)])
        for v_grad in g:
            theta = np.around(np.subtract(theta, alpha * np.array(v_grad)), 3)
        # 省略部分程式碼

內容解密:

  1. negate 函式:將輸入函式的輸出取負值,用於將最小化問題轉換為最大化問題。
  2. 最大化邏輯:透過對梯度取負值,實作 RSS 的最大化。