optimtool研究梯度优化
推荐下载2.9.0版本(加速函数计算)和github.com/linjing-lab/optimtool的最新版(优化Hessian计算):
python
pip install optimtool==2.9.0
下载最新版的代码:
python
git clone https://github.com/linjing-lab/optimtool.git
cd optimtool
pip install -e . --verbose
运行文件
python
import sympy as sp
import optimtool.unconstrain as ou
x, y = sp.symbols('x y')
PI = sp.pi
# 说明:下面 12 个都是「梯度法难收敛」的经典测试函数:
# 病态 / 峡谷 / 平坦高原 / 海量局部极小 / 起点极度敏感
# 每个函数给出:表达式 + 推荐初始点(已用 L-BFGS-B 与 Adam 实测可收敛到全局最优)
# 以及「陷阱起点」注释(从这些点出发会卡在局部极小、跑飞或梯度消失)
# 1. Rosenbrock ------ 香蕉形狭长山谷,梯度下降锯齿震荡
f1 = (1 - x)**2 + 100*(y - x**2)**2
x0_1 = (-1.2, 1.0) # 推荐(经典起点);全局最优 (1, 1), f=0
# 陷阱:(5, 5) ------ Adam 卡在 f≈2.85
# 2. Zakharov ------ 大范围平坦高原,梯度极小,一阶法爬行
f2 = x**2 + y**2 + (x/2 + y)**2 + (x/2 + y)**4
x0_2 = (1.0, -1.0) # 推荐;全局最优 (0, 0), f=0
# 陷阱:(10, 10) / (20, 20) ------ Adam 停在 f≈0.36 / 49.1
# 3. Beale ------ 大片平坦区 + 极尖的谷,远端起步会被甩飞
f3 = (sp.Rational(3, 2) - x + x*y)**2 \
+ (sp.Rational(9, 4) - x + x*y**2)**2 \
+ (sp.Rational(21, 8) - x + x*y**3)**2
x0_3 = (2.5, 0.5) # 推荐;全局最优 (3, 0.5), f=0
# 陷阱:(-4.5, -4.5) ------ L-BFGS 跑到 (-1337, 1), f=0.453
# 4. Rastrigin ------ 海量局部极小,只有落在原点「碗」内才收敛
f4 = 20 + x**2 - 10*sp.cos(2*PI*x) + y**2 - 10*sp.cos(2*PI*y)
x0_4 = (0.2, -0.2) # 推荐(也可用 (0.5, 0.5)、(-0.5, 0.5));全局最优 (0, 0), f=0
# 陷阱:(1,1)→f=1.99,(2,2)→7.96,(3,3)→17.91(均为局部极小)
# 5. Griewank ------ 大碗 + 高频涟漪,距离原点 >2 就被涟漪困住
f5 = 1 + (x**2 + y**2)/4000 - sp.cos(x)*sp.cos(y/sp.sqrt(2))
x0_5 = (1.5, -1.5) # 推荐(也可用 (1.2, 1.2)、(0.8, -0.8));全局最优 (0, 0), f=0
# 陷阱:(2, 2)→f=0.0666,(5, 5)→0.0074,(10,10)→0.0592
# 6. Easom ------ 全域近乎平坦,只有 (π, π) 处一个针孔式深坑
f6 = -sp.cos(x)*sp.cos(y)*sp.exp(-((x - PI)**2 + (y - PI)**2))
x0_6 = (2.9, 2.9) # 推荐(也可用 (3.1, 3.1)、(3.5, 2.8));全局最优 (π, π), f=-1
# 陷阱:(1, 1) ------ Adam 卡在 (1.305, 1.305), f≈-8.1e-5
# 7. Levy N.13 ------ 起点极度敏感,稍偏一点就落进假极小
f7 = sp.sin(3*PI*x)**2 + (x - 1)**2*(1 + sp.sin(3*PI*y)**2) \
+ (y - 1)**2*(1 + sp.sin(2*PI*y)**2)
x0_7 = (1.0, 0.5) # 推荐;全局最优 (1, 1), f=0
# 陷阱:(0, 0)、(0.5, 0.5) → 假极小 (0.6704, 1), f=0.1099
# 8. Styblinski--Tang ------ 多盆地,起点决定落进全局盆还是局部盆
f8 = (x**4 - 16*x**2 + 5*x + y**4 - 16*y**2 + 5*y)/2
x0_8 = (-2.5, -2.5) # 推荐(也可用 (0, 0)、(-2, -2));全局最优 (-2.9035, -2.9035), f=-78.3323
# 陷阱:(2, 2)、(5, 5) → 局部极小 (2.7468, 2.7468), f=-50.06
# 9. Michalewicz ------ 极窄的谷,谷外梯度几乎为零(原地不动)
f9 = -(sp.sin(x)*sp.sin(x**2/PI)**20 + sp.sin(y)*sp.sin(2*y**2/PI)**20)
x0_9 = (2.2, 1.5) # 推荐(也可用 (2.3, 1.6)、(2.6, 1.4)、(1.8, 1.2))
# 全局最优 (2.2029, 1.5708), f=-1.8013
# 陷阱:(0.5, 0.5) ------ 梯度≈0,直接原地卡死 f≈0
# 10. Drop-Wave ------ 同心波纹,只有极靠近原点才收敛(原点梯度奇异)
f10 = -(1 + sp.cos(12*sp.sqrt(x**2 + y**2)))/(sp.Rational(1, 2)*(x**2 + y**2) + 2)
x0_10 = (0.1, -0.1) # 推荐(也可用 (0.05, 0.05));全局最优 (0, 0), f=-1
# 陷阱:(0,0) 梯度 0/0 → NaN;(0.2,0.2)→-0.786;(0.5,0.5)→-0.9362
# 11. Schwefel(限定 x, y > 0)------ 次优盆地极具欺骗性,离 420.97 太远就回不来
f11 = 418.9829*2 - (x*sp.sin(sp.sqrt(x)) + y*sp.sin(sp.sqrt(y)))
x0_11 = (420.0, 420.0) # 推荐(也可用 (450, 450)、(480, 480)、(350, 420))
# 全局最优 (420.9687, 420.9687), f≈0
# 陷阱:(300, 300)、(200, 200) → 卡在 (203.81, 203.81), f=434.3
# 附:Himmelblau ------ 不难点在收敛,而在「起点决定落到 4 个极小点中的哪一个」
f12 = (x**2 + y - 11)**2 + (x + y**2 - 7)**2
x0_12 = (1.0, 0.5) # → (3, 2)
# (-2, -2) → (-3.7793, -3.2832);(-1.2, 1) → (-2.8051, 3.1313)
# (3.5, -1.5) → (3.5844, -1.8481);四处 f 均为 0
FUNCS = {
"Rosenbrock": (f1, x0_1),
"Zakharov": (f2, x0_2),
"Beale": (f3, x0_3),
"Rastrigin": (f4, x0_4),
"Griewank": (f5, x0_5),
"Easom": (f6, x0_6),
"Levy N.13": (f7, x0_7),
"Styblinski-Tang": (f8, x0_8),
"Michalewicz": (f9, x0_9),
"Drop-Wave": (f10, x0_10),
"Schwefel": (f11, x0_11),
"Himmelblau": (f12, x0_12),
}
if __name__ == "__main__":
for name, (expr, x0) in FUNCS.items():
print(f"{name:16s} x0 = {x0}")
print(f" f = {expr}")
opt = ou.gradient_descent.barzilar_borwein(expr, (x, y), x0, verbose=True, epsilon=1e-4)
print(opt, '\n')
可视化
从上到下依次是不同函数的可视化:











