使用Eigen来求解密集的约束最小二乘拟合

时间:2017-08-15 04:39:15

标签: c++ optimization eigen least-squares

对于大小为4的向量x,我需要解决形式为Ax = b的经典问题.A大约为500个数据点,因此是一个密集的500x4矩阵。

目前我可以使用here描述的常规方程来解决这个问题,但是它可以正常工作但是我想将x中的一个参数约束为永远不会高于某个值。

有没有一种很好的方法可以用Eigen以编程方式进行此操作?

2 个答案:

答案 0 :(得分:1)

您可以尝试基于Eigen there的四元编程求解器。你仍然需要形成正规方程式。

答案 1 :(得分:-1)

这里有一些 python-demo (使用与Eigen不太远的numpy)显示加速投影梯度算法来解决您的问题。通常,这种方法用于大规模问题(包含二阶信息的其他算法可能会遇到困难),但实施起来也很不错。

这个版本是对我的一些旧代码的一个小修改,并不是我们使用的最简单的方法:

  • 加速度/动量(更快的迭代)
  • 行搜索(节省一些步长调整问题)

您可以删除行搜索并调整步长。也不需要动力。

由于我现在没有做太多的C ++,我不认为我会把它移植到Eigen。但我敢肯定,如果你想要移植它,那并不难。 Eigen不应该与numpy太不同。

我没有测量表现,但结果是立即(感知)计算的。

编辑:一些非科学时间(更多动量;比下面的代码更小的容忍度):

A=(500,4)

Solve non-bounded with scipys lsq_linear
used:  0.004898870004675975
cost :  244.58267993

Solve bounded (0.001, 0.05) with scipys lsq_linear
used:  0.005605718416479959
cost :  246.990611225

Solve bounded (0.001, 0.05) with accelerated projected gradient
early-stopping @ it:  3
used:  0.002282825315435914
cost:  246.990611225

A=(50000, 500)

Solve non-bounded with scipys lsq_linear
used:  4.118898701951786 secs
cost :  24843.7115776

Solve bounded (0.001, 0.05) with scipys lsq_linear
used:  14.727660030288007 secs
cost :  25025.0328661

Solve bounded (0.001, 0.05) with accelerated projected gradient
early-stopping @ it:  14
used:  5.319953458329618 secs
cost:  25025.0330754

基本思想是使用一些类似梯度下降的算法,并在每个梯度步骤之后投影到我们的约束。如果可以有效地完成投影,这种方法非常强大。 Box-constraint-projection很简单!

Page 4 in this pdf显示了框约束投影。

我们只是将解决方案向量剪辑到lower_bound, upper_bound。 numpy中的剪辑描述为:给定间隔,区间外的值被剪切到间隔边缘。例如,如果指定了[0,1]的间隔,则小于0的值将变为0,而大于1的值将变为1.

它是一个近似解决方案的迭代算法,我认为每个使用的算法都是迭代算法。

代码

import numpy as np
from scipy.optimize import lsq_linear
np.random.seed(1)

A = np.random.normal(size=(500, 4))
b = np.random.normal(size=500)

""" Solve Ax=b
    ----------
"""
print('Solve non-bounded with scipys lsq_linear')
sol = lsq_linear(A, b)
print('Ax=b sol: ', sol['x'])
print('cost : ', sol['cost'])
print()

""" Solve Ax=b with box-constraints
    -------------------------------
"""
print('Solve bounded (0.001, 0.05) with scipys lsq_linear')
sol = lsq_linear(A, b, bounds=(0.001, 0.05))
print('Ax=b constrained sol: ', sol['x'])
print('cost : ', sol['cost'])
print()

""" Solve Ax=b with box-constraints using a projected gradient algorithm
    --------------------------------------------------------------------
"""
def solve_pg(A, b, bounds=(-np.inf, np.inf), momentum=0.9, maxiter=1000):
    """ remarks:
            algorithm: accelerated projected gradient
            projection: proj onto box constraints
            line-search: armijo-rule along projection-arc (Bertsekas book)
            stopping-criterion: naive
            gradient-calculation: precomputes AtA
    """

    lb = np.empty(A.shape[1])
    ub = np.empty(A.shape[1])
    if len(bounds) == 2:
        # apply lb & ub to all variables
        lb = bounds[0]
        ub = bounds[1]
    else:
        # assume dimensions are ok
        lb = np.array(bounds[0])
        ub = np.array(bounds[1])

    M, N = A.shape
    x = np.zeros(N)

    AtA = A.T.dot(A)
    Atb = A.T.dot(b)

    stop_count = 0

    def gradient(x):
        return AtA.dot(x) - Atb

    def obj(x):
        return 0.5 * np.linalg.norm(A.dot(x) - b)**2

    it = 0
    while True:
        grad = gradient(x)

        # line search
        alpha = 1
        beta = 0.5
        sigma=1e-2
        old_obj = obj(x)
        while True:
            new_x = x - alpha * grad
            new_obj = obj(new_x)
            if old_obj - new_obj >= sigma * grad.dot(x - new_x):
                break
            else:
                alpha *= beta

        x_old = x[:]
        x = x - alpha*grad

        # projection
        np.clip(x, lb, ub, out=x)  # Projection onto box constraints
                                   # see SO-text
                                   # in-place clipping

        y = x + momentum * (x - x_old)

        if np.abs(old_obj - obj(x)) < 1e-2:
            stop_count += 1
        else:
            stop_count = 0

        if stop_count == 3:
            print('early-stopping @ it: ', it)
            return x

        it += 1

        if it == maxiter:
            return x

print('Solve bounded (0.001, 0.05) with accelerated projected gradient')
sol = solve_pg(A, b, bounds=(0.001, 0.05))
print(sol)
print('cost: ',  0.5 * (np.square(np.linalg.norm(A.dot(sol) - b))))

输出

Solve non-bounded with scipys lsq_linear
Ax=b sol:  [ 0.06627173 -0.06104991 -0.07010355  0.04024075]
cost :  244.58267993

Solve bounded (0.001, 0.05) with scipys lsq_linear
Ax=b constrained sol:  [ 0.05        0.001       0.001       0.03902291]
cost :  246.990611225

Solve bounded (0.001, 0.05) with accelerated projected gradient
early-stopping @ it:  3
[ 0.05        0.001       0.001       0.03902229]
cost:  246.990611225