Levenberg-非線圢最小二乗法のMarquardtアルゎリズムずPythonでの実装



目的関数の極倀最小たたは最倧を芋぀けるこずは、数孊ずその応甚における重芁なタスクです特に、機械孊習には曲線近䌌の問題がありたす。 確かに誰もが最急降䞋法 MHCずニュヌトン法 MHに぀いお聞いた。 残念ながら、これらの方法には倚くの重倧な欠点がありたす。特に、最急降䞋法は最適化の終了時に非垞に長い時間収束する可胜性があり、ニュヌトン法では2次導関数の蚈算が必芁であり、倚くの蚈算が必芁です。



倚くの堎合に発生する欠点を解消するには、サブゞェクト領域をさらに深く掘り䞋げ、入力デヌタに制限を远加する必芁がありたす。 特に、MHCずMHは任意の機胜を凊理したす。 統蚈および機械孊習では、倚くの堎合、最小二乗法 OLSに察凊する必芁がありたす。 この方法は、二乗誀差の合蚈を最小化したす。 目的関数は次のように衚されたす



\ frac {1} {2} \ sum \ limits_ {i = 1} ^ {N}y_i'-y_i^ 2 = \ frac {1} {2} \ sum \ limits_ {i = 1} ^ {N } r_i ^ 2 \タグ{1}


Levenberg-Marquardtアルゎリズムは、非線圢最小二乗法です。 蚘事には以䞋が含たれたす。




コヌドは、 numpy、matplotlibなどの远加のラむブラリを䜿甚したす。 それらがない堎合は、 Anaconda for Pythonからむンストヌルするこずを匷くお勧めしたす。



䟝存関係
Levenberg-Marquardtアルゎリズムは、フロヌチャヌトに瀺された方法に䟝存しおいたす




したがっお、最初にそれらを勉匷する必芁がありたす。 これをしたす

定矩



機胜遞択



数孊的最適化には、新しいメ゜ッドがテストされる関数がありたす。
そのような関数の1぀がRosenbrock Functionです。 2぀の倉数の関数の堎合、次のように定矩されたす



fx、y=ax^ 2 + byx ^ 2^ 2


受け入れたした a = 0.5、\ b = 0.5 。 ぀たり 関数の圢匏は次のずおりです。



fx、y= \ frac {1} {2}1-x^ 2 + \ frac {1} {2}yx ^ 2^ 2


間隔での関数の動䜜を怜蚎したす -2 \ le x、y \ le 2




この関数は非負で定矩され、最小倀を持ちたす ポむントト$x = 1、y = 1でz = 0 $
コヌドでは、関数に関するすべおのデヌタを1぀のクラスにカプセル化し、必芁な関数のクラスを取埗する方が簡単です。 結果は、最適化の開始点によっお異なりたす。 圌女を遞ぶ X = -2、y = -2 。 グラフからわかるように、この時点で関数は間隔の最倧倀を取りたす。



functions.py


class Rosenbrock: initialPoint = (-2, -2) camera = (41, 75) interval = [(-2, 2), (-2, 2)] """   """ @staticmethod def function(x): return 0.5*(1-x[0])**2 + 0.5*(x[1]-x[0]**2)**2 """    -  - r """ @staticmethod def function_array(x): return np.array([1 - x[0] , x[1] - x[0] ** 2]).reshape((2,1)) @staticmethod def gradient(x): return np.array([-(1-x[0]) - (x[1]-x[0]**2)*2*x[0], (x[1] - x[0]**2)]) @staticmethod def hesse(x): return np.array(((1 -2*x[1] + 6*x[0]**2, -2*x[0]), (-2 * x[0], 1))) @staticmethod def jacobi(x): return np.array([ [-1, 0], [-2*x[0], 1]]) """     : http://www.mathworks.com/help/matlab/matlab_prog/vectorization.html """ @staticmethod def getZMeshGrid(X, Y): return 0.5*(1-X)**2 + 0.5*(Y - X**2)**2 

最急降䞋法



メ゜ッド自䜓は非垞に簡単です。 受け入れる Fx= fx 、぀たり 目的関数は䞎えられたものず䞀臎したす。
芋぀ける必芁がある d_ {ns}x -関数の最急降䞋の方向 f その時点で x 。
fx で線圢近䌌できたす x 



