最小二乘问题详解5:非线性最小二乘求解实例

news/2025/10/16 20:13:59/文章来源:https://www.cnblogs.com/charlee44/p/19146529

1. 引言

在上一篇文章《最小二乘问题详解4:非线性最小二乘》中,介绍了非线性最小二乘问题的基本定义、求解思路及其核心算法Gauss-Newton方法,强调通过局部线性化将非线性问题转化为迭代的线性最小二乘子问题来求解。由于非线性最小二乘问题起来比线性最小二乘复杂多了,这里就通过一个拟合曲线\(y = \exp(a x^2 + b x + c)\)的实例来加深对非线性最小二乘问题的理解。

2. 雅可比矩阵

最麻烦的还是计算雅可比矩阵。设参数向量为:

\[\boldsymbol{\theta} = \begin{bmatrix} a \\ b \\ c \end{bmatrix} \]

模型函数为:

\[f(x; \boldsymbol{\theta}) = \exp(a x^2 + b x + c) \]

令中间变量:

\[u = a x^2 + b x + c \quad \Rightarrow \quad f = e^u \]

根据定义,雅可比矩阵是模型输出对参数的偏导形成的矩阵,即:

\[\mathbf{J}_{i,j} = \frac{\partial f(x_i; \boldsymbol{\theta})}{\partial \theta_j} \]

每一行是模型在第\(i\)个样本处对参数的梯度。计算每个偏导数的解析形式:

  1. 对 $ a $ 求偏导:

    \[\frac{\partial f}{\partial a} = \frac{\partial}{\partial a} e^{a x^2 + b x + c} = e^{a x^2 + b x + c} \cdot \frac{\partial}{\partial a}(a x^2 + b x + c) = e^{u} \cdot x^2 = f(x; \boldsymbol{\theta}) \cdot x^2 \]

  2. 对 $ b $ 求偏导:

    \[\frac{\partial f}{\partial b} = e^{u} \cdot \frac{\partial}{\partial b}(a x^2 + b x + c) = e^{u} \cdot x = f(x; \boldsymbol{\theta}) \cdot x \]

  3. 对 $ c $ 求偏导:

    \[\frac{\partial f}{\partial c} = e^{u} \cdot \frac{\partial}{\partial c}(a x^2 + b x + c) = e^{u} \cdot 1 = f(x; \boldsymbol{\theta}) \]

因此,对于 $ N $ 个数据点 $ x_1, x_2, \dots, x_N $,雅可比矩阵为:

\[\mathbf{J} = \begin{bmatrix} \frac{\partial f(x_1)}{\partial a} & \frac{\partial f(x_1)}{\partial b} & \frac{\partial f(x_1)}{\partial c} \\ \frac{\partial f(x_2)}{\partial a} & \frac{\partial f(x_2)}{\partial b} & \frac{\partial f(x_2)}{\partial c} \\ \vdots & \vdots & \vdots \\ \frac{\partial f(x_N)}{\partial a} & \frac{\partial f(x_N)}{\partial b} & \frac{\partial f(x_N)}{\partial c} \end{bmatrix}= \begin{bmatrix} f(x_1) \cdot x_1^2 & f(x_1) \cdot x_1 & f(x_1) \\ f(x_2) \cdot x_2^2 & f(x_2) \cdot x_2 & f(x_2) \\ \vdots & \vdots & \vdots \\ f(x_N) \cdot x_N^2 & f(x_N) \cdot x_N & f(x_N) \end{bmatrix} \]

或者写成:

\[\boxed{ \mathbf{J} = \begin{bmatrix} e^{a x_1^2 + b x_1 + c} \cdot x_1^2 & e^{a x_1^2 + b x_1 + c} \cdot x_1 & e^{a x_1^2 + b x_1 + c} \\ e^{a x_2^2 + b x_2 + c} \cdot x_2^2 & e^{a x_2^2 + b x_2 + c} \cdot x_2 & e^{a x_2^2 + b x_2 + c} \\ \vdots & \vdots & \vdots \\ e^{a x_N^2 + b x_N + c} \cdot x_N^2 & e^{a x_N^2 + b x_N + c} \cdot x_N & e^{a x_N^2 + b x_N + c} \end{bmatrix} } \]

3. 实例

其实要求解非线性最小二乘问题可以使用现成的库(比如Ceres Solver),不过本文主要为了理解非线性最小二乘的求解过程,尤其是Gauss-Newton方法。因此这个实例还是使用基础的矩阵库Eigen来实现,具体代码如下:

