文章目录
- 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)=21∥f(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=0⇒HΔ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 21∥f(x)+J(x)TΔx∥2 最小。将其展开
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} 21∥f(x)+J(x)TΔx∥2=21(f(x)+J(x)TΔx)T(f(x)+J(x)TΔx)=21(∥f(x∥2+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) w∼N(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=1∑N∥yi−exp(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=yi−exp(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=[∂a∂F,∂b∂F,∂c∂F]⊤。
∂ 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)) ∂a∂F=ei(−xi2exp(axi2+bxi+c))∂a∂F=ei(−xiexp(axi2+bxi+c))∂a∂F=ei(exp(axi2+bxi+c))
/*********************************************************** *
* Time: 2023/8/10
* Author: xiaocong
* Function: 最速下降法
* 注意这里用的是 ae * xi * xi + be * xi + sin(ce)
* 原指数函数对误差和初值敏感,造成计算无穷大。
***********************************************************/#include <iostream>
#include <eigen3/Eigen/Core>
#include <eigen3/Eigen/Dense>
#include <cmath>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; // 求解 dxif (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= ∂a2∂2F∂b∂a∂2F∂c∂a∂2F∂a∂b∂2F∂b2∂2F∂c∂b∂2F∂a∂c∂2F∂b∂c∂2F∂c2∂2F
则 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 <iostream>
#include <eigen3/Eigen/Core>
#include <eigen3/Eigen/Dense>
#include <cmath>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); // 求解 dxif (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) ∂a∂ei=−xi2exp(axi2+bix+c)∂b∂ei=−xiexp(axi2+bix+c)∂c∂ei=−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=[∂a∂ei,∂b∂ei,∂c∂ei]⊤。和前面不一样。
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=1∑100JiJi⊤)Δx=−i=1∑100Jiei
/*********************************************************** *
* Time: 2023/8/10
* Author: xiaocong
* Function: 高斯牛顿法
***********************************************************/#include <iostream>
#include <eigen3/Eigen/Core>
#include <eigen3/Eigen/Dense>
#include <cmath>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^TVector3d b = Vector3d::Zero(); // b=-Jecost = 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;}// 求解 dxVector3d 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 三种方法优缺点
最速下降法、牛顿法和高斯牛顿法都是常用的优化算法,用于解决最小化问题。它们各自有不同的优缺点,下面是对这三种算法的优缺点进行总结:
最速下降法:
优点:
- 简单直观: 最速下降法的核心思想简单易懂,容易实现。
- 适用广泛: 适用于一般的优化问题,不需要太多的先验知识。
- 内存消耗小: 只需要存储当前参数和梯度,内存消耗相对较小。
缺点:
- 收敛速度慢: 最速下降法的收敛速度通常较慢,特别是在存在长轴状谷底或细长山谷的情况下。
- 依赖初始值: 初始参数的选择会影响收敛性能,不合适的初始值可能导致陷入局部最小值。
- 不适用于高维问题: 在高维问题中,计算梯度和参数更新可能变得耗时,从而影响效率。
牛顿法:
优点:
- 收敛速度快: 牛顿法在逼近局部最小值时的收敛速度通常比最速下降法更快。
- 二阶信息利用: 牛顿法利用了函数的二阶导数信息,可以更准确地估计局部几何特性。
缺点:
- 计算复杂: 牛顿法需要计算函数的二阶导数(Hessian矩阵),计算成本较高,特别是在高维问题中。
- 不稳定性: Hessian矩阵可能不是正定的,导致算法不稳定。此外,在某些情况下,牛顿法可能在非凸问题中陷入局部最小值。
高斯牛顿法:
优点:
- 适用于非线性问题: 高斯牛顿法适用于非线性优化问题,尤其是拟合参数化模型时效果较好。
- 不需要二阶导数: 高斯牛顿法不需要直接计算Hessian矩阵,而是通过近似方法来计算。
缺点:
- 对初始值敏感: 高斯牛顿法对初始参数的选择敏感,不合适的初始值可能导致算法发散。
- 可能陷入局部最小值: 高斯牛顿法有时可能陷入局部最小值,特别是在存在多个局部最小值的问题中。
综合来看,选择哪种算法取决于问题的性质和实际需求。最速下降法简单但收敛速度慢,适用于简单问题或作为其他优化算法的初始步骤。牛顿法收敛速度快,但在计算复杂和非凸问题中可能不稳定。高斯牛顿法适用于非线性问题,但对初始值敏感,需要权衡利弊。在实际应用中,可能需要结合多种算法来优化不同的问题。