3つの未知数X
、Y
、およびZ
を含む4つの非線形方程式を解きます。方程式の形式は次のとおりです。
F(m) = X^2 + a(m)Y^2 + b(m)XYcosZ + c(m)XYsinZ
...ここで、a
、b
、およびc
は、4つの方程式のF
の各値に依存する定数です。
これを解決する最善の方法は何ですか?
これを行うには2つの方法があります。
したがって、私はあなたの質問を理解しているので、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
_(私は地球物理学者なので、「Green's Functions」にはG
を使用し、「Model Parameters」にはm
を使用します。通常はd
の代わりにF
の代わりに「データ」も使用します。)
Pythonでは、これは次のように変換されます。
_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
_で最もアクセスしやすい関数は_scipy.optimize.curve_fit
_で、デフォルトでLevenberg-Marquardtメソッドを使用します。
Levenberg-Marquardtは「登山」アルゴリズムです(この場合は下り坂になりますが、この用語はとにかく使用されます)。ある意味では、モデルパラメーターの初期推測(すべて1、デフォルトでは_scipy.optimize
_)を行い、パラメータースペースの_observed - predicted
_の勾配に従って下に下ります。
警告:適切な非線形反転法の選択、初期推測、および方法のパラメーターの調整は、非常に「暗い芸術」です。あなたはそれをすることによってそれを学ぶだけであり、物事が適切に機能しない多くの状況があります。 Levenberg-Marquardtは、パラメータ空間がかなり滑らかな場合(これがそうでなければなりません)、一般的な方法として適しています。他にも、シミュレーテッドアニーリングなどのより一般的な方法に加えて、遺伝的アルゴリズム、ニューラルネットなど、他の状況で優れているものがたくさんあります。ここではその部分を掘り下げるつもりはありません。
_scipy.optimize
_が処理しようとしていない最適化ツールキットを修正しようとする一般的な落とし穴が1つあります。モデルパラメーターの大きさが異なる場合(例、_a=1, b=1000, c=1e-8
_)、大きさが類似するように物事を再スケーリングする必要があります。そうしないと、_scipy.optimize
_の「山登り」アルゴリズム(LMなど)が局所勾配の推定値を正確に計算せず、結果が大きく不正確になります。今のところ、a
、b
、およびc
の大きさが比較的似ていると仮定しています。また、本質的にすべての非線形手法では、初期推測を行う必要があり、その推測に敏感であることに注意してください。デフォルトの_p0
_は_curve_fit
_のかなり正確な推測であるため、以下では省略します(_a, b, c = 1, 1, 1
_ kwargとして_a, b, c = 3, 2, 1
_として渡します)。
邪魔にならないように、_curve_fit
_は、関数、観測が行われたポイントのセット(単一の_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
_
完全な実装を提供するために、以下に例を示します
_import numpy as np
import scipy.optimize as opt
def main():
nobservations = 4
a, b, c = 3.0, 2.0, 1.0
f, x, y, z = generate_data(nobservations, a, b, c)
print 'Linear results (should be {}, {}, {}):'.format(a, b, c)
print linear_invert(f, x, y, z)
print 'Non-linear results (should be {}, {}, {}):'.format(a, b, c)
print nonlinear_invert(f, x, y, z)
def generate_data(nobservations, a, b, c, noise_level=0.01):
x, y, z = np.random.random((3, nobservations))
noise = noise_level * np.random.normal(0, noise_level, nobservations)
f = func(x, y, z, a, b, c) + noise
return f, x, y, z
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
def linear_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
def nonlinear_invert(f, x, y, z):
# "curve_fit" expects the function to take a slightly different form...
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
main()
_
おそらくscipyの非線形ソルバーを使用したいでしょう、それらは本当に簡単です: http://docs.scipy.org/doc/scipy/reference/optimize.nonlin.html