#include <Eigen/Dense>
#include <cmath>
#include <iostream>
#include <random>
#include <vector>using namespace std;
using namespace Eigen;// 模型函数: y = exp(a*x^2 + b*x + c)
double model(double x, const Vector3d& theta) {double a = theta(0);double b = theta(1);double c = theta(2);double exponent = a * x * x + b * x + c;// 防止溢出if (exponent > 300) return exp(300);if (exponent < -300) return exp(-300);return exp(exponent);
}// 计算 Jacobian 矩阵(数值导数或解析导数)
MatrixXd computeJacobian(const vector<double>& x_data,const vector<double>& y_data, const Vector3d& theta) {int N = x_data.size();MatrixXd J(N, 3);  // 每行对应一个点,三列:∂f/∂a, ∂f/∂b, ∂f/∂cfor (int i = 0; i < N; ++i) {double x = x_data[i];double exponent = theta(0) * x * x + theta(1) * x + theta(2);double f = model(x, theta);  // 当前预测值// 解析导数(链式法则)double df_de = f;  // d(exp(u))/du = exp(u)double de_da = x * x;double de_db = x;double de_dc = 1.0;J(i, 0) = df_de * de_da;  // ∂f/∂aJ(i, 1) = df_de * de_db;  // ∂f/∂bJ(i, 2) = df_de * de_dc;  // ∂f/∂c}return J;
}// 计算残差向量 r_i = y_i - f(x_i; theta)
VectorXd computeResiduals(const vector<double>& x_data,const vector<double>& y_data, const Vector3d& theta) {int N = x_data.size();VectorXd residuals(N);for (int i = 0; i < N; ++i) {double pred = model(x_data[i], theta);residuals(i) = y_data[i] - pred;}return residuals;
}int main() {// ========================// 1. 设置真实参数// ========================Vector3d true_params;true_params << 0.05, -0.4, 1.0;  // a, b, cdouble a_true = true_params(0), b_true = true_params(1),c_true = true_params(2);cout << "真实参数: a=" << a_true << ", b=" << b_true << ", c=" << c_true<< endl;// ========================// 2. 生成观测数据(带高斯噪声)// ========================int N = 50;vector<double> x_data(N), y_data(N);random_device rd;mt19937 gen(rd());uniform_real_distribution<double> x_dis(-5.0, 5.0);  // x 在 [-5, 5]normal_distribution<double> noise_gen(0.0, 0.1);     // 噪声 ~ N(0, 0.1)for (int i = 0; i < N; ++i) {x_data[i] = x_dis(gen);double y_true = model(x_data[i], true_params);y_data[i] = y_true + noise_gen(gen);  // 添加噪声}// ========================// 3. 初始化参数(随便猜)// ========================Vector3d theta = Vector3d::Zero();  // 初始猜测: a=0, b=0, c=0cout << "初始猜测: a=" << theta(0) << ", b=" << theta(1) << ", c=" << theta(2)<< endl;// ========================// 4. Gauss-Newton 迭代// ========================int max_iter = 50;double tol = 1e-6;double prev_cost = 0.0;cout << "\n开始 Gauss-Newton 迭代...\n";for (int iter = 0; iter < max_iter; ++iter) {// 计算残差 rVectorXd r = computeResiduals(x_data, y_data, theta);// 计算代价函数 ||r||^2double cost = r.squaredNorm();cout << "迭代 " << iter << ": 残差平方和 = " << cost << endl;// 收敛判断if (iter > 0 && abs(cost - prev_cost) < tol) {cout << "收敛!" << endl;break;}prev_cost = cost;// 计算 Jacobian 矩阵MatrixXd J = computeJacobian(x_data, y_data, theta);// 求解 Gauss-Newton 方程: (J^T J) Δ = J^T r// 使用 QR 分解求解(更稳定)VectorXd delta = J.colPivHouseholderQr().solve(r);// 更新参数: theta = theta + deltatheta += delta;// 可选:限制更新幅度防止发散// if (delta.norm() > 1.0) delta *= 1.0 / delta.norm();// 检查参数是否合理(防止溢出)if (!theta.allFinite()) {cout << "警告:参数发散!停止迭代。" << endl;break;}}// ========================// 5. 输出结果// ========================cout << "\n--- 拟合完成 ---" << endl;cout << "估计参数: a=" << theta(0) << ", b=" << theta(1) << ", c=" << theta(2)<< endl;cout << "真实参数: a=" << a_true << ", b=" << b_true << ", c=" << c_true<< endl;// 最终残差VectorXd final_r = computeResiduals(x_data, y_data, theta);cout << "最终残差平方和: " << final_r.squaredNorm() << endl;// ========================// 6. (可选)计算参数协方差与标准差// ========================MatrixXd J_final = computeJacobian(x_data, y_data, theta);int n = N, p = 3;double sigma_squared = final_r.squaredNorm() / (n - p);  // 估计噪声方差MatrixXd cov_theta =sigma_squared * (J_final.transpose() * J_final).inverse();Vector3d std_error;std_error << sqrt(cov_theta(0, 0)), sqrt(cov_theta(1, 1)),sqrt(cov_theta(2, 2));cout << "\n参数标准差 (近似):" << endl;cout << "a: ±" << std_error(0) << endl;cout << "b: ±" << std_error(1) << endl;cout << "c: ±" << std_error(2) << endl;return 0;
}

