首先我们构建一个 2D 函数方程
$$ f(x,y)=(x^2+y-11)^2+(x+y^2-7)^2 $$
该方程为 Himmelblau 方程,是科学家们研究出来专门用于检测一个优化器效果的方程。该方程所绘制的图像如下由图可见,四个蓝色圆圈即为该方程的极小值点,其平面上的图像如右图所示。该方程虽然有四个极小值点,但四个点对应的值均为 0
- $f(3.0,2.0)=0.0$
- $f(-2.805118,3.131312)=0.0$
- $f(-3.779310,-3.283186)=0.0$
- $f(3.584428,-1.848126)=0.0$
我们可以通过测试优化器能否找到四个极小值点,来判断其优劣
- import torch
- import torch.nn.functional as F
- import numpy as np
- import matplotlib.pyplot as plt
- from mpl_toolkits.mplot3d import Axes3D
-
- def himmelblau(x, y):
- return (x**2 + y - 11)**2 + (x + y**2 - 7)**2
-
- x = np.arange(-6, 6, 0.1) # x轴的范围
- y = np.arange(-6, 6, 0.1) # y轴的范围
- X, Y = np.meshgrid(x, y)
- Z = himmelblau(X, Y)
-
- fig = plt.figure('himmelblau')
- ax = fig.gca(projection='3d')
- ax.plot_surface(X, Y, Z)
- ax.view_init(60, -30)
- ax.set_xlabel('x')
- ax.set_ylabel('y')
- plt.show()
输出图像为
关于 meshgrid()
函数,这篇博客解释的很好,如果不理解的可以看看
下面以梯度下降法为例子来试着找到 Himmelblau 方程的极小值。这里是以优化预测值 pred 为目标,而不是误差 Error
- x = torch.tensor([0., 0.], requires_grad=True) # 设定初始值(0, 0)
- optimizer = torch.optim.Adam([x], lr=1e-3)
- # 优化器对x进行优化,设定学习率为0.001
-
- for step in range(20000):
- pred = himmelblau(x[0], x[1])
- optimizer.zero_grad() # 梯度信息清零
- pred.backward()
- optimizer.step() # 进行一次优化器优化,根据梯度信息更新x[0]和x[1]
-
- if step % 2000 == 0:
- print("step{}: x={}, f(x) = {}".format(step, x.tolist(), pred.item()))
输出为
- step0: x=[0.0009999999310821295, 0.0009999999310821295], f(x) = 170.0
- step2000: x=[2.3331806659698486, 1.9540694952011108], f(x) = 13.730916023254395
- step4000: x=[2.9820079803466797, 2.0270984172821045], f(x) = 0.014858869835734367
- step6000: x=[2.999983549118042, 2.0000221729278564], f(x) = 1.1074007488787174e-08
- step8000: x=[2.9999938011169434, 2.0000083446502686], f(x) = 1.5572823031106964e-09
- step10000: x=[2.999997854232788, 2.000002861022949], f(x) = 1.8189894035458565e-10
- step12000: x=[2.9999992847442627, 2.0000009536743164], f(x) = 1.6370904631912708e-11
- step14000: x=[2.999999761581421, 2.000000238418579], f(x) = 1.8189894035458565e-12
- step16000: x=[3.0, 2.0], f(x) = 0.0
- step18000: x=[3.0, 2.0], f(x) = 0.0
由结果可见,运行到 16000 次后,找到极小值点。若改变初始值点,则可能会改变输出结果,比方说把初始值点由 (0,0) 改为 (4,0)