scipy中的整数步长优化最小化

时间:2012-08-29 14:56:05

标签: python optimization numpy machine-learning scipy

我想要使用scipy.optimize.minimize调整计算机视觉算法。现在我只想调整两个参数,但参数的数量可能最终增长所以我想使用一种可以进行高维渐变搜索的技术。 SciPy中的Nelder-Mead实现似乎非常合适。

我得到了所有设置的代码,但似乎最小化函数确实想要使用步长小于1的浮点值。当前参数集都是整数,其中一个步长为1另一个步长为2(即该值必须为奇数,如果不是我想要优化的东西将其转换为奇数)。大致一个参数是以像素为单位的窗口大小,另一个参数是阈值(从0到255的值)。

对于它的价值,我正在使用git repo中的scipy。有谁知道如何告诉scipy为每个参数使用特定的步长?有什么方法可以滚动我自己的渐变功能吗?是否有可以帮助我的scipy旗帜?我知道这可以通过简单的参数扫描完成,但我最终希望将此代码应用于更大的参数集。

代码本身很简单:

import numpy as np
from scipy.optimize import minimize
from ScannerUtil import straightenImg 
import bson

def doSingleIteration(parameters):
    # do some machine vision magic
    # return the difference between my value and the truth value

parameters = np.array([11,10])
res = minimize( doSingleIteration, parameters, method='Nelder-Mead',options={'xtol': 1e-2, 'disp': True,'ftol':1.0,}) #not sure if these params do anything
print "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
print res

这就是我的输出的样子。正如你所看到的那样,我们正在重复大量的运行并且没有在最小化中获得任何结果。

*+++++++++++++++++++++++++++++++++++++++++
[ 11.  10.]  <-- Output from scipy minimize
{'block_size': 11, 'degree': 10} <-- input to my algorithm rounded and made int
+++++++++++++++++++++++++++++++++++++++++
120  <-- output of the function I am trying to minimize
+++++++++++++++++++++++++++++++++++++++++
[ 11.55  10.  ]
{'block_size': 11, 'degree': 10}
+++++++++++++++++++++++++++++++++++++++++
120
+++++++++++++++++++++++++++++++++++++++++
[ 11.   10.5]
{'block_size': 11, 'degree': 10}
+++++++++++++++++++++++++++++++++++++++++
120
+++++++++++++++++++++++++++++++++++++++++
[ 11.55   9.5 ]
{'block_size': 11, 'degree': 9}
+++++++++++++++++++++++++++++++++++++++++
120
+++++++++++++++++++++++++++++++++++++++++
[ 11.1375  10.25  ]
{'block_size': 11, 'degree': 10}
+++++++++++++++++++++++++++++++++++++++++
120
+++++++++++++++++++++++++++++++++++++++++
[ 11.275  10.   ]
{'block_size': 11, 'degree': 10}
+++++++++++++++++++++++++++++++++++++++++
120
+++++++++++++++++++++++++++++++++++++++++
[ 11.    10.25]
{'block_size': 11, 'degree': 10}
+++++++++++++++++++++++++++++++++++++++++
120
+++++++++++++++++++++++++++++++++++++++++
[ 11.275   9.75 ]
{'block_size': 11, 'degree': 9}
+++++++++++++++++++++++++++++++++++++++++
120
+++++++++++++++++++++++++++++++++++++++++
~~~
SNIP 
~~~
+++++++++++++++++++++++++++++++++++++++++
[ 11.         10.0078125]
{'block_size': 11, 'degree': 10}
+++++++++++++++++++++++++++++++++++++++++
120
Optimization terminated successfully.
         Current function value: 120.000000
         Iterations: 7
         Function evaluations: 27
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  status: 0
    nfev: 27
 success: True
     fun: 120.0
       x: array([ 11.,  10.])
 message: 'Optimization terminated successfully.'
     nit: 7*

5 个答案:

答案 0 :(得分:6)

假设最小化的函数是任意复杂的(非线性),这通常是一个非常难的问题。除非您尝试所有可能的选项,否则无法保证最佳解决方案。我知道是否有任何整数约束非线性优化器(有点怀疑)我会假设你知道如果它是一个连续的函数,Nelder-Mead应该可以正常工作。

编辑:考虑来自@Dougal的评论我将在这里添加:首先设置一个粗略+精细网格搜索,如果你觉得如果你的Nelder-Mead工作(并且收敛得更快),下面的几点可能会有所帮助...

