Scipy NNLS执行此操作:
Solve argmin_x || Ax - b ||_2 for x>=0.
如果我寻求,可以选择其他方式
严格非零解决方案(即x > 0
)?
这是我使用Scipy的NNLS的LP代码:
import numpy as np
from numpy import array
from scipy.optimize import nnls
def by_nnls(A=None, B=None):
""" Linear programming by NNLS """
#print "NOF row = ", A.shape[0]
A = np.nan_to_num(A)
B = np.nan_to_num(B)
x, rnorm = nnls(A,B)
x = x / x.sum()
# print repr(x)
return x
B1 = array([ 22.133, 197.087, 84.344, 1.466, 3.974, 0.435,
8.291, 45.059, 5.755, 0.519, 0. , 30.272,
24.92 , 10.095])
A1 = array([[ 46.35, 80.58, 48.8 , 80.31, 489.01, 40.98,
29.98, 44.3 , 5882.96],
[ 2540.73, 49.53, 26.78, 30.49, 48.51, 20.88,
19.92, 21.05, 19.39],
[ 2540.73, 49.53, 26.78, 30.49, 48.51, 20.88,
19.92, 21.05, 19.39],
[ 30.95, 1482.24, 100.48, 35.98, 35.1 , 38.65,
31.57, 87.38, 33.39],
[ 30.95, 1482.24, 100.48, 35.98, 35.1 , 38.65,
31.57, 87.38, 33.39],
[ 30.95, 1482.24, 100.48, 35.98, 35.1 , 38.65,
31.57, 87.38, 33.39],
[ 15.99, 223.27, 655.79, 1978.2 , 18.21, 20.51,
19. , 16.19, 15.91],
[ 15.99, 223.27, 655.79, 1978.2 , 18.21, 20.51,
19. , 16.19, 15.91],
[ 16.49, 20.56, 19.08, 18.65, 4568.97, 20.7 ,
17.4 , 17.62, 25.51],
[ 33.84, 26.58, 18.69, 40.88, 19.17, 5247.84,
29.39, 25.55, 18.9 ],
[ 42.66, 83.59, 99.58, 52.11, 46.84, 64.93,
43.8 , 7610.12, 47.13],
[ 42.66, 83.59, 99.58, 52.11, 46.84, 64.93,
43.8 , 7610.12, 47.13],
[ 41.63, 204.32, 4170.37, 86.95, 49.92, 87.15,
51.88, 45.38, 42.89],
[ 81.34, 60.16, 357.92, 43.48, 36.92, 39.13,
1772.07, 68.43, 38.07]])
用法:
In [9]: by_nnls(A=A1,B=B1)
Out[9]:
array([ 0.70089761, 0. , 0.06481495, 0.14325696, 0.01218972,
0. , 0.02125942, 0.01906576, 0.03851557]
注意上面的零解决方案。
答案 0 :(得分:4)
如果您确实确定您需要严格的正面解决方案,则可以使用最新版本中提供的lsq_linear。它允许比nnls
更多地控制边界。
In [37]: from scipy.optimize import lsq_linear
In [38]: lsq_linear(A1, B1, bounds=(0.001, np.inf))
Out[38]:
active_mask: array([ 0, -1, 0, 0, -1, -1, 0, 0, 0])
cost: 3784.3150152135881
fun: array([ -0.06189388, -56.45892624, 56.28407376, 2.97647016,
0.46847016, 4.00747016, 18.24947887, -18.51852113,
0.19599207, 7.32663679, 15.0829264 , -15.1890736 ,
-0.14570891, -0.24341795])
message: 'The first-order optimality measure is less than `tol`.'
nit: 17
optimality: 5.4491449547056092e-11
status: 1
success: True
x: array([ 0.05506904, 0.001 , 0.00501077, 0.01112669, 0.001 ,
0.001 , 0.00154812, 0.00147833, 0.00300156])
答案 1 :(得分:3)
您应该询问确实是否需要x > 0
而不是x >= 0
。通常后一个约束用于稀疏结果,并且x中的零是理想的。除此之外,约束实际上是等同的。
如果约束x严格大于零,则0将变为非常小的正数。如果可以通过更大的值来改善溶液,那么您也可以使用原始约束来获得这些值。
让我们通过定义以下优化来证明这一点:解决argmin_x || Ax - b ||_2 for x>=eps
。虽然eps > 0
这也满足x > 0
。查看不同x
的结果eps
,我们得到:
你看到的是,对于商城eps
,目标函数几乎没有任何差异,x[1]
(原始解决方案中的0之一)越来越接近0。
因此,从x>0
到x>=0
的无穷小步骤几乎不会改变解决方案中的任何内容。出于实际目的,它们完全相似。但是,x>=0
的优势在于您可以获得实际的0而不是1.234e-20,这有助于简化解决方案。
以上是上图的代码:
from scipy.optimize import fmin_cobyla
import matplotlib.pyplot as plt
def by_minimize(A, B, eps=1e-6):
A = np.nan_to_num(A)
B = np.nan_to_num(B)
def objective(x, A=A, B=B):
return np.sum((np.dot(A, x) - B)**2)
x0 = np.zeros(A.shape[1])
x = fmin_cobyla(objective, x0, lambda x: x-eps)
return x / np.sum(x), objective(x)
results = []
obj = []
e = []
for eps in np.logspace(-1, -6, 100):
x, o = by_minimize(A=A1, B=B1, eps=eps)
e.append(eps)
results.append(x[1])
obj.append(o)
h1 = plt.semilogx(e, results, 'b')
plt.ylabel('x[1]', color='b')
plt.xlabel('eps')
plt.twinx()
h2 = plt.semilogx(e, obj, 'r')
plt.ylabel('objective', color='r')
plt.yticks([])
P.S。我尝试使用x > 0
在我的代码中实现lambda x: [1 if i>0 else -1 for i in x]
约束,但它无法收敛。