【轉】 Levenberg-Marquardt 算法快速入門教程

原帖地址:http://blog.csdn.net/a383201241/article/details/46299861

本文附的源程序是MATLAB代碼,總共不到80行,實現了 求雅克比矩陣的解析解,演示了Levenberg-Marquardt最優化迭代過程,演示瞭如何求解擬合問題。本文用圖文介紹了LM算法。轉帖來自蜜蜂電腦

什麼是最優化,可分爲幾大類?

答:Levenberg-Marquardt算法是最優化算法中的一種。最優化是尋找使得函數值最小的參數向量。它的應用領域非常廣泛,如:經濟學、管理優化、網絡分析 、最優設計、機械或電子設計等等。 
根據求導數的方法,可分爲2大類。第一類,若f具有解析函數形式,知道x後求導數速度快。第二類,使用數值差分來求導數。根據使用模型不同,分爲非約束最優化、約束最優化、最小二乘最優化。

什麼是Levenberg-Marquardt算法?

它是使用最廣泛的非線性最小二乘算法,中文爲列文伯格-馬夸爾特法。它是利用梯度求最大(小)值的算法,形象的說,屬於“爬山”法的一種。它同時具有梯度法和牛頓法的優點。當λ很小時,步長等於牛頓法步長,當λ很大時,步長約等於梯度下降法的步長。在作者的科研項目中曾經使用過多次。圖1顯示了算法從起點,根據函數梯度信息,不斷爬升直到最高點(最大值)的迭代過程。共進行了12步。(備註:圖1中綠色線條爲迭代過程,但是由於分辨率小,看得不太清楚,單擊該圖後可放大查看)。

這裏寫圖片描述
圖1 LM算法迭代過程形象描述

圖1中,算法從山腳開始不斷迭代。可以看到,它的尋優速度是比較快的,在山腰部分直接利用梯度大幅度提升(參見後文例子程序中lamda較小時),快到山頂時經過幾次嘗試(lamda較大時),最後達到頂峯(最大值點),算法終止。

如何快速學習LM算法?

學習該算法的主要困難是入門難。 要麼國內中文教材太艱澀難懂,要麼太抽象例子太少。目前,我看到的最好的英文入門教程是K. Madsen等人的《Methods for non-linear least squares problems》本來想把原文翻譯一下,貼到這裏。請讓我偷個懶吧。能找到這裏的讀者,應該都是E文好手,我翻譯得不清不楚,反而事倍功半了。 
該文鏈接:http://www2.imm.dtu.dk/pubdb/public/publications.php? year=&pubtype=7&pubsubtype=§ion=1&cmd=full_view&lastndays=&order=author。或者直接下載pdf原文:http://www2.imm.dtu.dk/pubdb/views/edoc_download.php/3215/pdf/imm3215.pdf

例子程序(MATLAB源程序)

本程序不到100行,實現了 求雅克比矩陣的解析解,Levenberg-Marquardt最優化迭代,演示瞭如何求解擬合問題。採用蕭樹鐵主編的《數學試驗》(第二版)(高等教育出版社)中p190例2(血藥濃度)來演示。在MATLAB中可直接運行得到最優解。

