最小二乘问题详解6:梯度下降法

news/2025/10/28 21:28:25/文章来源:https://www.cnblogs.com/charlee44/p/19172778

1. 引言

在之前的两篇文章《最小二乘问题详解4:非线性最小二乘》、《最小二乘问题详解5:非线性最小二乘求解实例》中,笔者介绍了非线性最小二乘问题,并使用Gauss-Newton方法来进行求解。不过,求解非线性最小二乘问题还有另外一种方法——梯度下降法。

2. 背景

梯度下降法在人工智能的机器学习中使用的非常多,因为机器学习的训练过程通常被形式化为经验风险最小化问题(Empirical Risk Minimization, ERM):即在训练数据上最小化损失函数。而最小二乘问题其实也是经验风险最小化问题的一种,甚至机器学习的某些任务(比如回归)本身就是最小二乘问题。经验风险最小化问题是一种通用的函数拟合框架,不过损失函数有所不同,通常使用梯度下降法来进行求解。

那么为什么机器学习中使用梯度下降法来求解,而计算机视觉(SLAM、SfM、相机标定、BA)中使用Gauss-Newton/Levenberg-Marquardt来进行求解呢?这是因为机器学习的问题可以只用关心局部的“好解”,而不用像计算机视觉问题那样需要求解全局的“精确最小值”;另外,机器学习问题规模巨大、结构复杂,使用梯度下降法要简单、健壮、高效的多。

3. 求解

接下来就来介绍一下使用梯度下降法求解非线性最小二乘问题。还是先看非线性最小二乘问题的定义:

\[\min_{\theta} S(\theta) = \ \mathbf{r}(\theta)\ ^2 = \sum_{i=1}^m r_i(\theta)^2 \]

其中:
\(\theta \in \mathbb{R}^n\):待优化的参数向量(比如曲线的系数)
\(\mathbf{r}(\theta) = \begin{bmatrix} r_1(\theta) \\ \vdots \\ r_m(\theta) \end{bmatrix}\):残差向量,\(r_i(\theta) = y_i - f(x_i; \theta)\)
\(S(\theta)\):目标函数(损失函数),是我们要最小化的残差平方和

梯度下降法的核心思想是:在当前点,沿着目标函数下降最快的方向走一步,然后重复。而这个“最快下降方向”就是负梯度方向\(-\nabla S(\theta)\)。因此问题的关键在于计算目标函数\(S(\theta) = \ \mathbf{r}(\theta)\ ^2\)的梯度。根据求导的链式法则:

\[\nabla S(\theta) = \frac{\partial}{\partial \theta} \left( \mathbf{r}(\theta)^T \mathbf{r}(\theta) \right) = 2 \, J(\theta)^T \mathbf{r}(\theta) \]

其中:
\(J(\theta)\):雅可比矩阵(Jacobian),大小为 \(m \times n\)

\[J(\theta) = \begin{bmatrix} \frac{\partial r_1}{\partial \theta_1} & \cdots & \frac{\partial r_1}{\partial \theta_n} \\ \vdots & \ddots & \vdots \\ \frac{\partial r_m}{\partial \theta_1} & \cdots & \frac{\partial r_m}{\partial \theta_n} \end{bmatrix} = \frac{\partial \mathbf{r}}{\partial \theta^T} \]

即目标函数的梯度是:\(\nabla S(\theta) = 2 J(\theta)^T \mathbf{r}(\theta)\)

另一方面,在每次梯度下降之后,需要更新参数向量:

\[\boxed{ \theta_{k+1} = \theta_k - \alpha \cdot \nabla S(\theta_k) = \theta_k - 2\alpha \cdot J_k^T \mathbf{r}_k } \]

其中:
\(\theta_k\):第 \(k\) 次迭代的参数
\(\alpha > 0\):学习率(step size),控制步长
\(J_k = J(\theta_k)\)\(\mathbf{r}_k = \mathbf{r}(\theta_k)\)

因此,将梯度下降方法完整的流程总结如下:

  1. 初始化:选一个初始猜测 θ₀
  2. 设置学习率 α(例如 0.01)
  3. 对 k = 0, 1, 2, ... 直到收敛:
    a. 计算残差:\(r_k = y - f(x; θ_k)\)
    b. 计算雅可比矩阵:\(J_k = J(θ_k)\)
    c. 计算梯度:\(g_k = 2 J_k^T r_k\)
    d. 更新参数:\(θ_{k+1} = θ_k - α g_k\)
    e. 检查是否收敛:\(Δθ = θ_{k+1} - θ_k < ε\)\(g_k < ε\)\(S(θ)\)变化很小
  4. 输出最终参数 θ