运行的结果如下:

真实参数: a=0.05, b=-0.4, c=1
初始猜测: a=0, b=0, c=0开始 Gauss-Newton 迭代...
迭代 0: 残差平方和 = 15595.7
迭代 1: 残差平方和 = 9.83388e+37
迭代 2: 残差平方和 = 1.33087e+37
迭代 3: 残差平方和 = 1.80114e+36
迭代 4: 残差平方和 = 2.43757e+35
迭代 5: 残差平方和 = 3.2989e+34
迭代 6: 残差平方和 = 4.46457e+33
迭代 7: 残差平方和 = 6.04214e+32
迭代 8: 残差平方和 = 8.17715e+31
迭代 9: 残差平方和 = 1.10666e+31
迭代 10: 残差平方和 = 1.4977e+30
迭代 11: 残差平方和 = 2.02691e+29
迭代 12: 残差平方和 = 2.74313e+28
迭代 13: 残差平方和 = 3.71242e+27
迭代 14: 残差平方和 = 5.02421e+26
迭代 15: 残差平方和 = 6.79954e+25
迭代 16: 残差平方和 = 9.20217e+24
迭代 17: 残差平方和 = 1.24538e+24
迭代 18: 残差平方和 = 1.68544e+23
迭代 19: 残差平方和 = 2.28099e+22
迭代 20: 残差平方和 = 3.08698e+21
迭代 21: 残差平方和 = 4.17778e+20
迭代 22: 残差平方和 = 5.65401e+19
迭代 23: 残差平方和 = 7.65187e+18
迭代 24: 残差平方和 = 1.03557e+18
迭代 25: 残差平方和 = 1.40149e+17
迭代 26: 残差平方和 = 1.89671e+16
迭代 27: 残差平方和 = 2.56691e+15
迭代 28: 残差平方和 = 3.47393e+14
迭代 29: 残差平方和 = 4.70143e+13
迭代 30: 残差平方和 = 6.36261e+12
迭代 31: 残差平方和 = 8.61052e+11
迭代 32: 残差平方和 = 1.16541e+11
迭代 33: 残差平方和 = 1.57677e+10
迭代 34: 残差平方和 = 2.13228e+09
迭代 35: 残差平方和 = 2.87975e+08
迭代 36: 残差平方和 = 3.87593e+07
迭代 37: 残差平方和 = 5.17234e+06
迭代 38: 残差平方和 = 677744
迭代 39: 残差平方和 = 86534.7
迭代 40: 残差平方和 = 10405.1
迭代 41: 残差平方和 = 543.309
迭代 42: 残差平方和 = 4.93922
迭代 43: 残差平方和 = 0.579241
迭代 44: 残差平方和 = 0.577034
迭代 45: 残差平方和 = 0.577034
收敛!--- 拟合完成 ---
估计参数: a=0.0498221, b=-0.400562, c=1.00071
真实参数: a=0.05, b=-0.4, c=1
最终残差平方和: 0.577034参数标准差 (近似):
a: ±0.00041604
b: ±0.00273643
c: ±0.00568421