<code class="language-matlab hljs  has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;">    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">% 計算函數f的雅克比矩陣,是解析式</span>
    syms a b y x <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">real</span>;
    f=a*<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">exp</span>(-b*x);
    Jsym=jacobian(f,<span class="hljs-matrix" style="box-sizing: border-box;">[a b]</span>)


    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">% 擬合用數據。參見《數學試驗》,p190,例2</span>
    data_1=<span class="hljs-matrix" style="box-sizing: border-box;">[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0.25</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0.5</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1.5</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">3</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">4</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">6</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">8</span>]</span>;
    obs_1=<span class="hljs-matrix" style="box-sizing: border-box;">[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">19.21</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">18.15</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">15.36</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">14.10</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">12.89</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">9.32</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">7.45</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">5.24</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">3.01</span>]</span>;

    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">% 2. LM算法</span>
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">% 初始猜測s</span>
    a0=<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">10</span>; b0=<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0.5</span>;
    y_init = a0*<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">exp</span>(-b0*data_1);
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">% 數據個數</span>
    Ndata=<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">length</span>(obs_1);
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">% 參數維數</span>
    Nparams=<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2</span>;
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">% 迭代最大次數</span>
    n_iters=<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">50</span>;
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">% LM算法的阻尼係數初值</span>
    lamda=<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0.01</span>;

    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">% step1: 變量賦值</span>
    updateJ=<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>;
    a_est=a0;
    b_est=b0;

    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">% step2: 迭代</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> it=<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>:n_iters
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> updateJ==<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">% 根據當前估計值,計算雅克比矩陣</span>
            J=<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">zeros</span>(Ndata,Nparams);
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">i</span>=<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>:<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">length</span>(data_1)
                J(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">i</span>,:)=<span class="hljs-matrix" style="box-sizing: border-box;">[exp(-b_est*data_1(i)) -a_est*data_1(i)*exp(-b_est*data_1(i))]</span>;
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">end</span>
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">% 根據當前參數,得到函數值</span>
            y_est = a_est*<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">exp</span>(-b_est*data_1);
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">% 計算誤差</span>
            d=obs_1-y_est;
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">% 計算(擬)海塞矩陣</span>
            H=<span class="hljs-transposed_variable" style="box-sizing: border-box;">J'</span>*J;
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">% 若是第一次迭代,計算誤差</span>
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> it==<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>
                e=<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">dot</span>(d,d);
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">end</span>
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">end</span>

        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">% 根據阻尼係數lamda混合得到H矩陣</span>
        H_lm=H+(lamda*<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">eye</span>(Nparams,Nparams));
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">% 計算步長dp,並根據步長計算新的可能的\參數估計值</span>
        dp=inv(H_lm)*(<span class="hljs-transposed_variable" style="box-sizing: border-box;">J'</span>*d(:));
        g = <span class="hljs-transposed_variable" style="box-sizing: border-box;">J'</span>*d(:);
        a_lm=a_est+dp(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>);
        b_lm=b_est+dp(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2</span>);
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">% 計算新的可能估計值對應的y和計算殘差e</span>
        y_est_lm = a_lm*<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">exp</span>(-b_lm*data_1);
        d_lm=obs_1-y_est_lm;
        e_lm=<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">dot</span>(d_lm,d_lm);
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">% 根據誤差,決定如何更新參數和阻尼係數</span>
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> e_lm<e
            lamda=lamda/<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">10</span>;
            a_est=a_lm;
            b_est=b_lm;
            e=e_lm;
            <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">disp</span>(e);
            updateJ=<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>;
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span>
            updateJ=<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>;
            lamda=lamda*<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">10</span>;
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">end</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">end</span>
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">%顯示優化的結果</span>
    a_est
    b_est</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li><li style="box-sizing: border-box; padding: 0px 5px;">45</li><li style="box-sizing: border-box; padding: 0px 5px;">46</li><li style="box-sizing: border-box; padding: 0px 5px;">47</li><li style="box-sizing: border-box; padding: 0px 5px;">48</li><li style="box-sizing: border-box; padding: 0px 5px;">49</li><li style="box-sizing: border-box; padding: 0px 5px;">50</li><li style="box-sizing: border-box; padding: 0px 5px;">51</li><li style="box-sizing: border-box; padding: 0px 5px;">52</li><li style="box-sizing: border-box; padding: 0px 5px;">53</li><li style="box-sizing: border-box; padding: 0px 5px;">54</li><li style="box-sizing: border-box; padding: 0px 5px;">55</li><li style="box-sizing: border-box; padding: 0px 5px;">56</li><li style="box-sizing: border-box; padding: 0px 5px;">57</li><li style="box-sizing: border-box; padding: 0px 5px;">58</li><li style="box-sizing: border-box; padding: 0px 5px;">59</li><li style="box-sizing: border-box; padding: 0px 5px;">60</li><li style="box-sizing: border-box; padding: 0px 5px;">61</li><li style="box-sizing: border-box; padding: 0px 5px;">62</li><li style="box-sizing: border-box; padding: 0px 5px;">63</li><li style="box-sizing: border-box; padding: 0px 5px;">64</li><li style="box-sizing: border-box; padding: 0px 5px;">65</li><li style="box-sizing: border-box; padding: 0px 5px;">66</li><li style="box-sizing: border-box; padding: 0px 5px;">67</li><li style="box-sizing: border-box; padding: 0px 5px;">68</li><li style="box-sizing: border-box; padding: 0px 5px;">69</li><li style="box-sizing: border-box; padding: 0px 5px;">70</li><li style="box-sizing: border-box; padding: 0px 5px;">71</li><li style="box-sizing: border-box; padding: 0px 5px;">72</li><li style="box-sizing: border-box; padding: 0px 5px;">73</li><li style="box-sizing: border-box; padding: 0px 5px;">74</li><li style="box-sizing: border-box; padding: 0px 5px;">75</li></ul>

本程序對應的C++實現,已經公開,點此進入。

演示程序求解的問題是《數學試驗》(蕭樹鐵,第二版,高等教育出版社)中p190例2。爲了方便讀者,提供該書籍的數據和目標函數照片(2012年4月1日)。 
這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章