scipy.optimize.minimize 的精度
Precision of scipy.optimize.minimize
我正在尝试计算函数的最小点
f(x)=(x-2e-17)*(x-2e-17)
与 scipy.optimize.minimize
。
预期的 精确 结果是 2e-17
。但是无论我如何微调 scipy.optimize.minimize
的公差参数 xtol
和 ftol
,它仍然只给出 不精确 结果 0
(见下文) 。怎样才能让scipy
return精确回答呢?谢谢。
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
从输出中可以看到found point的function-value是4.0000000000000006e-34,比你的ftol=1e-30小很多。
尝试向下推 ftol,例如到 1e-37。这应该可以解决问题。
或者,您可以尝试缩放函数,例如尝试使用函数 1e+34 * (x-2e-17)**2
而不是 (x-2e-17)**2
。两个函数的最小值在同一点。
我了解您的技术问题,但我认为这是由于优化器使用不当造成的。在回答您提出的问题之前,我会沉迷于一些哲学漫谈。
"Typical" 优化问题 "with useful answers" 的最优函数值在 1 的几个数量级(即大大少于 17 个数量级)内,在坐标都在几个数量级内的点处获得幅度为 1。(或者最优值为零,或者一些最优坐标为零。但在这种情况下,用户通常仍然对非常小的 objective 值和坐标感到满意。)
通常,提供给黑盒优化器的 objective 函数(及其梯度,也提供给一些黑盒优化器)并没有特别仔细地编写。在优化器附近,f 的计算梯度将由舍入误差决定。梯度甚至可能偏离最佳点。如果黑盒优化器永远循环采用长度为 0 的步长,或者当它非常接近最优值时因错误而爆炸,那么黑盒优化器的用处就会大打折扣,因此参数名称如 "ftol" 和 "gtol"相当宽松的默认值,例如 1e-4
.
即使在理想情况下,用户提供的函数总是 returns 在 x
最接近 f(x)
的浮点数,而另一个函数总是 returns 在 x
到 f
在 x
的正确舍入梯度,试图找到 最小化 f
的 浮点向量] 是一个非常丑陋的离散优化问题。 (NP-hard,如果有记忆的话。)如果 f
是一个以正确舍入的方式计算的良好缩放的二次方程——关于我能想象的最好的非平凡案例——丑陋的离散行为开始压倒当您开始在 1e-8
.
左右进行长度步长时,良好的连续行为
基于线搜索的方法会发现它们自己计算了 f(x + td)
的所有 t
的最小值,用于某些点 x
和某些方向 d
。考虑浮点运算中的 f(x + td)
是什么;对于某些 t
,你以某种方式计算 x+td
,最多得到最接近 x+td
的浮点向量,然后将其插入 f
。通常,此线搜索将沿着锯齿线评估 f
,通过 x
,在方向 d
上蜿蜒。即使 f
行为良好且实施良好,线搜索也可以在一定范围内发现非常糟糕的行为。因此,名称为 xtol
的参数表示何时停止行搜索。
很多方法——除了直接的牛顿法之外,几乎所有我能想到的方法——都需要对你的问题的合理比例进行某种猜测才能开始。 (BFGS 通常将单位矩阵作为初始猜测。我认为 L-BFGS 的第一步采用单位步长。梯度下降方法通常首先尝试梯度的固定倍数。信任区域方法使用信任区域,必须开始有一些半径。如果你正在进行数值微分,你的步长需要足够大,以便你捕获函数的 "continuous" 行为而不是 "discrete" 行为,但也足够小,你'捕捉它的精细行为,而不是接近你的意思的粗暴行为。)
在这里,您正在优化一个函数,其最优值为零,非常接近于零。从理论上讲,我上面所说的关于问题是可怕的及其子问题是可怕的没有任何内容需要应用。但是你真的希望求解器对最优值为零的函数有一个特殊情况,非常接近于零吗?特别是当这是(可能)降低鲁棒性的额外代码时?为什么不直接给求解器提供一个规模适当的问题呢?
为了回答您的直接问题,鲍威尔在 scipy 中的方法调用布伦特线搜索,从坐标方向开始。 scipy 中实现的布伦特线搜索可以提高您通过添加剂 1e-11
提供的任何公差。如果你破解 scipy.optimize 使得 Brent
的 _mintol
变成 1e-111
,我敢打赌你会得到想要的答案。 (_mintol
是 x
中的绝对容差,它被添加到您指定的相对容差中。它在那里是为了使线搜索不会浪费函数评估来决定是按 1e-200
还是按1e-201
当任何一种情况都可能导致根本没有任何步骤时。所以实际上不要那样做。)
尝试更改使用的 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]
这些类型的精度问题似乎与用于最小化的方法密切相关。
我正在尝试计算函数的最小点
f(x)=(x-2e-17)*(x-2e-17)
与 scipy.optimize.minimize
。
预期的 精确 结果是 2e-17
。但是无论我如何微调 scipy.optimize.minimize
的公差参数 xtol
和 ftol
,它仍然只给出 不精确 结果 0
(见下文) 。怎样才能让scipy
return精确回答呢?谢谢。
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
从输出中可以看到found point的function-value是4.0000000000000006e-34,比你的ftol=1e-30小很多。
尝试向下推 ftol,例如到 1e-37。这应该可以解决问题。
或者,您可以尝试缩放函数,例如尝试使用函数 1e+34 * (x-2e-17)**2
而不是 (x-2e-17)**2
。两个函数的最小值在同一点。
我了解您的技术问题,但我认为这是由于优化器使用不当造成的。在回答您提出的问题之前,我会沉迷于一些哲学漫谈。
"Typical" 优化问题 "with useful answers" 的最优函数值在 1 的几个数量级(即大大少于 17 个数量级)内,在坐标都在几个数量级内的点处获得幅度为 1。(或者最优值为零,或者一些最优坐标为零。但在这种情况下,用户通常仍然对非常小的 objective 值和坐标感到满意。)
通常,提供给黑盒优化器的 objective 函数(及其梯度,也提供给一些黑盒优化器)并没有特别仔细地编写。在优化器附近,f 的计算梯度将由舍入误差决定。梯度甚至可能偏离最佳点。如果黑盒优化器永远循环采用长度为 0 的步长,或者当它非常接近最优值时因错误而爆炸,那么黑盒优化器的用处就会大打折扣,因此参数名称如 "ftol" 和 "gtol"相当宽松的默认值,例如 1e-4
.
即使在理想情况下,用户提供的函数总是 returns 在 x
最接近 f(x)
的浮点数,而另一个函数总是 returns 在 x
到 f
在 x
的正确舍入梯度,试图找到 最小化 f
的 浮点向量] 是一个非常丑陋的离散优化问题。 (NP-hard,如果有记忆的话。)如果 f
是一个以正确舍入的方式计算的良好缩放的二次方程——关于我能想象的最好的非平凡案例——丑陋的离散行为开始压倒当您开始在 1e-8
.
基于线搜索的方法会发现它们自己计算了 f(x + td)
的所有 t
的最小值,用于某些点 x
和某些方向 d
。考虑浮点运算中的 f(x + td)
是什么;对于某些 t
,你以某种方式计算 x+td
,最多得到最接近 x+td
的浮点向量,然后将其插入 f
。通常,此线搜索将沿着锯齿线评估 f
,通过 x
,在方向 d
上蜿蜒。即使 f
行为良好且实施良好,线搜索也可以在一定范围内发现非常糟糕的行为。因此,名称为 xtol
的参数表示何时停止行搜索。
很多方法——除了直接的牛顿法之外,几乎所有我能想到的方法——都需要对你的问题的合理比例进行某种猜测才能开始。 (BFGS 通常将单位矩阵作为初始猜测。我认为 L-BFGS 的第一步采用单位步长。梯度下降方法通常首先尝试梯度的固定倍数。信任区域方法使用信任区域,必须开始有一些半径。如果你正在进行数值微分,你的步长需要足够大,以便你捕获函数的 "continuous" 行为而不是 "discrete" 行为,但也足够小,你'捕捉它的精细行为,而不是接近你的意思的粗暴行为。)
在这里,您正在优化一个函数,其最优值为零,非常接近于零。从理论上讲,我上面所说的关于问题是可怕的及其子问题是可怕的没有任何内容需要应用。但是你真的希望求解器对最优值为零的函数有一个特殊情况,非常接近于零吗?特别是当这是(可能)降低鲁棒性的额外代码时?为什么不直接给求解器提供一个规模适当的问题呢?
为了回答您的直接问题,鲍威尔在 scipy 中的方法调用布伦特线搜索,从坐标方向开始。 scipy 中实现的布伦特线搜索可以提高您通过添加剂 1e-11
提供的任何公差。如果你破解 scipy.optimize 使得 Brent
的 _mintol
变成 1e-111
,我敢打赌你会得到想要的答案。 (_mintol
是 x
中的绝对容差,它被添加到您指定的相对容差中。它在那里是为了使线搜索不会浪费函数评估来决定是按 1e-200
还是按1e-201
当任何一种情况都可能导致根本没有任何步骤时。所以实际上不要那样做。)
尝试更改使用的 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]
这些类型的精度问题似乎与用于最小化的方法密切相关。