梯度下降法是一種重要的最佳化演算法,廣泛應用於機器學習模型訓練。本文首先介紹如何使用蒙特卡羅模擬評估梯度下降法的穩定性,藉由多次模擬不同初始條件下的收斂過程,觀察迭代次數的變化,進而分析演算法的穩健性。接著,我們探討如何利用 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)))
內容解密:
step函式:根據給定的方向和步長更新向量,用於梯度下降法的迭代更新。sigmoid_gradient函式:計算 sigmoid 函式的梯度,用於梯度下降法的梯度計算。mod_vector函式:修正向量中的無窮大值,避免在計算過程中出現無窮大值導致的錯誤。- 主程式:進行多次實驗,每次實驗包含多次模擬。每次模擬隨機初始化一個向量,並使用梯度下降法進行最佳化,直到收斂。統計每次實驗的平均迭代次數,並計算最大與最小平均迭代次數的差距。
使用 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)
內容解密:
np.linalg.norm函式:使用 NumPy 的linalg.norm函式計算向量的範數(norm),這裡用於計算歐幾裡得距離。- 效能改進:使用 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()
內容解密:
rnd與random_vectors函式:生成隨機整數列表,用於初始化引數與生成樣本資料。sos與sos_gradient函式:計算殘差平方和及其梯度。in_random_order函式:將輸入資料隨機排序,以實作 SGD 的隨機性。- 主程式邏輯:
- 初始化引數
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]
# 省略部分程式碼
內容解密:
- 改進更新邏輯:在所有樣本梯度中選擇幅度最小的梯度對應的
theta更新值。 - 最佳化效果:透過選擇最優梯度,改進方法的收斂效果更好,且結果更穩定。
最大化 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)
# 省略部分程式碼
內容解密:
negate函式:將輸入函式的輸出取負值,用於將最小化問題轉換為最大化問題。- 最大化邏輯:透過對梯度取負值,實作 RSS 的最大化。