需要注意的就是一下几点:

  1. Gauss-Newton方法所使用的迭代逼近过程,在代码中通常使用一个while/for循环来实现。因此初值选择停止条件特别重要,否则容易在循环过程中发散和震荡,导致不能实现收敛合适的求解值:

    for (int iter = 0; iter < max_iter; ++iter) {//...// 收敛判断if (iter > 0 && abs(cost - prev_cost) < tol) {cout << "收敛!" << endl;break;}//...// 更新参数: theta = theta + deltatheta += delta;
    }
    
  2. 初值选择不太容易,需要对求解问题的领域知识有一定的先验经验,或者通过使用近似的线性最小二乘问题的解作为初值。这里初值选择(0,0,0)因为是示例没有考虑太多的因素,实际应用中具体的问题需要具体分析。

  3. 常见的停止条件有三种:

    1. 参数变化很小:

    \[\boxed{\| \Delta \theta_k \| < \epsilon_1} \]

    1. 目标函数\(S(\theta) = \| \mathbf{r}(\theta) \|^2\)变化很小:

    \[\boxed{| S(\theta_{k+1}) - S(\theta_k) | < \epsilon_2} \]

    1. 梯度足够小。

    \[\boxed{\| J_k^T \mathbf{r}_k \| < \epsilon_3} \]

    目标函数的梯度是\(\nabla S(\theta) = 2 J(\theta)^T \mathbf{r}(\theta)\)\(J^T \mathbf{r}\) 正是正规方程的右边项。

  4. 关键在于雅可比矩阵的计算:

    // 计算 Jacobian 矩阵(数值导数或解析导数)
    MatrixXd computeJacobian(const vector<double>& x_data,const vector<double>& y_data, const Vector3d& theta) {int N = x_data.size();MatrixXd J(N, 3);  // 每行对应一个点,三列:∂f/∂a, ∂f/∂b, ∂f/∂cfor (int i = 0; i < N; ++i) {double x = x_data[i];double exponent = theta(0) * x * x + theta(1) * x + theta(2);double f = model(x, theta);  // 当前预测值// 解析导数(链式法则)double df_de = f;  // d(exp(u))/du = exp(u)double de_da = x * x;double de_db = x;double de_dc = 1.0;J(i, 0) = df_de * de_da;  // ∂f/∂aJ(i, 1) = df_de * de_db;  // ∂f/∂bJ(i, 2) = df_de * de_dc;  // ∂f/∂c}return J;
    }
    

    可以将这段代码与推导的雅可比矩阵进行对照。

  5. 这一段代码生成的随机值不一定总是能够收敛,因为有可能会遇到雅可比矩阵病态导致更新方向错误,初始猜测太差导致发散等问题。Gauss-Newton也理论上易于理解的方法,更加工程化的实践需要使用Levenberg-Marquardt算法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/938485.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【28】C# WinForm入门到精通 ——多文档窗体MDI【属性、强大的方法、实例、源码】【多窗口重叠、水平平铺、垂直平铺、窗体传值】

【28】C# WinForm入门到精通 ——多文档窗体MDI【属性、强大的方法、实例、源码】【多窗口重叠、水平平铺、垂直平铺、窗体传值】pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto…

第五周预习

20231313 张景云《密码系统设计》第五周预习AI对内容的总结 Windows.C.C++.加密解密实战.sm.ys 一、数字签名技术基础背景与意义:互联网信息安全问题突出,如网站篡改、仿冒页面等,数字签名技术作为基于密码学的安全…

2025 非标门/铸铝门/别墅大门厂家推荐榜:聚焦品质与服务的实力之选

在建筑建材领域,门作为空间防护与装饰的核心载体,其品质直接关系到居住安全与生活体验。市面上门业厂商众多,产品性能与服务质量参差不齐。基于技术实力、产品适配性、市场口碑等多维度考量,本文筛选出五家各具优势…

工业数字化未来:IT与OT融合实践

本文探讨数字技术在工业与运营环境中的实际应用,包括远程诊断系统、统一登录平台和跨设备协作解决方案,分析如何通过优化数字员工体验提升生产力与安全性,并介绍多家机构在IT与OT融合过程中的实践经验。工业运营工作…

AI安全新威胁:提示注入与模型中毒攻击深度解析

本文深入探讨AI安全领域两大新兴威胁:提示注入与模型中毒攻击。详细分析直接注入、间接注入、多模态注入等攻击技术,以及数据投毒、后门植入等模型威胁,为企业安全团队提供全面的防御策略和实战案例。提示注入与模型…

神经网络入门研读报告

