04 牛顿法、高斯牛顿法及 Cpp 实现

文章目录

    • 04 牛顿法、高斯牛顿法及 Cpp 实现
      • 4.1 非线性最小二乘
      • 4.2 一阶和二阶梯度法
      • 4.3 高斯牛顿法
      • 4.4 总结
      • 4.5 代码实现
      • 4.6 三种方法优缺点

04 牛顿法、高斯牛顿法及 Cpp 实现

4.1 非线性最小二乘

考虑最小二乘问题:

min ⁡ x F ( x ) = 1 2 ∥ f ( x ) ∥ 2 2 \min _{x} F(x)=\frac{1}{2}\|f(x)\|_{2}^{2} xminF(x)=21f(x)22

对于简单的函数,我们可以令其导数 d F d x = 0 \frac{\rm{d}F}{\rm{d}\boldsymbol{x}}=0 dxdF=0,得到导数为零处的极值点。

而 SLAM 中的非线性函数形式复杂,常常难以用求导法得到最值,而采用梯度下降法,即,通过不断迭代 x k + 1 = x k + Δ x k \boldsymbol{x}_{k+1}=\boldsymbol{x}_k+\Delta\boldsymbol{x}_k xk+1=xk+Δxk,使得 ∣ ∣ f ( x k + Δ x k ) ∣ ∣ 2 ||f(\boldsymbol{x}_k+\Delta\boldsymbol{x}_k)||^2 ∣∣f(xk+Δxk)2 达到极小值,当 Δ x \Delta \boldsymbol{x} Δx 足够小时,即停止。关键在于增量 Δ x \Delta\boldsymbol{x} Δx 的选取。

过程如下:

——————————————————————————————————————————————————————

  • 给定某个初值 x 0 \boldsymbol{x}_0 x0

  • 对于第 k k k 次迭代,寻找增量 Δ x k \Delta\boldsymbol{x}_k Δxk

  • Δ x k \Delta \boldsymbol{x}_k Δxk 足够小时,即停止;否则,重复第二步,继续寻找。

——————————————————————————————————————————————————————

下面介绍几个常用的优化方法。

4.2 一阶和二阶梯度法

对于非线性函数 F ( x ) F(\boldsymbol{x}) F(x),考虑第 k k k 次迭代,将其在 x k \boldsymbol{x}_k xk 附近泰勒展开(将 Δ x k \Delta \boldsymbol{x}_k Δxk 看做未知数),

F ( x ) = F ( x k + Δ x k ) ≈ F ( x k ) + J ( x k ) T Δ x k + 1 2 Δ x k T H ( x k ) T Δ x k F(\boldsymbol{x})=F(\boldsymbol{x}_k+\Delta \boldsymbol{x}_k) \approx F(\boldsymbol{x}_k)+\boldsymbol{J}(\boldsymbol{x}_k)^T\Delta \boldsymbol{x}_k+\frac{1}{2}\Delta \boldsymbol{x}_k^T\boldsymbol{H}(\boldsymbol{x}_k)^T\Delta \boldsymbol{x}_k F(x)=F(xk+Δxk)F(xk)+J(xk)TΔxk+21ΔxkTH(xk)TΔxk

其中, J ( x k ) \boldsymbol{J}(\boldsymbol{x}_k) J(xk) F ( x ) F(\boldsymbol{x}) F(x) 关于 x \boldsymbol{x} x 的一阶导数, H ( x k ) \boldsymbol{H}(\boldsymbol{x}_k) H(xk) 是二阶导数。

(1)最速下降法

仅保留一阶导数时,取增量为反向梯度即可,即

Δ x ∗ = − J ( x k ) \Delta \boldsymbol{x}^*=-\boldsymbol{J}(\boldsymbol{x}_k) Δx=J(xk)

也被称为最速下降法

(2)牛顿法

保留二阶梯度信息。此时增量方程为

Δ x ∗ = arg ⁡ min ⁡ ( F ( x ) + J ( x ) T Δ x + 1 2 Δ x T H Δ x ) \Delta \boldsymbol{x}^{*}=\arg \min \left(F(\boldsymbol{x})+\boldsymbol{J}(\boldsymbol{x})^{\mathrm{T}} \Delta \boldsymbol{x}+\frac{1}{2} \Delta \boldsymbol{x}^{\mathrm{T}} \boldsymbol{H} \Delta \boldsymbol{x}\right) Δx=argmin(F(x)+J(x)TΔx+21ΔxTHΔx)

