【转】LM(Levenberg-Marquard) Matlab及C语言实现

帖子原地址:http://blog.csdn.net/wejoncy/article/details/44095527

LM在最优化中占据着极其重要的地位,将非线性问题转为为线性问题来求解,求出最优解。

 

最近在搞相机标定,而不得不面对LM,首先的想法百度,(个人不喜欢去看e文,总觉得效率不高,事实上错了)找到了一些相关的文章,好难啊,这是第一想法。不过还是找到了几篇大牛的文章,以及相关的MATLAB实现代码,本文的c实现便是基于MATLAB代码。感谢原作者

 

http://www.codelast.com/?p=29,这是大牛对之及其通俗的解释,相信看了能明白个大概。

http://www2.imm.dtu.dk/pubdb/views/edoc_download.php/3215/pdf/imm3215.pdf

讲解的非常透彻的一篇文档,以及实现。E文的,应该看懂都没问题。

[plain]  view plain copy
  1. % 计算函数f的雅克比矩阵,是解析式  
  2. syms a b y x real;  
  3. f=a*exp(-b*x);  
  4. Jsym=jacobian(f,[a b])  
  5.    
  6.    
  7. % 拟合用数据。参见《数学试验》,p190,例2  
  8. data_1=[0.25 0.5 1 1.5 2 3 4 6 8];  
  9. obs_1=[19.21 18.15 15.36 14.10 12.89 9.32 7.45 5.24 3.01];  
  10.    
  11. % 2. LM算法  
  12. % 初始猜测s  
  13. a0=10; b0=0.5;  
  14. y_init = a0*exp(-b0*data_1);  
  15. % 数据个数  
  16. Ndata=length(obs_1);  
  17. % 参数维数  
  18. Nparams=2;  
  19. % 迭代最大次数  
  20. n_iters=50;  
  21. % LM算法的阻尼系数初值  
  22. lamda=0.01;  
  23.    
  24. % step1: 变量赋值  
  25. updateJ=1;  
  26. a_est=a0;  
  27. b_est=b0;  
  28.    
  29. % step2: 迭代  
  30. for it=1:n_iters  
  31.     if updateJ==1  
  32.         % 根据当前估计值,计算雅克比矩阵  
  33.         J=zeros(Ndata,Nparams);  
  34.         for i=1:length(data_1)  
  35.             J(i,:)=[exp(-b_est*data_1(i)) -a_est*data_1(i)*exp(-b_est*data_1(i))];  
  36.         end  
  37.         % 根据当前参数,得到函数值  
  38.         y_est = a_est*exp(-b_est*data_1);  
  39.         % 计算误差  
  40.         d=obs_1-y_est;  
  41.         % 计算(拟)海塞矩阵  
  42.         H=J'*J;  
  43.         % 若是第一次迭代,计算误差  
  44.         if it==1  
  45.             e=dot(d,d);  
  46.         end  
  47.     end  
  48.    
  49.     % 根据阻尼系数lamda混合得到H矩阵  
  50.     H_lm=H+(lamda*eye(Nparams,Nparams));  
  51.     % 计算步长dp,并根据步长计算新的可能的\参数估计值  
  52.     dp=inv(H_lm)*(J'*d(:));  
  53.     g = J'*d(:);  
  54.     a_lm=a_est+dp(1);  
  55.     b_lm=b_est+dp(2);  
  56.     % 计算新的可能估计值对应的y和计算残差e  
  57.     y_est_lm = a_lm*exp(-b_lm*data_1);  
  58.     d_lm=obs_1-y_est_lm;  
  59.     e_lm=dot(d_lm,d_lm);  
  60.     % 根据误差,决定如何更新参数和阻尼系数  
  61.     if e_lm<e  
  62.         lamda=lamda/10;  
  63.         a_est=a_lm;  
  64.         b_est=b_lm;  
  65.         e=e_lm;  
  66.         disp(e);  
  67.         updateJ=1;  
  68.     else  
  69.         updateJ=0;  
  70.         lamda=lamda*10;  
  71.     end  
  72. end  
  73. %显示优化的结果  
  74. a_est  
  75. b_est  



以上是MATLAB实现,据说原作者是http://www.shenlejun.cn/my/article/show.asp?id=17&page=1但是好像打不开。


于是我自己想用c语言来实现,写了很久发现还是有点写不出来啊,看来代码实现能力还是不行。最后不得已写了一个 以上MATLABc语言版本。


非常感谢那些把经验和知识分享出来的牛人。


[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <math.h>  
  3. #include <string.h>  
  4.   
  5. /* 
  6. 函数表达式为 f(x)=a*exp(-b*x),a,b为待估参数 
  7. */  
  8. double data_1[9]={0.25 ,0.5, 1 ,1.5 ,2, 3, 4, 6 ,8};  
  9. double obs_1[9]={19.21 ,18.15 ,15.36 ,14.10 ,12.89, 9.32 ,7.45 ,5.24 ,3.01};  
  10. /* 
  11. void cal_jaco(double **J, double a_bst, double b_bst) 
  12. { 
  13.     int i,j; 
  14.     j=0; 
  15.     for(i=0;i<9;i++) 
  16.     { 
  17.             J[i][j] = exp(-b_bst*data_1[i]); 
  18.             J[i][j+1] = -a_bst*data_1[i]*exp(-b_bst*data_1[i]); 
  19.     } 
  20. } 
  21. */  
  22. //计算函数的值,目前只计算已知函数,即无法对输入的函数求解  
  23. void cal_fun_val(double *val,double a_bst, double b_bst)  
  24. {  
  25.     int i;  
  26.     for(i=0;i<9;i++)  
  27.     {  
  28.         val[i]=a_bst*exp(-b_bst*data_1[i]);  
  29.     }  
  30. }  
  31. //一位向量的减法  
  32. void matrix_sub(double *result, double *first_m, double *second_m)  
  33. {  
  34.     int i;  
  35.     for(i=8;i>=0;i--)  
  36.     {  
  37.         result[i]=first_m[i]-second_m[i];  
  38.     }  
  39. }  
  40. //一维向量的加法  
  41. void matrix_add(double *result, const double *first_m, const double *second_m)  
  42. {  
  43.     int i;  
  44.     for (i = 8; i >= 0; i--)  
  45.     {  
  46.         result[i] = first_m[i] + second_m[i];  
  47.     }  
  48. }  
  49. //二维向量的转置  
  50. void tran_matrix(double **tran_mat, double **matrix, int row,int col)  
  51. {  
  52.     int i,j;  
  53.     for (i = 0; i < row; i++)  
  54.     {  
  55.         for (j = 0; j < col; j++)  
  56.         {  
  57.             *((double *)tran_mat + row*j +i) = *((double *)matrix + col*i + j);  
  58.         }  
  59.     }  
  60. }  
  61. //打印出n*m的矩阵  
  62. void print_matrix(double **matrix, int row, int col)  
  63. {  
  64.     int i,j;  
  65.     for (i = 0; i < row; i++)  
  66.     {  
  67.         for (j = 0; j < col; j++)  
  68.         {  
  69.             printf("%f ", *((double *)matrix + col*i + j));  
  70.         }  
  71.             printf("\n");  
  72.     }  
  73. }  
  74. //二维矩阵的乘法  
  75. /* 
  76. outa=inb*inc 
  77. */  
  78. void matrix_mut(double ** outa, double** inb, int b_row, int b_col, double** inc, int c_col)  
  79. {  
  80.     int i, j, k,num;  
  81.     double tempdata=0;  
  82.     num = 0;  
  83.     double *tempa = (double *)outa;  
  84.     double t1, t2;  
  85.     for (i = 0; i < b_row; i++)  
  86.     {  
  87.         for (k = 0; k < c_col; k++)  
  88.         {  
  89.             for (j = 0; j < b_col; j++)  
  90.             {     
  91.                 t1 = (*((double *)inb + b_col*i + j));  
  92.                 t2 = (*((double *)inc + c_col*j + k));  
  93.                 tempdata += t1*t2;  
  94.             }  
  95.             tempa[num]=tempdata;  
  96.             num++;  
  97.             tempdata = 0;  
  98.         }  
  99.     }  
  100. }  
  101. //矩阵与常数相乘  
  102. void cost_mat_mul(double **res_mat, int ratio, double **mat, int row, int col)  
  103. {  
  104.     int i, j;  
  105.     for (i = 0; i < row; i++)  
  106.     {  
  107.         for (j = 0; j < col; j++)  
  108.         {  
  109.             (*((double *)res_mat + col*i + j)) = ratio * (*((double *)mat + col*i + j));  
  110.         }  
  111.     }  
  112. }  
  113. //向量点积  
  114. double dot(double * mat1, double * mat2,int nofelem)  
  115. {  
  116.     double *temp_mat1 = mat1;  
  117.     double *temp_mat2 = mat2;  
  118.     double result = 0;  
  119.     int i;  
  120.     for (i = 0; i<nofelem;i++)  
  121.         result += (*temp_mat1++)*(*temp_mat2++);  
  122.     return result;  
  123. }  
  124. #define MAX 2  
  125. #define E 0.000000001  
  126.   
  127. /** 
  128. * 计算矩阵行列式src的模值 
  129. */  
  130. double calculate_A(double src[][MAX], int n)  
  131. {  
  132.     int i, j, k, x, y;  
  133.     double tmp[MAX][MAX], t;  
  134.     double result = 0.0;  
  135.     if (n == 1)  
  136.     {  
  137.         return src[0][0];  
  138.     }  
  139.   
  140.     for (i = 0; i < n; ++i)  
  141.     {  
  142.         for (j = 0; j < n - 1; ++j)  
  143.         {  
  144.             for (k = 0; k < n - 1; ++k)  
  145.             {  
  146.                 x = j + 1;  
  147.                 y = k >= i ? k + 1 : k;  
  148.   
  149.                 tmp[j][k] = src[x][y];  
  150.             }  
  151.         }  
  152.         t = calculate_A(tmp, n - 1);  
  153.   
  154.         if (i % 2 == 0)  
  155.         {  
  156.             result += src[0][i] * t;  
  157.         }  
  158.         else  
  159.         {  
  160.             result -= src[0][i] * t;  
  161.         }  
  162.     }  
  163.   
  164.     return result;  
  165. }  
  166.   
  167. /** 
  168. * 计算伴随矩阵 
  169. */  
  170. void calculate_A_adjoint(double src[MAX][MAX], double dst[MAX][MAX], int n)  
  171. {  
  172.     int i, j, k, t, x, y;  
  173.     double tmp[MAX][MAX];  
  174.   
  175.     if (n == 1)  
  176.     {  
  177.         dst[0][0] = 1;  
  178.         return;  
  179.     }  
  180.   
  181.     for (i = 0; i < n; ++i)  
  182.     {  
  183.         for (j = 0; j < n; ++j)  
  184.         {  
  185.             for (k = 0; k < n - 1; ++k)  
  186.             {  
  187.                 for (t = 0; t < n - 1; ++t)  
  188.                 {  
  189.                     x = k >= i ? k + 1 : k;  
  190.                     y = t >= j ? t + 1 : t;  
  191.   
  192.                     tmp[k][t] = src[x][y];  
  193.                 }  
  194.             }  
  195.   
  196.             dst[j][i] = calculate_A(tmp, n - 1);  
  197.   
  198.             if ((i + j) % 2 == 1)  
  199.             {  
  200.                 dst[j][i] = -1 * dst[j][i];  
  201.             }  
  202.         }  
  203.     }  
  204. }  
  205.   
  206. /** 
  207. * 得到逆矩阵 
  208. */  
  209. int inv(const double src[MAX][MAX], double dst[MAX][MAX], int n)  
  210. {  
  211.     double A = calculate_A(src, n);  
  212.     double tmp[MAX][MAX];  
  213.     int i, j;  
  214.     if (fabs(A - 0) <= E)  
  215.     {  
  216.         //       printf("不可能有逆矩阵!\n");  
  217.         return 0;  
  218.     }  
  219.   
  220.     calculate_A_adjoint(src, tmp, n);  
  221.   
  222.     for (i = 0; i < n; ++i)  
  223.     {  
  224.         for (j = 0; j < n; ++j)  
  225.         {  
  226.             dst[i][j] = (double)(tmp[i][j] / A);  
  227.         }  
  228.     }  
  229.   
  230.     return 1;  
  231. }  
  232. //LM算法 核心代码  
  233. int LM()  
  234. {  
  235.         // 迭代最大次数  
  236.     int n_iters=400;  
  237.     // LM算法的阻尼系数初值  
  238.     double lamda=0.01;  
  239.     //初始值  
  240.     double a0=20;  
  241.     double b0=0.5;  
  242.     // step1: 变量赋值  
  243.     int updateJ=1;  
  244.     double a_est=a0;  
  245.     double b_est=b0;  
  246.   
  247.     int it=0;  
  248.     double J[9][2];  
  249.     double Jt[2][9];  
  250.     double y_est[9];  
  251.     double d[9];  
  252.     double H[2][2] = {0};  
  253.     double H_1m[2][2];  
  254.     double e;  
  255.     double tempeye[2][2] = { { 1, 0 }, { 0, 1 } };  
  256.     double eye[2][2] = { { 1, 0 }, { 0, 1 } };  
  257.     double g[2] = {0};  
  258.     int i, j;  
  259.     double invH_1m[2][2] = {0};  
  260.     double dp[2] = {0};  
  261.     double a_1m, b_1m;  
  262.     double y_est_lm[9];  
  263.     double d_1m[9], e_1m;  
  264.     double temp = 0;  
  265.     for ( it =0;it<n_iters; it++)  
  266.     {  
  267.         if(updateJ == 1)  
  268.         {  
  269. //          cal_jaco(J,a_est,b_est);  
  270.             for (i = 0; i<9; i++)  
  271.             {//计算雅克比矩阵  
  272.                 J[i][0] = exp(-b_est*data_1[i]);  
  273.                 J[i][1] = -a_est*data_1[i] * exp(-b_est*data_1[i]);  
  274.             }  
  275.             cal_fun_val(y_est, a_est, b_est);  
  276.             matrix_sub(d, obs_1, y_est);//计算当前的误差  
  277.             tran_matrix(Jt, J, 9, 2);  
  278.             matrix_mut(H, Jt, 2, 9, J, 2);//得到Hessian 矩阵  
  279.             if (it == 0)  
  280.             {  
  281.                 e = dot(d, d, 9);//第一次时计算误差作为误差初值  
  282.             }  
  283.         }  
  284.         cost_mat_mul(tempeye, lamda, eye, 2, 2);  
  285. /*      for (i = 0; i < 2; i++) 
  286.         { 
  287.             for (j = 0; j < 2; j++) 
  288.             { 
  289.                 tempeye[i][j] = eye[i][j] * lamda; 
  290.             } 
  291.         }*/  
  292. //      matrix_add(H_1m, H, tempeye);  
  293.         for (i = 0; i < 2; i++)  
  294.         {  
  295.             for (j = 0; j < 2; j++)  
  296.             {  
  297.                 H_1m[i][j] = H[i][j] + tempeye[i][j];  
  298.             }  
  299.         }  
  300.         for (i = 0; i < 2; i++)  
  301.         {  
  302.             for (j = 0; j < 9; j++)  
  303.             {  
  304.                 temp += Jt[i][j] * d[j];  
  305.             }  
  306.             g[i] = temp;  
  307.         }  
  308.         inv(H_1m, invH_1m, 2);  
  309.         temp = 0;  
  310.         for (i = 0; i < 2; i++)  
  311.         {  
  312.             for (j = 0; j < 2; j++)  
  313.             {  
  314.                 temp += invH_1m[i][j] * g[j];  
  315.             }  
  316.             dp[i] = temp;//求解步长  
  317.         }  
  318.         a_1m = a_est + dp[0];  
  319.         b_1m = b_est + dp[1];  
  320.         for (i = 0; i < 9; i++)  
  321.         {  
  322.             y_est_lm[i] = a_1m*exp(-b_1m*data_1[i]);  
  323.             d_1m[i] = obs_1[i] - y_est_lm[i];  
  324.         }  
  325.         e_1m = dot(d_1m, d_1m, 9);//计算新的 a,b时的误差  
  326.       
  327.         if (e_1m < e)//如果误差下降了,这是我们想要的  
  328.         {  
  329.             lamda = lamda / 10;  
  330.             a_est = a_1m;  
  331.             b_est = b_1m;  
  332.             e = e_1m;  
  333.             printf("%f  \n", e);  
  334.             updateJ = 1;  
  335.         }  
  336.         else  
  337.         {  
  338.             updateJ = 0;  
  339.             lamda = lamda * 10;  
  340.         }  
  341.     }  
  342.     printf("%f,%f  ", a_est, b_est);  
  343.     return 1;  
  344. }  
  345.   
  346. int main()  
  347. {  
  348.     LM();  
  349.     return 0;  
  350. }  
  • 4
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值