news 2026/4/16 16:17:28

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
最小二乘问题详解6:梯度下降法

4. 实例

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

f

(

x

;

θ

)

=

a

e

b

x

#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 到 6

double 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.0

cout << "初始猜测: 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

;

θ

)

=

a

e

b

x

来说,雅可比矩阵应该是:

J

(

θ

)

=

(

y

1

a

e

b

x

1

)

a

(

y

1

a

e

b

x

1

)

b

(

y

2

a

e

b

x

2

)

a

(

y

2

a

e

b

x

2

)

b

(

y

m

a

e

b

x

m

)

a

(

y

m

a

e

b

x

m

)

b

=

e

b

x

1

a

e

b

x

1

x

1

e

b

x

2

a

e

b

x

2

x

2

e

b

x

m

a

e

b

x

m

x

m

对比代码中的实现:

// 计算 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;

}

另外,除了迭代过程中的初始条件和迭代停止条件,控制步长的学习率也需要注意。设置的学习率过小,迭代次数就会很长导致收敛很慢;而设置的学习率过大,就容易

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 15:24:25

Yii2-Swoole 快速入门

Yii2-Swoole 快速入门让你的 Yii2 应用性能提升 10-100 倍&#xff01;本教程将教你如何在 yii2-app-basic 中快速集成 yii2-swoole。为什么使用 yii2-swoole&#xff1f;⚡ 比 PHP-FPM 快 10-100 倍&#x1f504; 数据库和 Redis 连接池自动管理&#x1f680; 协程并发处理请求…

作者头像 李华
网站建设 2026/4/16 12:16:22

计算机Java毕设实战-基于springboot的养宠物指南服务平台系统的设计与实现基于Java SpringBoot的宠物社区互动平台的设计与【完整源码+LW+部署说明+演示视频,全bao一条龙等】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/4/15 15:35:42

Java毕设选题推荐:基于SpringBoot的演唱会售票系统设计与实现基于springboot的演唱会购票系统的设计与实现【附源码、mysql、文档、调试+代码讲解+全bao等】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/4/15 17:56:25

零极点对消:原理、作用与风险

这是一个自动控制原理中的核心概念。我们来详细解释一下零极点对消的原理、作用以及需要注意的关键点。一、原理从数学本质上讲&#xff0c;零极点对消就是在系统传递函数的分子和分母中&#xff0c;消除具有相同数值的零点和极点。传递函数&#xff1a;线性时不变系统通常用传…

作者头像 李华
网站建设 2026/4/16 0:29:12

计算机Java毕设实战-基于springboot6+vue的校园智能物流包裹领取寄送管理系统的设计与实现【完整源码+LW+部署说明+演示视频,全bao一条龙等】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/4/15 12:02:09

22、oracle导入数据,sys_user表数据错误

项目场景&#xff1a; oracle导入数据&#xff0c;sys_user表数据错误问题描述原因分析&#xff1a; 熊小二 使用oracle客户端工具&#xff0c;导入数据&#xff0c;导入成功之后&#xff0c;发现有几张表的数据错误&#xff0c;当时也纳闷&#xff0c;为什么界面的菜单显示不对…

作者头像 李华