右侧各项分别为 Δ x \Delta \boldsymbol{x} Δx 的零次、一次和二次项,将其对 Δ x \Delta \boldsymbol{x} Δx 求导,并令其为零

J + H Δ x = 0 ⇒ H Δ x = − J \boldsymbol{J}+\boldsymbol{H} \Delta \boldsymbol{x}=0 \Rightarrow \boldsymbol{H} \Delta \boldsymbol{x}=-\boldsymbol{J} J+HΔx=0HΔx=J

求解这个方程,即得到增量。此方法也被称为牛顿法。但在实际中, H \boldsymbol{H} H 矩阵计算较为困难。

4.3 高斯牛顿法

f ( x ) f(\boldsymbol{x}) f(x) 一阶泰勒展开(注意不是 F ( x ) F(\boldsymbol{x}) F(x)):

f ( x + Δ x ) ≈ f ( x ) + J ( x ) T Δ x f(\boldsymbol{x}+\Delta \boldsymbol{x}) \approx f(\boldsymbol{x})+\boldsymbol{J}(\boldsymbol{x})^T\Delta \boldsymbol{x} f(x+Δx)f(x)+J(x)TΔx

其中, J ( x ) \boldsymbol{J}(\boldsymbol{x}) J(x) f ( x ) f(\boldsymbol{x}) f(x) 关于 x \boldsymbol{x} x 的一阶导数,为 n × 1 n \times 1 n×1 列向量。

此时,我们的问题变为找到 Δ x \Delta \boldsymbol{x} Δx ,使得 1 2 ∥ f ( x ) + J ( x ) T Δ x ∥ 2 \frac{1}{2} \|f(\boldsymbol{x})+\boldsymbol{J}(\boldsymbol{x})^T\Delta \boldsymbol{x}\|^2 21f(x)+J(x)TΔx2 最小。将其展开

