web-dev-qa-db-ja.com

python境界のある最適化でグローバル最小値を見つける方法は?

私は64変数のPython関数を持っており、最小化関数でL-BFGS-Bメソッドを使用して最適化しようとしましたが、このメソッドは初期推定に非常に強く依存しています。グローバル最小値を見つけることができませんでした。

しかし、変数の境界を設定する機能が気に入りました。変数の境界を持ちながら、グローバル最小値を見つける方法/関数はありますか?

14
dilyar

これはscipy.optimize.basinhoppingで実行できます。 Basinhoppingは、目的関数のglobal最小値を見つけるように設計された関数です。関数scipy.optimize.minimizeを使用して最小化を繰り返し、各最小化の後に座標空間でランダムなステップを実行します。 Basinhoppingは、境界を実装する最小化子の1つ(L-BFGS-Bなど)を使用することで、境界を尊重できます。これを行う方法を示すいくつかのコードがあります

# an example function with multiple minima
def f(x): return x.dot(x) + sin(np.linalg.norm(x) * np.pi)

# the starting point
x0 = [10., 10.]

# the bounds
xmin = [1., 1.]
xmax = [11., 11.]

# rewrite the bounds in the way required by L-BFGS-B
bounds = [(low, high) for low, high in Zip(xmin, xmax)]

# use method L-BFGS-B because the problem is smooth and bounded
minimizer_kwargs = dict(method="L-BFGS-B", bounds=bounds)
res = basinhopping(f, x0, minimizer_kwargs=minimizer_kwargs)
print res

上記のコードは単純なケースでは機能しますが、ベイスンホッピングのランダム変位ルーチンがそこに移動する場合は、禁止された領域に到達する可能性があります。幸いなことに、キーワードtake_stepを使用してカスタムステップ実行ルーチンを渡すことでオーバーライドできます。

class RandomDisplacementBounds(object):
    """random displacement with bounds"""
    def __init__(self, xmin, xmax, stepsize=0.5):
        self.xmin = xmin
        self.xmax = xmax
        self.stepsize = stepsize

    def __call__(self, x):
        """take a random step but ensure the new position is within the bounds"""
        while True:
            # this could be done in a much more clever way, but it will work for example purposes
            xnew = x + np.random.uniform(-self.stepsize, self.stepsize, np.shape(x))
            if np.all(xnew < self.xmax) and np.all(xnew > self.xmin):
                break
        return xnew

# define the new step taking routine and pass it to basinhopping
take_step = RandomDisplacementBounds(xmin, xmax)
result = basinhopping(f, x0, niter=100, minimizer_kwargs=minimizer_kwargs,
                      take_step=take_step)
print result
29
Jacob Stevenson

詳細な返信に感謝しますが、Pythonはかなり新しいので、プログラムにコードを実装する方法がよくわかりませんでしたが、最適化の試みは次のとおりです。

x0=np.array((10, 13, f*2.5, 0.08,    10, f*1.5,  0.06, 20, 
             10, 14, f*2.5, 0.08,    10, f*1.75, 0.07, 20,
             10, 15, f*2.5, 0.08,    10, f*2,    0.08, 20,
             10, 16, f*2.5, 0.08,    10, f*2.25, 0.09, 20,
             10, 17, f*2.5, -0.08,    10, f*2.5, -0.06, 20,
             10, 18, f*2.5, -0.08,    10, f*2.75,-0.07, 20,
             10, 19, f*2.5, -0.08,    10, f*3,   -0.08, 20,
             10, 20, f*2.5, -0.08,    10, f*3.25,-0.09, 20))

# boundary for each variable, each element in this restricts the corresponding element     above
bnds=((1,12), (1,35), (0,f*6.75), (-0.1, 0.1),(1,35), (0,f*6.75), (-0.1, 0.1),(13, 35), 
  (1,12), (1,35), (0,f*6.75), (-0.1, 0.1),(1,35), (0,f*6.75), (-0.1, 0.1),(13, 35), 
  (1,12), (1,35), (0,f*6.75), (-0.1, 0.1),(1,35), (0,f*6.75), (-0.1, 0.1),(13, 35), 
  (1,12), (1,35), (0,f*6.75), (-0.1, 0.1),(1,35), (0,f*6.75), (-0.1, 0.1),(13, 35), 
  (1,12), (1,35), (0,f*6.75), (-0.1, 0.1),(1,35), (0,f*6.75), (-0.1, 0.1),(13, 35), 
  (1,12), (1,35), (0,f*6.75), (-0.1, 0.1),(1,35), (0,f*6.75), (-0.1, 0.1),(13, 35), 
  (1,12), (1,35), (0,f*6.75), (-0.1, 0.1),(1,35), (0,f*6.75), (-0.1, 0.1),(13, 35), 
  (1,12), (1,35), (0,f*6.75), (-0.1, 0.1),(1,35), (0,f*6.75), (-0.1, 0.1),(13, 35), )

from scipy.optimize import basinhopping
from scipy.optimize import minimize

merit=a*meritoflength + b*meritofROC + c*meritofproximity +d*(distancetoceiling+distancetofloor)+e*heightorder
minimizer_kwargs = {"method": "L-BFGS-B", "bounds": bnds, "tol":1e0}
ret = basinhopping(merit_function, x0, minimizer_kwargs=minimizer_kwargs, niter=10, T=0.01)

zoom = ret['x']
res = minimize(merit_function, zoom, method = 'L-BFGS-B', bounds=bnds, tol=1e-5)
print res

メリット関数は、x0を他のいくつかの値と組み合わせて、8つの曲線の6つの制御点を形成し、それらの長さ、曲率半径などを計算します。これらのパラメーターといくつかの重みの線形結合として最終的なメリットを返します。

精度の低いbasinhoppingを使用して最小値を見つけ、次にminimizeを使用して最小の最小値の精度を上げました。

p.s.私が実行しているプラ​​ットフォームは、Enthoghtキャノピー1.3.0、numpy 1.8.0 scipy 0.13.2 mac10.8.3です。

1
dilyar