一、概述
这个Python程序实现了经典的梯度下降算法和随机梯度下降算法,并在两个不同的优化问题上进行了比较实验:Rosenbrock函数和强凸二次函数。
二、功能模块
1、测试函数定义
Rosenbrock函数
- 函数:
rosenbrock(w)
- 描述: 经典的非凸优化测试函数,也称为"香蕉函数"
- 数学表达式: f(x,y) = (1-x)² + 100(y-x²)²
- 最优解: (1, 1)
- 特点: 具有狭长的弯曲谷地,是测试优化算法性能的经典函数
强凸二次函数
- 函数:
quadratic(w, a=10)
- 描述: 强凸二次函数,具有良好的优化性质
- 数学表达式: f(x,y) = 0.5(ax² + y²)
- 最优解: (0, 0)
- 参数: a > 0,控制x方向的曲率
2、梯度计算
grad_rosenbrock(w)
: Rosenbrock函数的梯度grad_quadratic(w, a=10)
: 二次函数的梯度
3、优化算法
标准梯度下降 (Gradient Descent)
函数: gradient_descent(f, grad_f, w0, max_iter=1000, tol=1e-6, alpha=1e-3, use_line_search=False, **kwargs)
参数说明:
f
: 目标函数grad_f
: 梯度函数w0
: 初始点max_iter
: 最大迭代次数tol
: 收敛容差alpha
: 学习率(固定步长)use_line_search
: 是否使用自适应步长
特点:
- 确定性算法,每次使用精确梯度
- 支持固定步长和自适应步长两种模式
随机梯度下降 (Stochastic Gradient Descent)
函数: stochastic_gradient_descent(f, grad_f, w0, max_iter=1000, step=1e-3, add_noise=0.0, mult_noise=0.0, a=10)
参数说明:
add_noise
: 加性噪声强度mult_noise
: 乘性噪声强度- 其他参数与梯度下降相同
噪声模型:
- 加性噪声: g += add_noise * 𝒩(0,I)
- 乘性噪声: g = (1 + mult_noise 𝒩(0,I))
自适应步长搜索
函数: simple_line_search(f, grad_f, w, d, alpha0=1.0, c=1e-4, tau=0.5)
实现了基于Armijo准则的回溯线搜索:
alpha0
: 初始步长c
: Armijo参数tau
: 步长收缩因子
4、可视化功能
等高线图与优化路径
函数: plot_level_sets_and_path(f, hist, title, xmin=-2, xmax=2, ymin=-1, ymax=3, levels=30)
- 绘制函数的等高线图
- 显示优化算法的迭代路径
- 标记起始点和终点
损失曲线对比
函数: plot_loss_curve(f, *paths, labels)
- 绘制不同算法的收敛曲线
- 使用对数坐标显示损失值变化
二、实验设置
1、参数配置
w0 = np.array([-1.5, 2.0]) # 初始点
max_iter = 5000 # 最大迭代次数
tol = 1e-8 # 收敛容差
alpha = 1e-3 # 固定步长
a_quad = 10 # 二次函数参数
2、实验内容
Rosenbrock函数上的梯度下降
- 固定步长梯度下降
- 带线搜索的梯度下降
强凸二次函数上的梯度下降
- 固定步长梯度下降
随机梯度下降实验
- Rosenbrock函数 + 加性噪声
- 二次函数 + 加性噪声
- 二次函数 + 乘性噪声
算法性能对比
- 收敛曲线对比
- 迭代步数统计
三、使用方法
直接运行:
python SGD.py
自定义实验:
# 创建自定义优化实验 hist = gradient_descent(your_function, your_gradient, initial_point) plot_level_sets_and_path(your_function, hist, "Your Experiment")
四、输出结果
程序会生成以下可视化结果:
- Rosenbrock函数上的优化路径图(固定步长 vs 自适应步长)
- 二次函数上的优化路径图
- SGD在不同噪声设置下的优化路径图
- 不同算法的损失收敛曲线对比图
五、依赖库
numpy
: 数值计算matplotlib
: 绘图可视化
六、算法特点分析
1、梯度下降 (GD)
- 优点: 收敛稳定,理论保证强
- 缺点: 可能陷入局部最优,收敛速度慢
2、随机梯度下降 (SGD)
- 优点: 噪声有助于跳出局部最优,适合大规模问题
- 缺点: 收敛路径不稳定,可能在最优解附近震荡
3、自适应步长
- 优点: 自动调节步长,避免步长选择问题
- 缺点: 每次迭代计算开销更大
七、注意事项
- 不同函数需要不同的学习率设置
- 噪声强度需要根据具体问题调节
- Rosenbrock函数比二次函数更难优化
- 实际应用中建议结合多种策略(动量、自适应学习率等)
import numpy as np
import matplotlib.pyplot as plt
# 1. 测试函数和梯度的定义
def rosenbrock(w):
""" Rosenbrock function """
x, y = w
return (1 - x)**2 + 100 * (y - x**2)**2
def grad_rosenbrock(w):
""" Gradient of Rosenbrock function """
x, y = w
dfdx = -2 * (1 - x) - 400 * x * (y - x**2)
dfdy = 200 * (y - x**2)
return np.array([dfdx, dfdy])
def quadratic(w, a=10):
""" Strongly convex quadratic function. Minimum at [0,0]. Parameter a>0. """
x, y = w
return 0.5 * (a * x**2 + y**2)
def grad_quadratic(w, a=10):
x, y = w
return np.array([a * x, y])
# 2. Adaptive line search (Armijo rule as a simple example)
def simple_line_search(f, grad_f, w, d, alpha0=1.0, c=1e-4, tau=0.5):
""" Simple backtracking Armijo line search """
alpha = alpha0
f0 = f(w)
grad0 = grad_f(w)
while f(w + alpha * d) > f0 + c * alpha * np.dot(grad0, d):
alpha *= tau
if alpha < 1e-10:
break
return alpha
# 3. Gradient Descent (可选自适应步长,step_func为自适应步长搜索)
def gradient_descent(f, grad_f, w0, max_iter=1000, tol=1e-6, alpha=1e-3, use_line_search=False, **kwargs):
w = np.array(w0, dtype=float)
hist = [w.copy()]
for i in range(max_iter):
g = grad_f(w)
if np.linalg.norm(g) < tol:
break
if use_line_search:
# Adaptive line search direction is always -gradient
step = simple_line_search(f, grad_f, w, -g)
else:
step = alpha
w -= step * g
hist.append(w.copy())
return np.array(hist)
# 4. Stochastic Gradient Descent
def stochastic_gradient_descent(f, grad_f, w0, max_iter=1000, step=1e-3,
add_noise=0.0, mult_noise=0.0, a=10):
w = np.array(w0, dtype=float)
hist = [w.copy()]
for i in range(max_iter):
g = grad_f(w) if grad_f != grad_quadratic else grad_f(w, a)
# Add noise to gradient
g += add_noise * np.random.randn(*g.shape)
g *= (1.0 + mult_noise * np.random.randn(*g.shape))
w -= step * g
hist.append(w.copy())
return np.array(hist)
# 5. 绘图辅助函数
def plot_level_sets_and_path(f, hist, title, xmin=-2, xmax=2, ymin=-1, ymax=3, levels=30):
x = np.linspace(xmin, xmax, 400)
y = np.linspace(ymin, ymax, 400)
X, Y = np.meshgrid(x, y)
Z = np.array([[f([i, j]) for i, j in zip(row_x, row_y)]
for row_x, row_y in zip(X, Y)])
plt.figure(figsize=(7, 5))
plt.contour(X, Y, Z, levels=levels, cmap='viridis')
plt.plot(hist[:, 0], hist[:, 1], 'r.-', label='Path')
plt.scatter(hist[0,0], hist[0,1], marker='o', color='blue', label='Start')
plt.scatter(hist[-1,0], hist[-1,1], marker='x', color='black', label='End')
plt.title(title)
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.show()
# ===================== #
# ====== 实验区 ====== #
# ===================== #
if __name__ == '__main__':
# 参数设置
w0 = np.array([-1.5, 2.0]) # 初始点
max_iter = 5000 # 最大迭代
tol = 1e-8 # 收敛容忍
alpha = 1e-3 # 固定步长
a_quad = 10 # 二次函数参数a
# ---- Rosenbrock: 梯度下降与自适应步长
hist_gd_rosen = gradient_descent(rosenbrock, grad_rosenbrock, w0, max_iter, tol, alpha, use_line_search=False)
hist_gd_rosen_ls = gradient_descent(rosenbrock, grad_rosenbrock, w0, max_iter, tol, alpha, use_line_search=True)
plot_level_sets_and_path(rosenbrock, hist_gd_rosen, 'Gradient Descent on Rosenbrock')
plot_level_sets_and_path(rosenbrock, hist_gd_rosen_ls, 'Gradient Descent (+Line Search) on Rosenbrock')
# ---- Convex Quadratic: 梯度下降与自适应步长
hist_gd_quad = gradient_descent(lambda w: quadratic(w, a=a_quad),
lambda w: grad_quadratic(w, a=a_quad),
w0, max_iter, tol, alpha, use_line_search=False)
plot_level_sets_and_path(lambda w: quadratic(w, a=a_quad), hist_gd_quad, 'Gradient Descent on Convex Quadratic')
# ---- SGD: Rosenbrock, 随机扰动
hist_sgd_rosen = stochastic_gradient_descent(rosenbrock, grad_rosenbrock, w0, max_iter=2000, step=1e-4,
add_noise=0.1, mult_noise=0.0)
plot_level_sets_and_path(rosenbrock, hist_sgd_rosen, 'SGD (Additive noise) on Rosenbrock')
# ---- SGD: Convex Quadratic,加性和乘性噪声实验
hist_sgd_add = stochastic_gradient_descent(quadratic, grad_quadratic, w0, max_iter=2000, step=5e-3,
add_noise=1.0, mult_noise=0.0, a=a_quad)
hist_sgd_mult = stochastic_gradient_descent(quadratic, grad_quadratic, w0, max_iter=2000, step=5e-3,
add_noise=0.0, mult_noise=0.5, a=a_quad)
plot_level_sets_and_path(lambda w: quadratic(w, a=a_quad), hist_sgd_add, 'SGD (Add noise) Quadratic')
plot_level_sets_and_path(lambda w: quadratic(w, a=a_quad), hist_sgd_mult, 'SGD (Mult noise) Quadratic')
# 对比不同实验
# 收敛曲线对比
def plot_loss_curve(f, *paths, labels):
plt.figure()
for path, label in zip(paths, labels):
loss = [f(w) for w in path]
plt.plot(loss, label=label)
plt.yscale('log')
plt.xlabel('iteration')
plt.ylabel('f(w)')
plt.legend()
plt.title("Loss curve")
plt.show()
plot_loss_curve(lambda w: quadratic(w, a=a_quad),
hist_gd_quad, hist_sgd_add, hist_sgd_mult,
labels=["GD", "SGD (add noise)", "SGD (mult noise)"])
print("Rosenbrock GD steps:", hist_gd_rosen.shape[0])
print("Quadratic GD steps:", hist_gd_quad.shape[0])
print("SGD (add noise) final point:", hist_sgd_add[-1])
网站地址:https://blog.6uu.us/
头像图片url:https://images.6uu.us/20250511114301488.JPG
描述:科技激荡人文,洞见智慧本真。
站点截图:https://img.z4a.net/images/2025/02/16/2025-02-16-14.08.18.png
我的博客换域名了,刚备案下来,请将墨冢这个改一下,感谢。
名称:异数
链接:https://www.yishu.pro/
描述:笔落惊风雨,诗成泣鬼神。
头像:https://www.yishu.pro/img/logo.jpg链接已加好:https://www.yishu.pro/index.php/links.html
对了,博客之前的友链,现已更名。
原名:春花秋月
新名:我的飛鳥集
麻烦有空更改呢~