1 2 ∥ f ( x ) + J ( x ) T Δ x ∥ 2 = 1 2 ( f ( x ) + J ( x ) T Δ x ) T ( f ( x ) + J ( x ) T Δ x ) = 1 2 ( ∥ f ( x ∥ 2 + 2 f ( x ) J ( x ) T Δ x + Δ x T J ( x ) J ( x ) T Δ x ) \begin{aligned} \frac{1}{2} \|f(\boldsymbol{x})+\boldsymbol{J}(\boldsymbol{x})^T\Delta \boldsymbol{x}\|^2&=\frac{1}{2} (f(\boldsymbol{x})+\boldsymbol{J}(\boldsymbol{x})^T\Delta \boldsymbol{x})^T(f(\boldsymbol{x})+\boldsymbol{J}(\boldsymbol{x})^T\Delta \boldsymbol{x}) \\ &=\frac{1}{2} (\|f(\boldsymbol{x}\|^2+2f(\boldsymbol{x})\boldsymbol{J}(\boldsymbol{x})^T\Delta \boldsymbol{x}+\Delta \boldsymbol{x}^T\boldsymbol{J}(\boldsymbol{x})\boldsymbol{J}(\boldsymbol{x})^T\Delta \boldsymbol{x}) \end{aligned} 21f(x)+J(x)TΔx2=21(f(x)+J(x)TΔx)T(f(x)+J(x)TΔx)=21(f(x2+2f(x)J(x)TΔx+ΔxTJ(x)J(x)TΔx)

Δ x \Delta \boldsymbol{x} Δx 看做未知数,对其求导,并令其为零:

f ( x ) J ( x ) + J ( x ) J ( x ) T Δ x = 0 f(\boldsymbol{x})\boldsymbol{J}(\boldsymbol{x})+\boldsymbol{J}(\boldsymbol{x})\boldsymbol{J}(\boldsymbol{x})^T\Delta \boldsymbol{x}=\boldsymbol{0} f(x)J(x)+J(x)J(x)TΔx=0

J ( x ) J T ⏟ H ( x ) ( x ) Δ x = − J ( x ) f ( x ) ⏟ g ( x ) \underbrace{\boldsymbol{J}(\boldsymbol{x}) \boldsymbol{J}^{\mathrm{T}}}_{\boldsymbol{H}(\boldsymbol{x})}(\boldsymbol{x}) \Delta \boldsymbol{x}=\underbrace{-\boldsymbol{J}(\boldsymbol{x}) f(\boldsymbol{x})}_{\boldsymbol{g}(\boldsymbol{x})} H(x) J(x)JT(x)Δx=g(x) J(x)f(x)

H ( x ) Δ x = g ( x ) \boldsymbol{H}(\boldsymbol{x})\Delta \boldsymbol{x}=\boldsymbol{g}(\boldsymbol{x}) H(x)Δx=g(x)

前面说到牛顿法中的二阶矩阵 H \boldsymbol{H} H 求解较为困难,所以这里用 J J ⊤ \boldsymbol{JJ}^\top JJ 作为 H \boldsymbol{H} H 的近似,从而省略了计算 H \boldsymbol{H} H 的过程。

4.4 总结

(1)最速下降法

Δ x ∗ = − J ( x k ) \Delta \boldsymbol{x}^*=-\boldsymbol{J}(\boldsymbol{x}_k) Δx=J(xk)

(2)牛顿法

H Δ x = − J \boldsymbol{H} \Delta \boldsymbol{x}=-\boldsymbol{J} HΔx=J

(3)高斯牛顿法

J ( x ) J T ⏟ H ( x ) ( x ) Δ x = − J ( x ) f ( x ) ⏟ g ( x ) \underbrace{\boldsymbol{J}(\boldsymbol{x}) \boldsymbol{J}^{\mathrm{T}}}_{\boldsymbol{H}(\boldsymbol{x})}(\boldsymbol{x}) \Delta \boldsymbol{x}=\underbrace{-\boldsymbol{J}(\boldsymbol{x}) f(\boldsymbol{x})}_{\boldsymbol{g}(\boldsymbol{x})} H(x) J(x)JT(x)Δx=g(x) J(x)f(x)

需要注意的是,最速下降法和牛顿法属于同一类型,他们的 J \boldsymbol{J} J H \boldsymbol{H} H 都是针对 F ( x ) \boldsymbol{F(x)} F(x)(也就是整个目标函数);而高斯牛顿法的 J \boldsymbol{J} J 是针对 f ( x ) \boldsymbol{f(x)} f(x) (也就是误差项)。 此外,为减小数值,目标函数 F ( x ) \boldsymbol{F(x)} F(x) 可用均方差函数 。

4.5 代码实现

考虑一条满足以下方程的曲线

y = exp ⁡ ( a x 2 + b x + c ) + w y=\exp(ax^2+bx+c)+w y=exp(ax2+bx+c)+w

其中, a , b , c a, b, c a,b,c 为曲线参数, w w w 为高斯噪声,满足 w ∼ N ( 0 , σ 2 ) w \sim N(0, \sigma^2) wN(0,σ2)。假设有 N N N 个关于 x , y x, y x,y 的观测数据点,落在该直线附近,我们想用该方程进行拟合,也就是求出使残差最小的曲线参数 a , b , c a, b, c a,b,c。定义残差

( a , b , c ) ∗ = arg ⁡ min ⁡ a , b , c 1 2 ∑ i = 1 N ∥ y i − exp ⁡ ( a x i 2 + b x i + c ) ∥ 2 2 (a, b, c)^*=\arg \min_{a, b, c} \frac{1}{2}\sum^N_{i=1}\|y_i-\exp(ax_i^2+bx_i+c)\|^2_2 (a,b,c)=arga,b,cmin21i=1Nyiexp(axi2+bxi+c)22

注意,这个问题的带估计变量是 a , b , c a, b, c a,b,c,而不是 x x x

我们先根据模型生成 x , y x, y x,y 的真值,再加入高斯噪声,构成观测数据点。定义每一项的误差

e i = y i − exp ⁡ ( a x i 2 + b i x + c ) e_i=y_i-\exp(ax_i^2+b_ix+c) ei=yiexp(axi2+bix+c)

(1)最速下降法

Δ x ∗ = − J \Delta \boldsymbol{x}^*=-\boldsymbol{J} Δx=J

其中, J = ∑ J i \boldsymbol{J}=\sum \boldsymbol{J}_i J=Ji J i = [ ∂ F ∂ a , ∂ F ∂ b , ∂ F ∂ c ] ⊤ \boldsymbol{J}_i=[\frac{ \partial \boldsymbol{F}}{ \partial a}, \frac{ \partial \boldsymbol{F}}{ \partial b}, \frac{ \partial \boldsymbol{F}}{ \partial c}]^\top Ji=[aF,bF,cF]

∂ F ∂ a = e i ( − x i 2 exp ⁡ ( a x i 2 + b x i + c ) ) ∂ F ∂ a = e i ( − x i exp ⁡ ( a x i 2 + b x i + c ) ) ∂ F ∂ a = e i ( exp ⁡ ( a x i 2 + b x i + c ) ) \frac{ \partial \boldsymbol{F}}{ \partial a}=e_i(-x_i^2\exp(ax_i^2+bx_i+c)) \\ \frac{ \partial \boldsymbol{F}}{ \partial a}=e_i(-x_i\exp(ax_i^2+bx_i+c))\\ \frac{ \partial \boldsymbol{F}}{ \partial a}=e_i(\exp(ax_i^2+bx_i+c)) aF=ei(xi2exp(axi2+bxi+c))aF=ei(xiexp(axi2+bxi+c))aF=ei(exp(axi2+bxi+c))

/***********************************************************                                          *
* Time: 2023/8/10
* Author: xiaocong
* Function: 最速下降法
* 注意这里用的是 ae * xi * xi + be * xi + sin(ce)
* 原指数函数对误差和初值敏感,造成计算无穷大。
***********************************************************/

#include 
#include 
#include 
#include 

using namespace std;
using namespace Eigen;

const int N = 100;                 // 数据点个数
const int MAX_INTER = 1000;        // 最大迭代次数


void steepestDescent(const VectorXd& x_data, const VectorXd& y_data, Vector3d& params)
{
    double cost = 0;

    for (int iter = 0; iter < MAX_INTER; iter++)
    {
        Vector3d J = Vector3d::Zero();           // 雅可比矩阵

        cost = 0;
        double ae = params(0);
        double be = params(1);
        double ce = params(2);

        for (int i = 0;i < N;i++)
        {
            double xi = x_data(i), yi = y_data(i);

            double ei = (yi - (ae * xi * xi + be * xi + sin(ce)));    // 残差项

            Vector3d Ji;                                          // 雅可比矩阵
            Ji(0) = ei * (-xi * xi);
            Ji(1) = ei * (-xi);
            Ji(2) = ei * (-cos(ce));

            J += Ji;
            cost += ei * ei;
        }

        Vector3d dx = -J / N;          // 求解 dx

        if (isnan(dx[0]))
        {
            cout << "result is nan!" << endl;
            break;
        }

        if (dx.squaredNorm() < 1e-6)      // dx 足够小
        {
            break;
        }


        // 迭代
        params += dx;
        cout << "iter= " << iter + 1 << endl;
        cout << dx(0) << endl;
        cout << dx(1) << endl;
        cout << dx(2) << endl;
        cout << "cost: " << cost << endl;
        cout << "estimated abc= " << params(0) << ", " << params(1) << ", " << params(2) << endl;
    }

}



int main(int argc, char** argv)
{
    double ar = 1.0, br = 2.0, cr = 1;         // 真实参数值

    Vector3d params(2.0, 1.0, 0);            // 初始参数值

    // 生成数据
    VectorXd x_data(N);
    VectorXd y_data(N);

    for (int i = 0; i < N; i++)
    {
        double xi = (i / 100.0);                                     // [0~1.0]
        double sigma = 0.02 * (rand() % 1000) / 1000.0 - 0.01;     // 随机噪声,[-0.01, 0.01]
        double yi = ar * xi * xi + br * xi + sin(cr) + sigma;

        x_data(i) = xi;
        y_data(i) = yi;
    }

    steepestDescent(x_data, y_data, params);

    return 0;
}
iter= 454
-0.000666889
0.000694395
-0.000279458
cost: 0.0427055
estimated abc= 1.26563, 1.72471, 1.09882

(2)牛顿法

H Δ x = − J \boldsymbol{H} \Delta \boldsymbol{x}=-\boldsymbol{J} HΔx=J

这里还需要求出海森矩阵即二阶导

H i = [ ∂ 2 F ∂ a 2 ∂ 2 F ∂ a ∂ b ∂ 2 F ∂ a ∂ c ∂ 2 F ∂ b ∂ a ∂ 2 F ∂ b 2 ∂ 2 F ∂ b ∂ c ∂ 2 F ∂ c ∂ a ∂ 2 F ∂ c ∂ b ∂ 2 F ∂ c 2 ] \boldsymbol{H}_i=\left[\begin{array}{c} \frac{ \partial^2 \boldsymbol{F}}{ \partial a^2} & \frac{ \partial^2 \boldsymbol{F} }{\partial a \partial b} &\frac{ \partial^2 \boldsymbol{F}}{ \partial a \partial c} \\ \frac{ \partial^2 \boldsymbol{F}}{\partial b \partial a} & \frac{ \partial^2 \boldsymbol{F}}{\partial b^2} &\frac{ \partial^2 \boldsymbol{F}}{ \partial b \partial c} \\ \frac{ \partial^2 \boldsymbol{F}}{ \partial c \partial a} & \frac{ \partial^2 \boldsymbol{F}}{\partial c \partial b} &\frac{ \partial^2 \boldsymbol{F}}{\partial c^2} \\ \end{array}\right] Hi= a22Fba2Fca2Fab2Fb22Fcb2Fac2Fbc2Fc22F

H = ∑ H i \boldsymbol{H}=\sum \boldsymbol{H}_i H=Hi

/***********************************************************                                          *
* Time: 2023/8/10
* Author: xiaocong
* Function: 牛顿法
* 注意这里用的是 ae * xi * xi + be * xi + sin(ce)
***********************************************************/

#include 
#include 
#include 
#include 

using namespace std;
using namespace Eigen;

const int N = 100;                 // 数据点个数
const int MAX_INTER = 1000;        // 最大迭代次数


void steepestDescent(const VectorXd& x_data, const VectorXd& y_data, Vector3d& params)
{
    double cost = 0;

    for (int iter = 0; iter < MAX_INTER; iter++)
    {
        Vector3d J = Vector3d::Zero();           // 雅可比矩阵
        Eigen::MatrixXd H(3, 3);                 // 海森矩阵
        H.setZero();

        cost = 0;
        double ae = params(0);
        double be = params(1);
        double ce = params(2);

        for (int i = 0;i < N;i++)
        {
            double xi = x_data(i), yi = y_data(i);

            double ei = (yi - (ae * xi * xi + be * xi + sin(ce))) / N;    // 残差项

            J(0) += ei * (-xi * xi);
            J(1) += ei * (-xi);
            J(2) += ei * (-cos(ce));

            H(0, 0) += xi * xi * xi * xi;
            H(0, 1) += xi * xi * xi;
            H(0, 2) += xi * xi * cos(ce);

            H(1, 0) += xi * xi * xi;
            H(1, 1) += xi * xi;
            H(1, 2) += xi * cos(ce);

            H(2, 0) += xi * xi * cos(ce);
            H(2, 1) += xi * cos(ce);
            H(2, 2) += cos(2 * ce);

            cost += ei * ei;
        }

        Vector3d dx = H.ldlt().solve(-J);   // 求解 dx

        if (isnan(dx[0]))
        {
            cout << "result is nan!" << endl;
            break;
        }

        if (dx.squaredNorm() < 1e-6)      // dx 足够小
        {
            break;
        }


        // 迭代
        params += dx;
        cout << "iter= " << iter + 1 << endl;
        cout << dx(0) << endl;
        cout << dx(1) << endl;
        cout << dx(2) << endl;
        cout << "cost: " << cost << endl;
        cout << "estimated abc= " << params(0) << ", " << params(1) << ", " << params(2) << endl;
    }

}



int main(int argc, char** argv)
{
    double ar = 1.0, br = 2.0, cr = 1.0;         // 真实参数值

    Vector3d params(2.0, 1.0, 0.0);            // 初始参数值

    // 生成数据
    VectorXd x_data(N);
    VectorXd y_data(N);

    for (int i = 0; i < N; i++)
    {
        double xi = (i / 100.0);                                     // [0~1.0]
        double sigma = 0.02 * (rand() % 1000) / 1000.0 - 0.01;     // 随机噪声,[-0.01, 0.01]
        double yi = ar * xi * xi + br * xi + sin(cr) + sigma;

        x_data(i) = xi;
        y_data(i) = yi;
    }

    steepestDescent(x_data, y_data, params);

    return 0;
}
iter= 470
-0.000656148
0.000766084
5.77874e-06
cost: 3.52463e-06
estimated abc= 1.09233, 1.89203, 1.01297

(3)高斯牛顿法

求出每个误差项关于状态变量的导数(注意 a , b , c a, b, c a,b,c 才是未知数):

∂ e i ∂ a = − x i 2 exp ⁡ ( a x i 2 + b i x + c ) ∂ e i ∂ b = − x i exp ⁡ ( a x i 2 + b i x + c ) ∂ e i ∂ c = − exp ⁡ ( a x i 2 + b i x + c ) \frac{ \partial e_i }{ \partial a}=-x_i^2\exp(ax_i^2+b_ix+c) \\ \frac{ \partial e_i }{ \partial b}=-x_i\exp(ax_i^2+b_ix+c) \\ \frac{ \partial e_i }{ \partial c}=-\exp(ax_i^2+b_ix+c) aei=xi2exp(axi2+bix+c)bei=xiexp(axi2+bix+c)cei=exp(axi2+bix+c)

J i = [ ∂ e i ∂ a , ∂ e i ∂ b , ∂ e i ∂ c ] ⊤ \boldsymbol{J}_i=[\frac{ \partial e_i }{ \partial a}, \frac{ \partial e_i }{ \partial b}, \frac{ \partial e_i }{ \partial c}]^\top Ji=[aei,bei,cei]。和前面不一样。

J ( x ) J T ⏟ H ( x ) ( x ) Δ x = − J ( x ) f ( x ) ⏟ g ( x ) \underbrace{\boldsymbol{J}(\boldsymbol{x}) \boldsymbol{J}^{\mathrm{T}}}_{\boldsymbol{H}(\boldsymbol{x})}(\boldsymbol{x}) \Delta \boldsymbol{x}=\underbrace{-\boldsymbol{J}(\boldsymbol{x}) f(\boldsymbol{x})}_{\boldsymbol{g}(\boldsymbol{x})} H(x) J(x)JT(x)Δx=g(x) J(x)f(x)

此处即为

( ∑ i = 1 100 J i J i ⊤ ) Δ x = − ∑ i = 1 100 J i e i (\sum^{100}_{i=1}\boldsymbol{J}_i\boldsymbol{J}_i^\top)\Delta \boldsymbol{x}=-\sum^{100}_{i=1}\boldsymbol{J}_ie_i (i=1100JiJi)Δx=i=1100Jiei

/***********************************************************                                          *
* Time: 2023/8/10
* Author: xiaocong
* Function: 高斯牛顿法
***********************************************************/

#include 
#include 
#include 
#include 

using namespace std;
using namespace Eigen;

const int N = 100;                // 数据点个数
const int MAX_INTER = 100;        // 最大迭代次数


void gaussNewton(const VectorXd& x_data, const VectorXd& y_data, Vector3d& params)
{
    double cost = 0;

    for (int iter = 0; iter < MAX_INTER; iter++)
    {
        Matrix3d H = Matrix3d::Zero();      // H=JJ^T
        Vector3d b = Vector3d::Zero();      // b=-Je
        cost = 0;
        double ae = params(0);
        double be = params(1);
        double ce = params(2);


        for (int i = 0;i < N;i++)
        {
            double xi = x_data(i), yi = y_data(i);

            double ei = yi - exp(ae * xi * xi + be * xi + ce);    // 残差项

            Vector3d Ji;                                          // 雅可比矩阵
            Ji(0) = -xi * xi * exp(ae * xi * xi + be * xi + ce);
            Ji(1) = -xi * exp(ae * xi * xi + be * xi + ce);
            Ji(2) = -exp(ae * xi * xi + be * xi + ce);

            H += Ji * Ji.transpose();
            b += -Ji * ei;

            cost += ei * ei;
        }

        // 求解 dx
        Vector3d dx = H.ldlt().solve(b);

        if (isnan(dx[0]))
        {
            cout << "result is nan!" << endl;
            break;
        }

        if (dx.squaredNorm() < 1e-6)      // dx 足够小
        {
            break;
        }


        // 迭代
        params += dx;
        cout << "iter= " << iter + 1 << endl;
        cout << dx(0) << endl;
        cout << dx(1) << endl;
        cout << dx(2) << endl;
        cout << "cost= " << cost << endl;
        cout << "estimated abc= " << params(0) << ", " << params(1) << ", " << params(2) << endl;
    }
}



int main(int argc, char** argv)
{
    double ar = 1.0, br = 2.0, cr = 1.0;         // 真实参数值

    Vector3d params(2.0, -1.0, 5.0);            // 初始参数值

    // 生成数据
    VectorXd x_data(N);
    VectorXd y_data(N);

    for (int i = 0; i < N; i++)
    {
        double xi = i / 100.0;                                     // [0~1]
        double sigma = 0.02 * (rand() % 1000) / 1000.0 - 0.01;     // 随机噪声,[-0.01, 0.01]
        double yi = exp(ar * xi * xi + br * xi + cr) + sigma;

        x_data(i) = xi;
        y_data(i) = yi;
    }

    gaussNewton(x_data, y_data, params);

    return 0;
}
iter= 7
-0.00134314
0.00213517
-0.000823118
cost= 0.00356444
estimated abc= 0.999314, 2.00098, 0.999659

4.6 三种方法优缺点

最速下降法、牛顿法和高斯牛顿法都是常用的优化算法,用于解决最小化问题。它们各自有不同的优缺点,下面是对这三种算法的优缺点进行总结:

最速下降法:

优点:

  1. 简单直观: 最速下降法的核心思想简单易懂,容易实现。
  2. 适用广泛: 适用于一般的优化问题,不需要太多的先验知识。
  3. 内存消耗小: 只需要存储当前参数和梯度,内存消耗相对较小。

缺点:

  1. 收敛速度慢: 最速下降法的收敛速度通常较慢,特别是在存在长轴状谷底或细长山谷的情况下。
  2. 依赖初始值: 初始参数的选择会影响收敛性能,不合适的初始值可能导致陷入局部最小值。
  3. 不适用于高维问题: 在高维问题中,计算梯度和参数更新可能变得耗时,从而影响效率。

牛顿法:

优点:

  1. 收敛速度快: 牛顿法在逼近局部最小值时的收敛速度通常比最速下降法更快。
  2. 二阶信息利用: 牛顿法利用了函数的二阶导数信息,可以更准确地估计局部几何特性。

缺点:

  1. 计算复杂: 牛顿法需要计算函数的二阶导数(Hessian矩阵),计算成本较高,特别是在高维问题中。
  2. 不稳定性: Hessian矩阵可能不是正定的,导致算法不稳定。此外,在某些情况下,牛顿法可能在非凸问题中陷入局部最小值。

高斯牛顿法:

优点:

  1. 适用于非线性问题: 高斯牛顿法适用于非线性优化问题,尤其是拟合参数化模型时效果较好。
  2. 不需要二阶导数: 高斯牛顿法不需要直接计算Hessian矩阵,而是通过近似方法来计算。

缺点:

  1. 对初始值敏感: 高斯牛顿法对初始参数的选择敏感,不合适的初始值可能导致算法发散。
  2. 可能陷入局部最小值: 高斯牛顿法有时可能陷入局部最小值,特别是在存在多个局部最小值的问题中。

综合来看,选择哪种算法取决于问题的性质和实际需求。最速下降法简单但收敛速度慢,适用于简单问题或作为其他优化算法的初始步骤。牛顿法收敛速度快,但在计算复杂和非凸问题中可能不稳定。高斯牛顿法适用于非线性问题,但对初始值敏感,需要权衡利弊。在实际应用中,可能需要结合多种算法来优化不同的问题。

你可能感兴趣的:(视觉SLAM十四讲,算法,SLAM)