4. 实例

从上述求解过程可以看到,梯度下降法其实比之前文章中介绍的Gauss-Newton方法要简单很多,那么这里还是给出一个只使用Eigen实现梯度下降法求解非线性最小二乘问题的例子。例子中模型函数为\(f(x; \boldsymbol{\theta}) = a e ^{bx}\)

#include <Eigen/Dense>
#include <cmath>
#include <iostream>
#include <random>
#include <vector>using namespace std;
using namespace Eigen;// 模型函数: y = a * exp(b * x)
double model(double x, const Vector2d& theta) {double a = theta(0);double b = theta(1);return a * exp(b * x);
}// 计算残差: r_i = y_i - f(x_i; a, b)
VectorXd computeResiduals(const vector<double>& x_data,const vector<double>& y_data, const Vector2d& theta) {int N = x_data.size();VectorXd r(N);for (int i = 0; i < N; ++i) {r(i) = y_data[i] - model(x_data[i], theta);}return r;
}// 计算 Jacobian 矩阵 (N x 2): ∂r_i/∂a, ∂r_i/∂b
MatrixXd computeJacobian(const vector<double>& x_data, const Vector2d& theta) {int N = x_data.size();MatrixXd J(N, 2);double a = theta(0);double b = theta(1);for (int i = 0; i < N; ++i) {double x = x_data[i];double exp_bx = exp(b * x);  // exp(b*x)J(i, 0) = -exp_bx;          // ∂r/∂a = -exp(b*x)J(i, 1) = -a * exp_bx * x;  // ∂r/∂b = -a * exp(b*x) * x}return J;
}int main() {// ========================// 1. 真实参数// ========================Vector2d true_params;true_params << 2.0, -0.3;  // a=2.0, b=-0.3 → y = 2 * exp(-0.3 * x)cout << "真实参数: a = " << true_params(0) << ", b = " << true_params(1)<< endl;// ========================// 2. 生成带噪声的数据// ========================int N = 20;vector<double> x_data(N), y_data(N);random_device rd;mt19937 gen(rd());normal_distribution<double> noise(0.0, 0.05);  // 小噪声for (int i = 0; i < N; ++i) {x_data[i] = -2.0 + i * 0.4;  // x 从 -2 到 6double y_true = model(x_data[i], true_params);y_data[i] = y_true + noise(gen);}// ========================// 3. 初始化参数// ========================Vector2d theta;theta << 1.0, 0.0;  // 初始猜测: a=1.0, b=0.0cout << "初始猜测: a = " << theta(0) << ", b = " << theta(1) << endl;// ========================// 4. 梯度下降法// ========================int max_iter = 500;double alpha = 5e-3;  // 学习率double tol = 1e-6;cout << "\n开始梯度下降...\n";cout << "迭代\t残差平方和\t\t参数 a\t\t参数 b\n";cout << "----\t----------\t\t------\t\t------\n";for (int iter = 0; iter < max_iter; ++iter) {// 计算残差VectorXd r = computeResiduals(x_data, y_data, theta);double cost = r.squaredNorm();// 计算梯度MatrixXd J = computeJacobian(x_data, theta);Vector2d gradient = 2.0 * J.transpose() * r;// 打印当前状态(每10次)if (iter % 10 == 0) {cout << iter << "\t" << cost << "\t\t" << theta(0) << "\t\t" << theta(1)<< endl;}// 终止条件if (gradient.norm() < tol) {cout << "收敛!梯度范数: " << gradient.norm() << endl;break;}// 更新参数theta -= alpha * gradient;}// ========================// 5. 输出结果// ========================cout << "\n--- 拟合完成 ---" << endl;cout << "估计参数: a = " << theta(0) << ", b = " << theta(1) << endl;cout << "真实参数: a = " << true_params(0) << ", b = " << true_params(1)<< endl;return 0;
}

运行结果如下:

真实参数: a = 2, b = -0.3
初始猜测: a = 1, b = 0开始梯度下降...
迭代    残差平方和              参数 a          参数 b
----    ----------              ------          ------
0       22.7591         1               0
10      1.11435         1.72284         -0.345
20      0.100641                1.93634         -0.301778
30      0.0326195               1.99193         -0.294493
40      0.0286004               2.00545         -0.292882
50      0.0283681               2.0087          -0.292503
60      0.0283548               2.00948         -0.292413
70      0.028354                2.00967         -0.292391
80      0.0283539               2.00971         -0.292386
90      0.0283539               2.00972         -0.292385
100     0.0283539               2.00972         -0.292384
110     0.0283539               2.00973         -0.292384
120     0.0283539               2.00973         -0.292384
收敛!梯度范数: 9.36104e-07--- 拟合完成 ---
估计参数: a = 2.00973, b = -0.292384
真实参数: a = 2, b = -0.3

求解的关键还是在于计算雅可比矩阵,对于问题模型函数\(f(x; \boldsymbol{\theta}) = a e ^{bx}\)来说,雅可比矩阵应该是:

\[J(\theta) = \begin{bmatrix} \frac{\partial (y_1-ae^{bx_1})}{\partial a} & \frac{\partial (y_1-ae^{bx_1})}{\partial b} \\ \frac{\partial (y_2-ae^{bx_2})}{\partial a} & \frac{\partial (y_2-ae^{bx_2})}{\partial b} \\ \vdots & \vdots \\ \frac{\partial (y_m-ae^{bx_m})}{\partial a} & \frac{\partial (y_m-ae^{bx_m})}{\partial b} \\ \end{bmatrix}= -\begin{bmatrix} e^{bx_1} & ae^{bx_1}x_1 \\ e^{bx_2} & ae^{bx_2}x_2 \\ \vdots & \vdots \\ e^{bx_m} & ae^{bx_m}x_m \\ \end{bmatrix} \]

对比代码中的实现:

// 计算 Jacobian 矩阵 (N x 2): ∂r_i/∂a, ∂r_i/∂b
MatrixXd computeJacobian(const vector<double>& x_data, const Vector2d& theta) {int N = x_data.size();MatrixXd J(N, 2);double a = theta(0);double b = theta(1);for (int i = 0; i < N; ++i) {double x = x_data[i];double exp_bx = exp(b * x);  // exp(b*x)J(i, 0) = -exp_bx;          // ∂r/∂a = -exp(b*x)J(i, 1) = -a * exp_bx * x;  // ∂r/∂b = -a * exp(b*x) * x}return J;
}

另外,除了迭代过程中的初始条件和迭代停止条件,控制步长的学习率也需要注意。设置的学习率过小,迭代次数就会很长导致收敛很慢;而设置的学习率过大,就容易略过最优解导致结果不问题。因此,在实际的工程应用中,通常不会使用原始的梯度下降法,而是根据需求使用不同优化版本的梯度下降法。关于这一点,有机会的话会在后续的文章中进一步论述。

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

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

相关文章

JavaWeb01

1.JavaWeb介绍 什么是JavaWeb?Web:全球广域网,也称万维网(www),能够通过浏览器访问的网站 JavaWeb:使用Java技术来解决相关web互联网领域的技术栈网页:展现数据 数据库:存储和管理数据 JavaWeb程序:逻辑处理数…

现代C++编程初体验

##实验任务1 ##代码#pragma once#include <string>// 类T: 声明 class T { // 对象属性、方法 public:T(int x = 0, int y = 0); // 普通构造函数T(const T &t); // 复制构造函数T(T &&t); …

Delphi 利用接口实现frame窗体间的通讯(互动)

需求说明: 程序设计:效果演示:设计思路: FrmCK 只负责发布事件,不关心谁在监听. FrmGrid 只负责响应事件,不关心事件来源. 创建过程: 一.创建接口单元FrmInterface. 全部代码如下:unit FrmInterface;interfaceusessy…

Python冒泡排序:简单易懂的算法实现

在编程的世界里,排序算法是数据处理的基础之一。冒泡排序(Bubble Sort)是一种简单且直观的排序算法,虽然它的效率不是最高的,但它非常适合初学者学习排序算法的基本概念。今天,我们就来详细探讨如何在Python中实…

SAM+ARM

一、首先是图像caption的生成。 输入的图像,被输入进BLIP的图像编码器得到图像嵌入,图像嵌入再经过(BLIP Image-grounded Text Decoder) 得到图像caption。ti表示caption的第i个单词,总共有L个单词。 但是,capti…

《代码大全2》观后感(二):需求分析——代码质量的“源头防线”

《代码大全2》观后感(二):需求分析——代码质量的“源头防线” “为什么明明按需求写的代码,最后还是要推翻重写?”这是我过去常有的困惑,直到读了《代码大全2》中“需求分析”的章节,才找到答案:很多时候,我…

NRF54LM20A 芯片的优点

多达 66 个 GPIO 7 个串行接口(SPI、TWI、UART、HS-SPI) 14 位 ADC、全局 RTC(在系统关闭状态下可用)、TDM、PDM、NFC、PWM、QDEC 等 显著降低的功耗 - 与 nRF52 系列相比,典型蓝牙低功耗应用场景下功耗降低约 30-50% …

零散点小总结(25.10.28)

今天练习了Dp,主要把Dp重新看待了一下,有以下几点Dp其实本质是一种表,用于储存子问题的答案 Dp中其实还有枚举,只是由于子问题被存入表中了,所以减少了时间复杂度 一个搜索其实就是Dp的暴力解,有很多的子问题,但…

Top Tree大学习

前言 \(Top Tree\) 用来解决 路径查询,动态 \(dp\) 等问题。 信息储存在 簇 中。 簇(\(Cluster\)) 树上一个边联通块,可以收缩成一条边,我们成这样的联通子图为 簇。 簇上的某些点与其它簇相接,我们称其为簇的 端…

乱学点东西目录

这里记录了各种各样的奇奇怪怪的算法/思路/数据结构,好玩! 乱学点东西#1 :二进制警报器可以自由转载

CFS任务的负载均衡(load balance)

前言 我们描述CFS任务负载均衡的系列文章一共三篇,第一篇是框架部分,第二篇描述了task placement和active upmigration两个典型的负载均衡场景。本文是第三篇,主要是分析各种负载均衡的触发和具体的均衡逻辑过程。 …

EVE-NG导入华为等镜像的方法

镜像下载Dynamips:思科设备真实IOS镜像,类似GNS3,电脑CPU利用率非常高。 IOL:IOU模拟器的镜像,基本完全支持思科设备二、三层功能。 QEMU:这已经不是镜像文件,而是KVM虚拟机安装操作系统后生成的磁盘文件,通常…

(简记)一类支配点对解决区间查询问题

前言:最近好像见了挺多这种题,记录一下。 支配点对 我们经常遇到树上或区间上关于 \(x,y\in[l,r]\) 一类的区间统计问题,且通常要求区间内点两两任意匹配并统计总贡献,这个贡献不具有简单可加性。我们往往通过找支…

2025 云斗

10/27 Contest 5 A:小分讨+dp C:发现是所有的数和它的倍数有限制,对于值域 \(n\) 这样的限制也只有 \(\sum\limits_{i=1}^n\frac{n}{i}=n\log n\) 个,考虑如何表示这些限制。 考虑对于限制 u,v,若两点都不是对方的…

c++ ranges随笔

ranges c++20引入,在<ranges>头文件中 建立在 std::algo 和 iterator基础上,并做了进一步的抽象集成 与之前相比更加的 安全、简洁、方便 // ranges concept template <typename T> concept range = req…

qoj14458. 调色滤镜

qoj14458. 调色滤镜 平面 \([1,10^9]\times[1,10^9]\) 上有 \(n\) 个点,点 \(i\) 位于 \((x,y)\),有颜色 \(c_i\in [0,9]\)。 有 \(q\) 次操作,每次对平面上一个矩形范围内的点的颜色作用映射 \(f:[0,9]\rightarrow…

第8天(中等题 不定长滑动窗口、哈希表)

打卡第八天 3道中等题滑动窗口相当于在维护一个队列。右指针的移动可以视作入队,左指针的移动可以视作出队。 熟练度+++ 可以十几分钟独立写出相似题了^O^/ 耗时≈一小时 明天继续

P10259 [COCI 2023/2024 #5] Piratski kod

题链 题意 首先,题目写的很抽象,模拟赛时读了半个小时才读懂 题意概括一下就是枚举长度为k的所有01串 然后对01串进行划分,每遇到两个1就进行一次划分 然后把每段提取出来单独处理 如果把提出来的01串计为\(s[1...r]\)…

巧用 using 作用域(IDisposable)的生命周期包装特性 实现前后置处理

需求:在多个方法前后输出日志 logger.Info("begin"); method(); logger.Info("end");如果需要在方法后输出日志同时加上时长 logger.Info("begin"); var sw= Stopwatch.StartNew(); me…

2025.10.27训练记录

其实是10.28晚上写的。感觉就这个题要记录一下。 上午noip模拟。喜提一道不会。 B 题外话: 7:45 开始考试,广附集训爷大吼一声我做过!声称A完全不可做,但B他场切了。于是我开场看B。 那就看B,7:50闭了一下眼睛,睁…