我有4个非线性方程,其中有三个未知数X
,Y
和Z
我想要求解。方程的形式如下:
F(m) = X^2 + a(m)Y^2 + b(m)XYcosZ + c(m)XYsinZ
...其中a
,b
和c
是常量,它们取决于四个等式中F
的每个值。
解决这个问题的最佳方法是什么?
答案 0 :(得分:50)
有两种方法可以做到这一点。
所以,据我了解你的问题,你知道4个不同点的F,a,b和c,你想要反演模型参数X,Y和Z.我们有3个未知数和4个观测数据点,所以问题是超定的。因此,我们将在最小二乘意义上解决。
在这种情况下使用相反的术语更为常见,所以让我们翻转你的方程式。而不是:
F_i = X^2 + a_i Y^2 + b_i X Y cosZ + c_i X Y sinZ
让我们写一下:
F_i = a^2 + X_i b^2 + Y_i a b cos(c) + Z_i a b sin(c)
我们在4个不同点(例如F
)了解X
,Y
,Z
和F_0, F_1, ... F_i
。
我们只是改变变量的名称,而不是方程本身。 (这比我更容易思考。)
实际上可以将这个等式线性化。您可以轻松解决a^2
,b^2
,a b cos(c)
和a b sin(c)
。为了使这更容易,让我们再次重新考虑:
d = a^2
e = b^2
f = a b cos(c)
g = a b sin(c)
现在方程式更简单:F_i = d + e X_i + f Y_i + g Z_i
。对d
,e
,f
和g
进行最小二乘线性反演很容易。然后,我们可以从
a
,b
和c
a = sqrt(d)
b = sqrt(e)
c = arctan(g/f)
好的,让我们以矩阵形式写出来。我们将翻译4个观察结果(我们将编写的代码将采用任意数量的观察结果,但现在让它保持具体):
F_i = d + e X_i + f Y_i + g Z_i
分为:
|F_0| |1, X_0, Y_0, Z_0| |d|
|F_1| = |1, X_1, Y_1, Z_1| * |e|
|F_2| |1, X_2, Y_2, Z_2| |f|
|F_3| |1, X_3, Y_3, Z_3| |g|
或者:F = G * m
(我是地球物理学家,因此我们使用G
表示“格林函数”,m
表示“模型参数”。通常我们使用{{1也可以使用“数据”代替d
。)
在python中,这将转换为:
F
你也可以使用def invert(f, x, y, z):
G = np.vstack([np.ones_like(x), x, y, z]).T
m, _, _, _ = np.linalg.lstsq(G, f)
d, e, f, g = m
a = np.sqrt(d)
b = np.sqrt(e)
c = np.arctan2(g, f) # Note that `c` will be in radians, not degrees
return a, b, c
来解决这个问题,就像@Joe建议的那样。 scipy.optimize
中最易于访问的函数是scipy.optimize
,默认情况下使用Levenberg-Marquardt方法。
Levenberg-Marquardt是一种“爬山”算法(在这种情况下,它会走下坡路,但无论如何都会使用该术语)。从某种意义上说,您可以对模型参数进行初步猜测(默认情况下为scipy.optimize.curve_fit
)并按照参数空间中scipy.optimize
的斜率下坡到底部。
警告:选择正确的非线性反演方法,初步猜测和调整方法的参数是非常“黑暗的艺术”。你只能通过这样做来学习它,并且在很多情况下,事情将无法正常工作。如果你的参数空间相当平滑(这应该是),Levenberg-Marquardt是一个很好的通用方法。除了更常见的方法(如模拟退火)之外,还有很多其他方法(包括遗传算法,神经网络等)在其他情况下更好。我不打算在这里深入研究这一部分。
有一个常见问题是某些优化工具包试图纠正observed - predicted
没有尝试处理的问题。如果您的模型参数具有不同的大小(例如scipy.optimize
),则需要重新调整大小以使它们的大小相似。否则a=1, b=1000, c=1e-8
的“爬山”算法(如LM)将无法准确计算局部梯度的估计值,并且会产生非常不准确的结果。目前,我假设scipy.optimize
,a
和b
具有相对相似的幅度。此外,请注意,基本上所有非线性方法都需要您进行初始猜测,并且对该猜测很敏感。我将它留在下面(只需将其作为c
kwarg传递给p0
),因为默认curve_fit
对于a, b, c = 1, 1, 1
是一个相当准确的猜测。
有了警告,a, b, c = 3, 2, 1
期望传递一个函数,一组观察点(作为单个curve_fit
数组)和观察值。 / p>
所以,如果我们写这样的函数:
ndim x npoints
在将其传递给def func(x, y, z, a, b, c):
f = (a**2
+ x * b**2
+ y * a * b * np.cos(c)
+ z * a * b * np.sin(c))
return f
之前,我们需要将其包装以接受稍微不同的参数。
简而言之:
curve_fit
为了给你一个完整的实现,这是一个
的例子def nonlinear_invert(f, x, y, z):
def wrapped_func(observation_points, a, b, c):
x, y, z = observation_points
return func(x, y, z, a, b, c)
xdata = np.vstack([x, y, z])
model, cov = opt.curve_fit(wrapped_func, xdata, f)
return model
答案 1 :(得分:2)
你可能想要使用scipy的非线性求解器,它们非常简单:http://docs.scipy.org/doc/scipy/reference/optimize.nonlin.html