代码之家  ›  专栏  ›  技术社区  ›  Attack68

每次迭代的收敛结果最小化?

  •  0
  • Attack68  · 技术社区  · 7 年前

    我想使用 scipy.optimize.minimize

    假设我有以下线性约束二次优化问题:

    最小化:x|i Q|ij x|j+a | x|i|

    受制于:总和(x_i)=1

    我可以将其编码为:

    def _fun(x, Q, a):
        c = np.einsum('i,ij,j->', x, Q, x)
        p = np.sum(a * np.abs(x))
        return c + p
    def _constr(x):
        return np.sum(x) - 1
    

    scipy 作为:

    x_0 = # some initial vector
    x_soln = scipy.optimise.minimize(_fun, x_0, args=(Q, a), method='SLSQP', 
                                     constraints={'type': 'eq', 'fun': _constr})
    

    callback 论点 但它在每次迭代中只接受一个参数值参数 . 在一个更为深奥的情况下,我应该如何利用它呢?在这种情况下,我可能需要向回调函数提供其他参数?

    1 回复  |  直到 6 年前
        1
  •  1
  •   Attack68    7 年前

    我解决这个问题的方法是每次从回调函数引用一个通用回调缓存对象。假设你想做20个测试,在同一个图表中绘制每次迭代后的目标函数。您将需要一个外部循环来运行20个测试,但我们稍后会创建它。

    首先让我们创建一个类,它将为我们存储所有迭代目标函数值,以及一些额外的比特和片段:

    class OpObj(object):
        def __init__(self, Q, a):
            self.Q, self.a = Q, a
            rv = np.random.rand()
            self.x_0 = np.array([rv, (1-rv)/2, (1-rv)/2])
            self.f = np.full(shape=(500,), fill_value=np.NaN)
            self.count = 0
        def _fun(self, x):
            return _fun(x, self.Q, self.a)
    

    还可以添加一个回调函数来处理该类obj。现在不要担心它有多个参数,因为我们稍后会解决这个问题。只要确保第一个参数是解变量。

    def cb(xk, obj=None):
        obj.f[obj.count] = obj._fun(xk)
        obj.count += 1
    

    所有这些都是使用对象的函数和值来更新自己,计算每次的迭代次数。此函数将在每次迭代后调用。

    把这些放在一起,我们还需要做两件事:1)一些matplotlib来进行绘图,并将回调修复为只有一个参数。我们可以用一个装饰器来实现这一点,这正是functools partial所做的。它返回的函数的参数比原始函数少。最后的代码是这样的:

    import matplotlib.pyplot as plt
    import scipy.optimize as op
    import numpy as np
    from functools import partial
    
    Q = np.array([[1.0, 0.75, 0.45], [0.75, 1.0, 0.60], [0.45, 0.60, 1.0]])
    a = 1.0
    
    def _fun(x, Q, a):
        c = np.einsum('i,ij,j->', x, Q, x)
        p = np.sum(a * np.abs(x))
        return c + p
    def _constr(x):
        return np.sum(x) - 1
    
    class OpObj(object):
        def __init__(self, Q, a):
            self.Q, self.a = Q, a
            rv = np.random.rand()
            self.x_0 = np.array([rv, (1-rv)/2, (1-rv)/2])
            self.f = np.full(shape=(500,), fill_value=np.NaN)
            self.count = 0
        def _fun(self, x):
            return _fun(x, self.Q, self.a)
    
    def cb(xk, obj=None):
        obj.f[obj.count] = obj._fun(xk)
        obj.count += 1
    
    fig, ax = plt.subplots(1,1)
    x = np.linspace(1,500,500)
    for test in range(20):
        op_obj = OpObj(Q, a)
        x_soln = op.minimize(_fun, op_obj.x_0, args=(Q, a), method='SLSQP',
                             constraints={'type': 'eq', 'fun': _constr},
                             callback=partial(cb, obj=op_obj))
        ax.plot(x, op_obj.f)
    
    ax.set_ylim((1.71,1.76))
    plt.show()
    

    enter image description here