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

Dynamically choose argument for which to minimize a function in python using scipy.optimize

我有一个将变量列表作为参数的函数,我想使用 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 技巧?

非常感谢您的帮助!

如您所知,要最小化的函数会根据参数的不同而变化 给出。所以我们需要编写一些动态定义函数的代码。 一种方法是定义一个模板字符串,进行一些字符串格式化以 根据给定的参数修改模板,然后使用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

我只是想使用列表 arr 作为单个输入提供关于未知数量变量的 的改编,其中要拟合的参数设置为 None

最小化 fm 的函数是一个虚拟函数,只是尝试使用 numpy.std.

来最小化数组的标准差

它看起来有点笨重而且不太符合 Python 风格,但它确实有效。

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+"))"