我正在尝试计算最小功能点
f(x)=(x-2e-17)*(x-2e-17)
scipy.optimize.minimize
。
预期的精确结果为2e-17
。但无论我如何微调xtol
的容忍度参数ftol
和scipy.optimize.minimize
,它仍然只会给出不精确结果0
(见下文) )。我们如何让scipy
返回精确的答案?谢谢。
In [35]: scipy.optimize.minimize(lambda x: (x-2e-17)**2,2,method='Powell',options={'xtol': 1e-30, 'ftol': 1e-30})
Out[35]:
status: 0
success: True
direc: array([[ 1.]])
nfev: 20
fun: array(4.0000000000000006e-34)
x: array(0.0)
message: 'Optimization terminated successfully.'
nit: 2
答案 0 :(得分:7)
我理解您的技术问题,但在我看来,这源于对优化程序的不恰当使用。在回答你提出的问题之前,我会沉迷于一些哲学的谣言。
具有有用答案的“典型”优化问题“在坐标均在1的几个数量级内的点处获得的几个(即,基本上少于17个)数量级的最佳函数值为1。 (或者最佳值为零,或者一些最佳坐标为零。但在这种情况下,用户通常仍然对非常小的客观值和坐标感到满意。)
通常,用于黑盒优化器的目标函数(以及它们的渐变,也用于某些黑盒优化器)并没有特别仔细地编写。在优化器附近,f的计算梯度将由舍入误差支配。梯度甚至可以指向最佳点。如果一个黑盒优化器永远循环使用长度为0的步骤,或者当它非常接近最佳值时出现错误,那么黑盒优化器就没那么有用了,因此参数的名称如“ftol”和“gtol”具有相当宽松的默认值例如1e-4
。
即使在理想的情况下,用户提供的函数始终返回x
最近的浮点数到f(x)
,另一个函数总是在x
正确返回f
处于x
的渐变渐变,试图找到最小化f
的浮点矢量是一个非常难看的离散优化问题。 (NP-hard,如果内存服务的话。)如果f
是一个以正确的方式评估的大规模二次方 - 关于我能想象的最好的非平凡情况 - 丑陋的离散行为开始压倒性的当你开始在1e-8
周围采取长度步长时,这是一种很好的连续行为。
基于线搜索的方法会发现自己计算的某个点t
和某个方向f(x + td)
的所有x
d
的最小值。考虑f(x + td)
在浮点运算中的含义;对于某些t
,您以某种方式计算x+td
,最好使浮点向量最接近x+td
,然后将其插入f
。一般来说,此行搜索将沿着f
沿x
方向蜿蜒的锯齿线评估d
。即使f
表现良好并且执行得很好,行搜索也可以很好地发现非常糟糕的行为。因此,名称如xtol
的参数表示何时停止线搜索。
很多方法---除了牛顿的方法之外,几乎所有我能想到的东西---需要对你的问题的合理范围进行某种猜测才能启动。 (BFGS通常将单位矩阵作为初始猜测。我认为L-BFGS的第一步采取单位步骤。梯度下降方法通常首先尝试梯度的固定倍数。信任区域方法使用信任区域,必须启动如果你正在进行数值微分,你的步长需要足够大,以至于你捕捉到“连续”行为而不是函数的“离散”行为,但是足够小以至于你正在捕获它良好的行为,而不是在你的观点附近的粗略行为。)
在这里,您正在优化一个最佳值为零的函数,该函数非常接近于零。从理论上讲,我上面所说的任何关于问题都很糟糕而且他们的子问题很糟糕的事情都需要适用。但是你真的希望求解器有一个特殊情况,用于最优值为零,达到非常接近零的函数吗?特别是当(可能)降低稳健性的额外代码时?为什么不直接给解算器提供一个很好的问题呢?
要回答你的直接问题,鲍威尔的scipy方法会调用布伦特的线搜索,从坐标方向开始。布伦特的线搜索,如在scipy中实现的那样,通过添加剂1e-11
来提高你提供的任何容差。如果你破解scipy.optimize以便Brent
的{{1}}代替_mintol
,我打赌你得到了理想的答案。 (1e-111
是_mintol
中的绝对容差,它被添加到您指定的相对容差中。它就在那里,因此行搜索不会浪费函数评估来决定是逐步x
还是1e-200
当任何一种情况都可能导致根本没有步骤时。所以实际上不要这样做。)
答案 1 :(得分:2)
从输出中,您可以看到找到的点的函数值是4.0000000000000006e-34,这比您的ftol = 1e-30小得多。
尝试按下ftol,例如到1e-37。这应该可以解决问题。
或者,您可以尝试缩放功能,例如:而不是(x-2e-17)**2
,请尝试使用函数1e+34 * (x-2e-17)**2
。这两个函数在同一点上有最小值。
答案 2 :(得分:0)
尝试更改使用的method
,例如使用“ Nelder-Mead”:
res = scipy.optimize.minimize(lambda x: (x-2e-17)**2,2,method='Nelder-Mead',options={'xtol': 1e-30, 'ftol': 1e-30})
print(res.x)
打印所需的结果:[2.e-17]
这些类型的精度问题似乎与最小化方法密切相关。