使用scipy.optimize动态选择要在python中最小化函数的参数

时间:2015-08-11 21:04:15

标签: python scipy

我有一个函数,它将变量列表作为参数,我想使用scipy.optimize.minimize来最小化这个函数。
问题是在运行时决定参数列表中的哪个变量应该进行最小化。所有其他变量都将获得固定值。

让我们举个例子来澄清:

a = 1
c = 1.1
d = -1.2

def func( b ):
    return function_to_minimize( array=[a,b,c,d] )

sol = scipy.optimize.minimize( func, [b0], args=(a,c,d) )

但是,这可能是bcd已知,我想优化a以找到最小解决方案。

为了使它更复杂,列表的长度也是未知的。这意味着可能存在变量efg,......等等。

实际表示法如下。 None元素是应该优化的元素。

array = [1, 1.1, None, -0.5, 4]

def func(arr):
    return function_to_minimize(arr)

startvalue = 1.0
sol = scipy.optimize.minimize( func, [startvalue], args='Array without None' )

有没有办法告诉scipy.optimize.minimize要优化哪个元素?我可以做一个聪明的lambda技巧吗?

我真的很感谢你的帮助!

2 个答案:

答案 0 :(得分:1)

如您所知,要最小化的功能会根据参数而变化 给出。所以我们需要编写一些动态定义函数的代码。 一种方法是定义模板字符串,进行一些字符串格式化 根据给定的参数修改模板,然后使用exec 定义功能。这有一些优先权 - 标准库使用此technique to define namedtuples

因此,例如,如果我们希望最小化的表达式是

4*(b-a)**2 + 5*(c-d)**2

然后你可以使用

import textwrap
import scipy.optimize as optimize

def make_model(*fixed):
    template = textwrap.dedent("""
        def func(variable, {fixed}):
            {variable} = variable
            return 4*(b-a)**2 + 5*(c-d)**2
        """)
    variable = set(('a', 'b', 'c', 'd')).difference(fixed)
    ns = dict()
    funcstr = template.format(variable=', '.join(variable), fixed=', '.join(fixed))
    print(funcstr)  # comment out if you don't want to see the function
    exec funcstr in ns
    return ns['func']

def solve(initial_guess, **givens):
    fixed = tuple(givens.keys())
    vals = tuple(givens.values())
    sol = optimize.minimize(make_model(*fixed), initial_guess, args=vals)
    return sol

print(solve(initial_guess=1, a=1, c=1.1, d=-1.2))

产生

def func(variable, a, c, d):
    b = variable
    return 4*(b-a)**2 + 5*(c-d)**2

   status: 0
  success: True
     njev: 1
     nfev: 3
 hess_inv: array([[1]])
      fun: array([ 26.45])
        x: array([ 1.])
  message: 'Optimization terminated successfully.'
      jac: array([ 0.])
      nit: 0
print(solve(initial_guess=(1, 1), a=1, c=1.1))

产量

def func(variable, a, c):
    b, d = variable
    return 4*(b-a)**2 + 5*(c-d)**2

   status: 0
  success: True
     njev: 3
     nfev: 12
 hess_inv: array([[1, 0],
       [0, 1]])
      fun: 2.4611848645596973e-16
        x: array([ 0.99999999,  1.1       ])
  message: 'Optimization terminated successfully.'
      jac: array([  1.19209279e-08,   2.88966118e-08])
      nit: 1

答案 1 :(得分:0)

我只想使用列表arr作为单个输入,提供有关未知数量变量的unutbu's answer的修改,其中要拟合的参数设置为None

最小化fm的函数是一个虚函数,只是尝试使用numpy.std最小化数组的标准偏差。

它看起来有点笨重而且不是非常pythonesque,但它的确有效。

import textwrap
import scipy.optimize as optimize


def make_model(n,*fixed):
    template = textwrap.dedent("""
        import numpy as np
        def fm(arr):
            return np.std(arr)
        def func(variable, {fixed}):
            {variable} = variable
            return fm(["""+",".join(["a"+str(i) for i in range(n)])+"""])
        """)
    settuple = tuple(['a'+str(i) for i in range(n)])
    variable = set(settuple).difference(fixed)
    ns = dict()
    funcstr = template.format(variable=', '.join(variable), fixed=', '.join(fixed))
    print(funcstr)  # comment out if you don't want to see the function
    exec funcstr in ns
    return ns['func']


def solve(initial_guess, n, **givens):
    fixed = tuple(givens.keys())
    vals = tuple(givens.values())
    sol = optimize.minimize(make_model(n,*fixed), initial_guess, args=vals)
    return sol


arr = [1, 1.1, None, -0.5, 4, 3]

s = ""
for i,a in enumerate(arr):
    if a is not None:
        s+=", a"+str(i)+"="+str(a)

print "print(solve(initial_guess=1, n="+str(len(arr))+s+"))"  # comment out if you don't want to see the function       
exec "print(solve(initial_guess=1, n="+str(len(arr))+s+"))"