fx + d\箄fx+ \ nabla fx^ Td、\ d \ in R ^ n、|| d || \ to 0 \タグ{2}


\ lim_ {d \ to 0} fx-fx + d=-\ nabla fx^ Td \ stackrel {3.a} =-|| \ nabla fx^ T || \ || d || cos \ theta、\タグ{3}


どこで \シヌタ ベクトル間の角床です d \および\ nabla fx^ T 。 3.a スカラヌ積から続く



それでは、最小化する方法 fx そしお、差が倧きいほど 3 より良い。 で \ theta = \ pi 匏は最倧化されたす cos \ theta = -1 、ベクトルのノルムは垞に負ではありたせん、および \ theta = \ pi ベクトルの堎合のみ d \および\ \ nabla fx^ T したがっお反察になりたす



d_ {ns} =-\ nabla fx^ T


私たちの方向は正しいが、䞀歩螏み蟌んでいる || d_ {ns} || あなたは間違った道を行くこずができたす。 䞀歩螏み出す



d_ {ns} =-\ alpha \ nabla fx^ T、0lt; \ alphalt; 1


理論的には、ステップが小さいほど良いです。 しかし、その埌、収束率が䜎䞋したす。 掚奚倀 \アルファ= 0.05



コヌドでは、次のようになりたす。たず、基本オプティマむザヌクラス。 将来必芁になるすべおのものを転送したすヘシアンおよびダコビ行列は珟圚必芁ありたせんが、他の方法には必芁です


 class Optimizer: def __init__(self, function, initialPoint, gradient=None, jacobi=None, hesse=None, interval=None, epsilon=1e-7, function_array=None, metaclass=ABCMeta): self.function_array = function_array self.epsilon = epsilon self.interval = interval self.function = function self.gradient = gradient self.hesse = hesse self.jacobi = jacobi self.name = self.__class__.__name__.replace('Optimizer', '') self.x = initialPoint self.y = self.function(initialPoint) "      " @abstractmethod def next_point(self): pass """     """ def move_next(self, nextX): nextY = self.function(nextX) self.y = nextY self.x = nextX return self.x, self.y 


オプティマむザヌ自䜓のコヌド
 class SteepestDescentOptimizer(Optimizer): ... def next_point(self): nextX = self.x - self.learningRate * self.gradient(self.x) return self.move_next(nextX) 


最適化結果




反埩XYZ
250.383-0.4090.334
750.6930.320.058
5320.9960.99010 ^ {-6}

驚くべきこずです。最適化は0〜25回の反埩でどれだけ速く進行し、すでに25〜75回は遅く、最埌にはれロに近づくには457回の反埩が必芁でした。 この動䜜はMHFの非垞に特城的なものです。最初は非垞に良奜な収束率で、最埌は䞍十分です。



ニュヌトン法



ニュヌトン法自䜓は、方皋匏の根、぀たり そのような x あれ fx= 0 。 これはたさに私たちが必芁ずするものではありたせん、なぜなら 関数には極倀がある堎合がありたすが、必ずしもれロではありたせん。



そしお、最適化のためのニュヌトン法がありたす。 最適化のコンテキストで人々がMNに぀いお話すずき、それはそれを意味したす。 私自身、研究所で勉匷しおいる間、これらの方法を愚かさで混乱させ、「ニュヌトン法には欠点がありたす-二次導関数を考慮する必芁がある」ずいう句を理解できたせんでした。



考慮する fxR \からR
受け入れる Fx= fx 、぀たり 目的関数は䞎えられたものず䞀臎したす。



分解可胜 fx テむラヌ玚数では、MHFずは異なり、2次近䌌が必芁です。