但也许有些观点有帮助:

  1. 考虑整个约束是如何非常困难的,也许可以选择进行一些简单的插值来帮助优化器。它仍然应该收敛到整数解。当然,这需要计算额外的积分,但它可能解决许多其他问题。 (即使在线性整数规划中,它常见于解决无约束系统的第一个AFAIK)
  2. Nelder-Mead以N + 1分开始,这些在scipy(至少是旧版本)中连接到(1+0.05) * x0[j](所有维度为j,除非x0[j]为0 ),您将在第一个评估步骤中看到。也许这些可以在更新的版本中提供,否则你可以只更改/复制scipy代码(它是纯python)并将其设置为更合理的东西。或者,如果您觉得更简单,请缩小所有输入变量,使(1 + 0.05)* x0具有合理的大小。
  3. 也许你应该缓存所有的功能评估,因为如果你使用Nelder-Mead,我猜你总是可以进行重复评估(至少在最后)。
  4. 您必须检查Nelder-Mead缩小到单个值并放弃的可能性,因为它总能找到相同的结果。
  5. 你通常必须检查你的功能是否表现良好......如果函数在参数空间上没有平滑变化,那么这种优化就注定了,即使这样,如果你应该有这些功能,它也很容易遇到局部最小值。 (因为您缓存了所有评估 - 请参阅2. - 您至少可以绘制这些评估并查看错误情况,而无需进行任何额外的评估)。

答案 1 :(得分:2)

不幸的是,Scipy的内置优化工具并不容易实现这一点。但永远不要害怕;听起来你有一个凸出的问题,所以你应该能够找到一个独特的最优,即使它在数学上不是很漂亮。

我针对不同问题实施的两个选项是创建自定义梯度下降算法,并对一系列单变量问题使用二分法。如果您在调整中进行交叉验证,那么您的损失函数很遗憾不会是平滑的(因为来自不同数据集的交叉验证产生的噪声),但通常是凸的。

要以数字方式实现梯度下降(没有用于评估梯度的分析方法),请在所有维度中选择距离测试点delta的测试点和第二个点。在这两点评估您的损失函数可以让您以数字方式计算本地子梯度。重要的是delta足够大,以至于它超出交叉验证噪声所产生的局部最小值。

较慢但可能更强大的替代方法是对您要测试的每个参数实施二分法。如果你知道两个参数(或 n 参数)中联合凸的问题,你可以将它分成 n 单变量优化问题,并写一个递归的二分算法关注最佳参数。这可以帮助处理某些类型的拟凸性(例如,如果你的损失函数对其部分域采用背景噪声值,并且在另一个域中是凸的),但需要对初始迭代的边界进行很好的猜测。

如果您只是将请求的x值捕捉到整数网格而不修复xtol以映射到该网格化,则可能会让解算器请求网格单元格中的两个点,并接收相同的输出值并得出结论认为它是最低限度的。

不幸的是,没有简单的答案。

答案 2 :(得分:1)

将你的浮点数x,y(a.k.a. winsize,threshold)捕捉到你的函数中的整数网格 ,如下所示:

def func( x, y ):
    x = round( x )
    y = round( (y - 1) / 2 ) * 2 + 1  # 1 3 5 ...
    ...

然后Nelder-Mead将仅在网格上看到函数值,并且应该给出接近整数x,y。

(如果您想在某个地方发布代码,我正在寻找Nelder-Mead的测试用例 重启。)

答案 3 :(得分:1)

Nelder-Mead最小化方法现在允许您指定初始单纯形顶点,因此您应该能够将单纯形点设置得相距很远,然后单面形状将会翻转并找到最小值并在单形尺寸下降时收敛低于1.

https://docs.scipy.org/doc/scipy/reference/optimize.minimize-neldermead.html#optimize-minimize-neldermead

答案 4 :(得分:0)

问题是算法在试图缩小其 (N+1) 单纯形时卡住了。 我强烈建议任何对这个概念不熟悉的人学习更多关于 simplex 的地理形状并弄清楚输入参数如何与单纯形上的点相关联。一旦你掌握了这一点,那么作为 I.P. Freeley 建议可以通过为单纯形定义强初始点来解决该问题,请注意,这与定义 x0 不同并进入 nelder-mead's dedicated options。这是更高 --4-- 维问题的示例。另请注意,在这种情况下为 5 和在您的情况下为 3,初始单纯形必须具有 N+1 个点。

init_simplex = np.array([[1, .1, .3, .3], [.1, 1, .3, .3], [.1, .1, 5, .3],
                         [.1, .1, .3, 5], [1, 1, 5, 5]])
minimum = minimize(Optimize.simplex_objective, x0=np.array([.01, .01, .01, .01]),
                   method='Nelder-Mead',
                   options={'adaptive': True, 'xatol': 0.1, 'fatol': .00001,
                            'initial_simplex': init_simplex})

在本例中,initial_simplex 的定义忽略了 x0。高维问题中的其他有用选项是“自适应”选项,它在尝试设置模型操作系数(即分别用于反射、扩展、收缩和收缩的 α、γ、ρ 和 σ)时考虑参数的数量.如果您还没有,我还建议您熟悉 algorithm 的步骤。

现在至于发生这个问题的原因是因为该方法在扩展中没有得到好的结果,因此它不断缩小单纯形越来越小,试图找出可能存在或可能不存在的更好解决方案。