神经网络入门研读报告:基于数据驱动的机器认知模型 神经网络是一种模拟生物神经系统信息处理机制的机器学习模型,核心功能是通过多层非线性变换,从结构化或非结构化数据中自动学习特征与规律,实现分类、预测等认知…

阅读《记录一类分治方法》笔记

目前只有题一。阅读《记录一类分治方法》笔记 题一 \(n\) 个点的无向图,边有边权。\(q\) 次操作,每次操作是添加/删除一条边、修改一条边的边权、查询最小生成树这四种之一。 前言 其实只看了题一,后面都还没看呢。…

CF2140E2

给定 \(n(1 \le n\le 20), m(1 \le m \le 10^6)\) 和一些 \(p_i\)。有 \(n\) 堆石子,每堆石子有 \(1 \sim m\) 个。两个人进行博弈,每次每个人可以取走第 \(p_i\) 堆石子(\(i\) 任选),然后剩下的石堆重新编号,直…

Codeforces 380E Sereja and Dividing 题解 [ 紫 ] [ 线段树 ] [ 贪心 ] [ 数学 ]

Sereja and Dividing:一年前的模拟赛就能秒这个 *2600 了,可我现在怎么还这么菜 /ll/ll/ll。 先考虑当杯子的集合固定时如何选择,显然一个杯子不会被选第二次,并且杯子从大到小选一定是最优的。证明只需要列出最后…

JPA教程

一 什么是 Spring Data JPA Spring Data JPA 是 Spring 框架的一个子项目,它简化了基于 JPA (Java Persistence API) 的数据访问层开发。它通过提供一套抽象接口,减少了开发者编写重复性的数据访问代码的工作量,使开…

实验指导-基于阿里云Serverless应用引l擎SAE的服务部署迀移 - 详解

实验指导-基于阿里云Serverless应用引l擎SAE的服务部署迀移 - 详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "…

夜莺监控设计思考(二)边缘机房架构思考

这将是一个系列,讲解 夜莺监控 的设计思考,可以理解为原理+最佳实践+产品设计时的折中取舍。 本系列其他文章:夜莺监控设计思考(一)项目定位、组件思考、单进程多进程选择、高可用设计下面开始第2篇。 上一篇我们…

搜维尔科技:具有人手级别抓握和操纵能力的灵巧手

应用 仿人机器人研究;涉及工具使用、双手操作的任务;需要手动操作的过程,如物体组装和连接器锁紧! 具有人手级别抓握和操纵能力的灵巧手 5指20自由度(DOF)Gripper与人手一样通用,可广泛应用。 专为高级人形机器人研…

v-model 的实现原理

vue 中 v-model 可以实现数据的双向绑定,但是为什么这个指令就可以实现数据的双向绑定呢? 其实 v-model 是 vue 的一个语法糖。即利用 v-model 绑定数据后,既绑定了数据,又添加了一个 input 事件监听。 实现原理:…

防塔游戏单机 王国保卫战全集下载 1~5部全系列MOD DLC修改版 安卓+ios+PC电脑版

王国保卫战全集下载 1~5部全系列MOD DLC修改版 安卓+ios+PC电脑版 《王国保卫战》中玩家需在地图上建造防御塔来抵御兽人、巨魔、恶魔等敌人的进攻。游戏设有森林、山野、荒地等多种战斗场景…

详细介绍:【译】Visual Studio 中针对 .NET MAUI 的 XAML 实时预览功能的增强

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

德州东站换乘攻略(仅供参考)

注意(免责声明):本文仅在极限情况下参考。本文仅给出参考,若发生任何与此有关的事情,本文作者不承担任何责任。 作者所携带的行李为 \(26\) 英寸拉杆箱,重约 \(9 kg\)。测试时作者为高中学生。德州东站换乘攻略:…

第十六篇

今天是10月16日今天上了数据结构,进行体测。

Date 2025.10.6

在 Print 之前 这场比赛打的不好,T1 没判边界挂了 \(50 pts\),T2、T3 都只打了 \(60 pts\),总的来说不算太好。 A - 玩具P.S. \(n \le 100000 \quad t \le 100000 \quad w_{i} \le 100 \quad k_{i} \le 100\) 说真的…

macOS 双开/多开微信WeChat完整教程(支持 4.X 及以上版本) - 实践

macOS 双开/多开微信WeChat完整教程(支持 4.X 及以上版本) - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: &quo…