给出数组a
,b
和c
:
import numpy as np
a = np.array([100, 200, 300])
b = np.array([[1, 0, 0],
[1, 0, 1],
[0, 1, 1],
[1, 1, 1]])
c = np.array([150, 300, 500, 650])
我想优化a
,以使每个值都最小化c_prime
中定义的绝对差之和。
c_prime = c - np.sum(a*b, axis=1)
print(c_prime)
print(np.abs(c_prime).sum())
[ 50 -100 0 50]
200
手动...通过更改a
中的第一个元素,c_prime
开始获得所需的结果。
a = np.array([150, 200, 300])
c_prime = c - np.sum(a*b, axis=1)
print(c_prime)
print(np.abs(c_prime).sum())
[ 0 -150 0 0]
150
现在,令人尴尬的是,我如何才能取得理想的结果?
我已经尝试过scipy.optimize.minimize
,但是很明显,这段代码没有标记,该功能在概念上可能完全不正确。
def f(x, b, c):
return np.abs(c - np.sum(x*b, axis=1)).sum()
x0 = a
minimize(f, x0, args=(b,c))
fun: 200.0
hess_inv: array([[1, 0, 0],
[0, 1, 0],
[0, 0, 1]])
jac: array([-1., 0., 1.])
message: 'Desired error not necessarily achieved due to precision loss.'
nfev: 327
nit: 0
njev: 63
status: 2
success: False
x: array([100., 200., 300.])
鉴于从上面手动设置a[0]
到150
所获得的改进结果,为什么这些结果返回非最优的x
?
答案 0 :(得分:3)
这里的问题是您的目标函数不可微。 SciPy默认使用BFGS优化,这要求目标函数的一阶导数存在。
我可以想到3种解决此问题的主要方法:使用无导数优化,对目标函数使用微分逼近或将绝对值转换为约束。
scipy.optimize.minimize
中的几乎所有优化方法都需要可微的目标函数。少数人没有,但是即使如此,也无法保证他们会找到最低要求。
例如,指定method='Nelder-Mead'
会成功完成优化,并且在我的test run中指定x: array([ 149.99998103, 349.99999851, 150.00000599])
的结果,但是正如Paul Panzer在评论中指出的那样,它从x0=[1, 1, 1]
开始结果为convergence to a non-minimum。 Nelder-Mead有时只是这样做。即使目标函数具有微分,它也可以收敛到一个非平稳点。
目标函数的可微近似是容易的,并提供了更好的收敛性,但有一点误差。例如,将np.abs
替换为
def pseudoabs(x):
return (x**2+0.1)**0.5
results in convergence到x: array([ 150.00000001, 350.00000011, 150.00000039])
,使用默认的BFGS求解器。
关于将绝对值转换掉,您的问题几乎是一个标准的线性规划问题,但目标函数中包含绝对值。通过引入额外的变量,it's possible to convert an absolute value into two new linear constraints。想法是在约束|x|
和x'
的约束下,用x' >= x
项替换目标中的x' >= -x
项。
这样做可以让您使用scipy.optimize.linprog
之类的标准线性编程求解器解决问题,如果愿意,可以使用scipy.optimize.minimize
来解决问题。
除此之外,可能还有其他算法可以解决这种形式的优化问题。我尝试按照“最小二乘最优化”的方式谷歌搜索“ l1最优化”,但结果却使最小化解向量的l1范数成为结果,这对这种情况是错误的向量。