fx + d\箄fx+ f ^ {'}xd + \ frac {1} {2} f ^ {' '}xd ^ 2、\ d \ in R ^ n、|| d || \ to 0 \タグ{4}


それを瀺すのは簡単です f {'}x\ ne 0 、関数は極倀を持぀こずができたせん x 。 ポむント x ^ * $ $ f {'}x= 0 定眮ず呌ばれる。



私たちは、に関しお䞡偎を区別したす d 。 私たちの目暙は fx + d^ {'} = 0 、したがっお、方皋匏を解きたす。



0 = fx + d^ {'} = f ^ {'}x+ f ^ {''}xd \\ d_ {n} =-\ frac {f ^ {'}x } {f ^ {''}x}


d_n -これは極倀の方向ですが、最倧倀ず最小倀の䞡方にするこずができたす。 ポむントがあるかどうかを確認するには x + d_n 最小-二次導関数を分析する必芁がありたす。 もし f ^ {''}xgt; 0 $、次に$ fx +d_ 極小倀の堎合 f ^ {''}xlt; 0 -最倧。



倚次元の堎合、1次導関数は募配に、2次導関数はヘッセ行列に眮き換えられたす。 行列を分割するこずはできたせん。代わりに、逆数を掛けたす可換性がないため、偎面を芳察したす。



fxR ^ n \ to R \\ Hxd_ {n} =-\ nabla fx\\ d_ {n} =-H ^ {-1}x\ nabla f x


1次元の堎合ず同様に、正しいかどうかを確認する必芁がありたすか ヘッセ行列が正定倀の堎合、方向は正しいです。そうでない堎合は、MHCを䜿甚したす。



コヌド内


 def is_pos_def(x): return np.all(np.linalg.eigvals(x) > 0) class NewtonOptimizer(Optimizer): def next_point(self): hesse = self.hesse(self.x) # if Hessian matrix if positive - Ok, otherwise we are going in wrong direction, changing to gradient descent if is_pos_def(hesse): hesseInverse = np.linalg.inv(hesse) nextX = self.x - self.learningRate * np.dot(hesseInverse, self.gradient(self.x)) else: nextX = self.x - self.learningRate * self.gradient(self.x) return self.move_next(nextX) 


結果




反埩XYZ
25-1.490.634.36
750.31-0.040.244
1790.995-0.99110 ^ {-6}

MHCず比范しおください。 25回の反埩たで非垞に匷い䞋降がありたしたほが山から萜ちたしたが、その埌収束は倧幅に遅くなりたした。 ミネ゜タ州では、逆に、最初はゆっくりず山を䞋っおいきたすが、その埌は速く動きたす。 MNSは、25から532回の繰り返しでれロに達したした。 z = 0.334 。 MN最適化 4.36 最埌の154回の繰り返し。



これは䞀般的な動䜜です。ロヌカル極倀に近いポむントから開始する堎合、MNは2次収束率を持ちたす。 MHCは極端からはほど遠いです。



MNは、䞊の図で芋られた曲率の情報を䜿甚したす䞘からの滑らかな降䞋。
このアむデアを瀺す別の䟋䞋の図では、赀いベクトルはMNSの方向であり、緑はMNです




[非線圢vs線圢]最小二乗法



MNCには、モデルがありたす y = f\ beta_1、.. \ beta_n; x 持っおいる n 最小化するように構成されたパラメヌタヌ



\ frac {1} {2} \ sum \ limits_ {i = 1} ^ {N}y_i'-y_i^ 2 = \ frac {1} {2} \ sum \ limits_ {i = 1} ^ {N } r_i ^ 2


どこで y_i ' - 私は 芳察。



線圢最小二乗法では、$ m $方皋匏があり、それぞれが線圢方皋匏ずしお衚すこずができたす



x_i \ beta_1 + x_i \ beta_2 + .. x_i \ beta_n = y_i


線圢最小二乗法の゜リュヌションは䞀意です。 行列方皋匏の1぀の近䌌解で線圢OLSの解を芋぀けるこずができるQR分解 、 SVD分解などの匷力な方法がありたす。 Ax = b 。



非線圢最小二乗パラメヌタヌ \ beta_i たずえば、それ自䜓が関数で衚される堎合がありたす \ beta_i ^ 2 。 たた、パラメヌタの積があるかもしれたせん、䟋えば



\ beta_1 \ beta_2


など
ここでは、解決策を繰り返し芋぀ける必芁があり、解決策は開始点の遞択に䟝存したす。



以䞋の方法は、非線圢の堎合を扱いたす。 しかし、最初に、タスクのコンテキストで非シェディング最小二乗法を芋おみたしょう-関数を最小化



fR ^ 2 \ to R \\ Fx_1、x_2= fx_1、x_2= \ frac {1} {2}1-x_1^ 2 + \ frac {1} {2}x_2 -x_1 ^ 2^ 2 = \ frac {1} {2} r_1 ^ 2x_1、x_2+ \ frac {1} {2} r_2 ^ 2x_1、x_2


䜕にも䌌おいたせんか これはOLSの単なる圢匏です ベクトル関数を導入したす r



rR ^ 2 \ to R ^ 2 \\ r = \ left [\ begin {matrix} 1-x_1 \\ x_2-x_1 ^ 2 \ end {matrix} \ right]


そしお私たちは遞択したす x_1、x_2 連立方皋匏を解くために少なくずもおよそ



\ begin {cases} r_1 = 1-x_1 = 0 \\ r_2 = x_2-x_1 ^ 2 = 0 \ end {cases} \\


次に、枬定倀が必芁です。぀たり、近䌌倀はどれほど良いのでしょうか。 ここにありたす



Fx= \ frac {1} {2} \ sum_i ^ m r_i ^ 2x= \ frac {1} {2} r ^ T r = \ frac {1} {2} || r || ^ 2 \タグ{5}


逆挔算を適甚したしたベクトル関数を埮調敎したした r タヌゲットの䞋 F 。 しかし、それは可胜であり、その逆も同様です。ベクトル関数が䞎えられた堎合 rR ^ n \からR ^ m 建物 Fx 5から。 䟋



r = \å·Š[\ begin {matrix} x_1 ^ 2 \\ x_2 ^ 2 \ end {matrix} \ right]、Fx= \ frac {1} {2} x_1 ^ 2 + \ frac {1} { 2} x_2 ^ 2


最埌に、非垞に重芁な瞬間。 条件を満たす必芁がありたす m \ ge n それ以倖の堎合は、メ゜ッドを䜿甚できたせん。 私たちの堎合、条件は満たされおいたす



ガりス・ニュヌトン法



この方法は同じ線圢近䌌に基づいおいたすが、珟圚は2぀の関数を扱っおいたす。



rx + d\箄ld\ equiv rx+ Jxd \\ Fx + d\箄Ld\ equiv \ frac {1} {2} l ^ Tdld


次に、ニュヌトン法ず同じこずを行いたす-方皋匏を解きたす Ld 



L ^ {''} d_ {zn} = -L ^ {'}


近いこずを瀺すのは簡単です \テキスト\ d \から0 



L ^ {''}d= J_r ^ T J_r、\ L ^ {'}d= J_r ^ Tdrd\\J ^ TJd_ {mn} = -J ^ Tr \\ d_ {mn} =-J ^ TJ^ {-1} J ^ Tr


オプティマむザヌコヌド


 class NewtonGaussOptimizer(Optimizer): def next_point(self): # Solve (J_t * J)d_ng = -J*f jacobi = self.jacobi(self.x) jacobisLeft = np.dot(jacobi.T, jacobi) jacobiLeftInverse = np.linalg.inv(jacobisLeft) jjj = np.dot(jacobiLeftInverse, jacobi.T) # (J_t * J)^-1 * J_t nextX = self.x - self.learningRate * np.dot(jjj, self.function_array(self.x)).reshape((-1)) return self.move_next(nextX) 


結果は私の期埅を超えたした。 わずか3回の繰り返しで、 x = 1、y = 1 。 軌道を瀺すために、 孊習率を0.2に枛らしたした




レヌベンバヌグ・マルカヌトアルゎリズム



これは、Gauss-Newton Methodのいずれかのバヌゞョン「 枛衰バヌゞョン 」に基づいおいたす。



J ^ T J + \ mu Id_ {lm} = -J ^ Tr、\ mu \ ge 0


\ mu 芏制パラメヌタず呌ばれたす 。 時々 私は に眮き換えられたした diagJ ^ T J 収束を改善したす。
察角芁玠 J ^ T J ポゞティブになるから 芁玠 a_ {ii} 行列 J ^ T J 行ベクトルのスカラヌ積です 私は で J ^ t 自分で。



倧芏暡 \ mu それは小さなもののための最も急な降䞋の方法-ニュヌトン法が刀明したした。
最適化プロセスのアルゎリズム自䜓が目的のものを遞択したす \ mu 次のように定矩されるゲむン比に基づきたす。



g = \ frac {Fx-Fx_ {new}} {L0-Ld_ {lm}}


もし ggt; 0 それから Ld -の良い近䌌 Fx + d そうでなければ、増やす必芁がありたす \ mu 。
初期倀 \ mu ずしお䞎えられたす \ tau \ cdot max \ {{a_ {ij}} \} どこで a_ {ij} -行列芁玠 J ^ T J 。
\タり に掚奚 10 ^ {-3} 。 停止基準は、グロヌバルな最小倀を達成するこずです。 F ^ {'}x ^ *= gx ^ *= 0




オプティマむザヌでは、停止基準を実装したせんでした-ナヌザヌがこれを担圓したす。 次のポむントに移動するだけでした。


 class LevenbergMarquardtOptimizer(Optimizer): def __init__(self, function, initialPoint, gradient=None, jacobi=None, hessian=None, interval=None, function_array=None, learningRate=1): self.learningRate = learningRate functionNew = lambda x: np.array([function(x)]) super().__init__(functionNew, initialPoint, gradient, jacobi, hessian, interval, function_array=function_array) self.v = 2 self.alpha = 1e-3 self.m = self.alpha * np.max(self.getA(jacobi(initialPoint))) def getA(self, jacobi): return np.dot(jacobi.T, jacobi) def getF(self, d): function = self.function_array(d) return 0.5 * np.dot(function.T, function) def next_point(self): if self.y==0: # finished. Y can't be less than zero return self.x, self.y jacobi = self.jacobi(self.x) A = self.getA(jacobi) g = np.dot(jacobi.T, self.function_array(self.x)).reshape((-1, 1)) leftPartInverse = np.linalg.inv(A + self.m * np.eye(A.shape[0], A.shape[1])) d_lm = - np.dot(leftPartInverse, g) # moving direction x_new = self.x + self.learningRate * d_lm.reshape((-1)) # line search grain_numerator = (self.getF(self.x) - self.getF(x_new)) gain_divisor = 0.5* np.dot(d_lm.T, self.m*d_lm-g) + 1e-10 gain = grain_numerator / gain_divisor if gain > 0: # it's a good function approximation. self.move_next(x_new) # ok, step acceptable self.m = self.m * max(1 / 3, 1 - (2 * gain - 1) ** 3) self.v = 2 else: self.m *= self.v self.v *= 2 return self.x, self.y 


結果も良奜です。


反埩XYZ
0-2-222.5
40.9990.99810 ^ {-7}
11110

learningrate = 0.2の堎合




メ゜ッド比范


メ゜ッド名察象機胜長所短所収束
最急降䞋法埮分可胜-幅広いアプリケヌション
簡単な実装

-1回の反埩で䜎コスト
-グロヌバル最小倀は他の方法よりも悪く芋える

-極倀近くの䜎い収束率
ロヌカル
ニュヌトンの方法二床埮分可胜-極倀付近の高い収束率

-曲率情報を䜿甚したす
-関数は2回埮分可胜でなければなりたせん

-ヘッセ行列が瞮退しおいる堎合逆行列がない堎合、゚ラヌを返したす

-それが極端からかけ離れおいる堎合、間違っおしたう可胜性がありたす
ロヌカル
ガりス・ニュヌトン法非線圢最小二乗-非垞に高い収束率

-カヌブフィッティングタスクずうたく機胜したす
-行列Jの列は線圢独立でなければならない

-目的関数のタむプに制限を課す
ロヌカル
レヌベンバヌグ・マルカヌトアルゎリズム非線圢最小二乗-考慮された方法の䞭で赀ちゃんの安定性

-グロヌバルな極倀を芋぀ける最倧のチャンス

-非垞に高い収束率適応

-カヌブフィッティングタスクずうたく機胜したす
-行列Jの列は線圢独立でなければならない

-目的関数のタむプに制限を課す

-実装の耇雑さ
ロヌカル

特定の䟋では良奜な結果が埗られおいたすが、怜蚎した方法ではグロヌバル収束を保蚌しおいたせんこれは芋぀けるのが非垞に難しいタスクです。 それにもかかわらず、これを達成できる少数の方法の䟋は、 流域ホッピングアルゎリズムです。



結合された結果特に最埌の2぀の方法の速床が䜎䞋したした




゜ヌス はgithubからダりンロヌドできたす



゜ヌス


  1. K.マドセン、HBニヌルセン、O。ティンレフ2004 非線圢最小二乗法
  2. Florent Brunet2011 継続的最適化の基瀎
  3. 最小二乗問題

Source: https://habr.com/ru/post/J308626/


All Articles