ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

梯度下降法Matlab程序及利用Excel做详细说明:从一行公式到能跑通的代码

梯度下降法Matlab程序及利用Excel做详细说明:从一行公式到能跑通的代码

简介:这份资源围绕梯度下降法这一数据科学中最常用的优化算法展开,面向机器学习入门者与需要动手实现优化算法的同学,用住宅价格预测作为案例,帮助读者从原理到代码建立直观理解。压缩包共4个文件,约221KB,包含Matlab程序源文件(.m)、配套数据文件(.mat)、原理讲解PDF以及一张示意图,其中脚本可直接运行复现实验,PDF则用Excel表格逐步演示梯度下降的迭代过程,把抽象的公式转化为可观察的数值变化。资源已有270人学习下载,适合课堂实验、自学练手或作为教学演示素材。读者可借此掌握梯度下降的更新规则、学习率影响与收敛判断,并对照Excel推导过程排查代码实现中的常见问题,形成从理论到编程的完整闭环。

1. 梯度下降法Matlab程序及利用excel做详细说明:从一行公式到能跑通的代码

很多做算法落地的朋友第一次接触优化算法,都是从梯度下降法开始的。标题里这个「梯度下降法Matlab程序及利用excel做详细说明」,说白了就是两件事:一是用Matlab把梯度下降的迭代过程写成能跑的程序,二是用Excel把每一步的数值变化摊开给你看,让你知道程序里那个循环到底在算什么。我见过太多人Matlab代码能跑,但问他第3次迭代的梯度是多少、学习率调大调小会发生什么,答不上来。Excel在这里不是配角,它是把黑匣子拆开的工具。这篇适合刚入门数值优化、需要交课程设计、或者想给团队做内部培训的工程师,跟着走一遍,你能自己改目标函数、调学习率、看收敛曲线,也能把Excel表格当成调试面板用。

2. 梯度下降法的数学骨架与Matlab实现选型

2.1 从一元二次函数看迭代公式到底在做什么

梯度下降法的核心就一句话:沿着当前点梯度的反方向走一小步,步子大小由学习率决定。用数学写出来是 θ_{k+1} = θ_k - α * ∇J(θ_k)。别小看这个式子,里面有三个东西决定了你能不能收敛:初始点θ_0、学习率α、梯度∇J。我一般拿一个最简单的目标函数 J(θ) = θ^2 + 2θ + 1 来演示,它的梯度是 2θ + 2,最小值在 θ = -1 处。为什么选这个?因为它的梯度是线性的,Excel里手算几步就能看出规律,Matlab里跑出来也不会因为数值问题干扰你对算法本身的理解。

在Matlab里实现时,常见做法是把目标函数和梯度函数分开写成两个匿名函数或者独立的.m文件。匿名函数适合快速验证,独立函数适合复杂场景。我一般会先写匿名函数版本,因为改起来快,不用来回切文件。下面这段代码就是最小可运行版本,目标函数和梯度都写在一起,迭代过程全部记录下来,方便后面和Excel对账。

% 梯度下降法最小示例:J(theta) = theta^2 + 2*theta + 1 % 梯度 dJ/dtheta = 2*theta + 2 clear; clc; J = @(theta) theta.^2 + 2*theta + 1; % 目标函数 gradJ = @(theta) 2*theta + 2; % 梯度函数 alpha = 0.1; % 学习率 theta = 3.0; % 初始点 maxIter = 20; % 最大迭代次数 tol = 1e-6; % 收敛容差 history = zeros(maxIter, 4); % 记录 [迭代次数, theta, J值, 梯度值] for k = 1:maxIter g = gradJ(theta); jVal = J(theta); history(k, :) = [k, theta, jVal, g]; if abs(g) < tol history = history(1:k, :); fprintf('在第 %d 次迭代收敛,theta = %.6f\n', k, theta); break; end theta = theta - alpha * g; % 核心更新步骤 end % 打印迭代记录 fprintf('迭代次数\t theta\t\t J值\t\t 梯度\n'); for i = 1:size(history, 1) fprintf('%d\t\t %.6f\t %.6f\t %.6f\n', history(i,1), history(i,2), history(i,3), history(i,4)); end

这段代码里,history矩阵是给Excel做对照用的,每一行对应一次迭代。alpha设成0.1是因为对于这个函数,学习率超过1就会发散,0.1能稳定收敛且步数适中。tol设1e-6是工程上常用的精度,再小意义不大,浮点误差会干扰判断。maxIter设20是因为这个函数从3.0出发,0.1的学习率大约15步就能到-1附近。你如果换成alpha=0.5,收敛会更快但可能震荡;换成alpha=1.5,直接发散,theta会飞到无穷大。这些边界后面在Excel里会看得更清楚。

2.2 学习率、初始点、迭代终止条件怎么选

学习率α是梯度下降法里最玄学的参数。选大了震荡甚至发散,选小了收敛慢到你想砸键盘。对于上面那个二次函数,理论上的稳定条件是 α < 2/L,其中L是梯度的最大变化率,这里L=2,所以α必须小于1。我一般会先用0.1试,然后根据收敛曲线调整。如果你不确定,有个土办法:从0.01开始,每次乘以3,看什么时候开始震荡,然后取那个值的三分之一。

初始点θ_0的选择也有讲究。对于凸函数,初始点随便选都能收敛到全局最小,但步数会差很多。从3.0出发和从100出发,前者十几步,后者可能要几百步。实际工程里如果目标函数不是凸的,初始点就决定了你最终掉进哪个局部最小。我一般会跑多次随机初始化,取目标函数值最小的那个结果。

迭代终止条件通常用梯度范数小于某个阈值,或者相邻两次目标函数值的变化小于阈值。前者更直接,后者更实用。Matlab里还可以用optimset设置MaxIter和TolFun,但自己写循环的好处是每一步都能记录,方便和Excel对照。如果你用Matlab自带的fminunc,它内部用的不是纯梯度下降,而是拟牛顿法,收敛更快但中间过程不透明。做教学和调试时,我坚持手写循环。

2.3 向量化写法与逐元素写法的取舍

上面的例子是一维的,实际数据往往是多维的。Matlab的强项是矩阵运算,梯度下降的向量化写法能把循环压成一行。假设目标函数是 J(θ) = (1/2) * ||Xθ - y||^2,梯度是 X^T(Xθ - y)。向量化写法如下:

% 多元线性回归的梯度下降向量化实现 % X: m x n 特征矩阵,y: m x 1 标签向量 % theta: n x 1 参数向量 m = size(X, 1); alpha = 0.01; theta = zeros(size(X, 2), 1); maxIter = 1000; for k = 1:maxIter grad = (1/m) * X' * (X * theta - y); % 梯度向量 theta = theta - alpha * grad; % 同时更新所有参数 end

这里除以m是为了让梯度不受样本量影响,学习率可以设得稳定一些。向量化写法比逐元素循环快几十倍,尤其是特征维度高的时候。但向量化也有坑:X' * (X * theta - y) 这个表达式会临时生成一个m x 1的中间向量,如果m是百万级,内存会爆。这时候要分块计算,或者用Matlab的tall array。我一般会在数据量小于10万时用向量化,超过就分块。

3. 用Excel把每一次迭代摊开:从公式到表格的完整映射

3.1 搭建Excel迭代表:列设计与公式填写

Excel在这里的作用是把Matlab循环里的每一步都可视化。我一般建四列:迭代次数k、当前θ值、目标函数J(θ)、梯度∇J(θ)。第一行填初始值,第二行开始用公式引用上一行的结果。具体操作:A2填1,B2填3(初始θ),C2填 =B2^2+2B2+1,D2填 =2B2+2。然后A3填 =A2+1,B3填 =B2-0.1*D2,C3和D3分别复制C2和D2的公式往下拉。这样你改B2的初始值或者把0.1改成其他学习率,整张表会自动重算。

这里的关键是B3的公式:=B2-0.1D2。0.1就是学习率α,D2是当前梯度。这个公式和Matlab里的 theta = theta - alpha * g 完全对应。你可以在Excel里单独设一个单元格放学习率,比如E1填0.1,然后B3写成 =B2-$E$1D2,这样调学习率不用改公式。我习惯把学习率、初始点、迭代次数都做成参数单元格,整张表变成可交互的。

3.2 用Excel图表看收敛:J值和梯度的双轴曲线

光看数字不够直观,Excel的图表能把收敛过程画出来。选中C列(J值),插入折线图,横轴用A列(迭代次数)。你会看到J值一开始下降很快,后面越来越平,这就是收敛。如果J值先下降然后上升,说明学习率太大,震荡发散了。再把D列(梯度)加到次坐标轴,能看到梯度绝对值单调减小到接近零。这两条曲线一对照,你就知道程序里那个tol到底在卡什么。

我一般会做三张图对比:α=0.1、α=0.5、α=1.5。α=0.1的J值平滑下降;α=0.5的J值会上下震荡但最终收敛;α=1.5的J值直接飞出去。这三张图放在一起,比任何文字解释都管用。Excel里改一个单元格,三张图同时变,这种即时反馈是Matlab命令行给不了的。

3.3 把Matlab输出导入Excel做逐行对账

Matlab跑完后的history矩阵可以导出成CSV,然后用Excel打开,和手算的表格逐行对比。如果第5行的θ值差在小数点后六位以外,说明你的Matlab代码和Excel公式有一处不一致。常见的不一致点:Matlab里梯度是2theta+2,Excel里写成2B2+1,少了个2;或者Matlab里更新用的是旧梯度,Excel里用了新梯度。这种对账能帮你抓出代码里的低级错误。

导出命令很简单:writematrix(history, 'gd_history.csv')。然后在Excel里用「数据」→「从文本/CSV」导入。我一般会在Excel里加一列「Matlab θ」和「Excel θ」的差值,用条件格式标出大于1e-6的单元格。这样一眼就能看到哪一步开始分叉。

4. 避坑与排查:梯度下降法在Matlab和Excel里最容易翻车的5个地方

4.1 现象:Matlab跑着跑着theta变成NaN

原因:学习率太大导致数值溢出,或者目标函数里有log(0)之类的未定义运算。解决:先把学习率降到0.001试,如果还NaN就检查目标函数定义域。在循环里加一句 if any(isnan(theta)), break; end 能防止死循环。

4.2 现象:Excel表格拉了几百行,J值一直不降

原因:公式引用错了行,比如B3引用了D3而不是D2,导致用了当前梯度而不是上一步梯度。解决:检查B3的公式,确保减的是上一行的梯度。另一个可能是学习率太小,比如1e-6,那确实降得慢,改成0.01试试。

4.3 现象:Matlab和Excel结果对不上,差在小数点后几位

原因:Matlab默认双精度,Excel也是双精度,但Excel显示的时候可能截断了。解决:在Excel里把单元格格式改成显示15位小数,或者用 =ROUND(B2,10) 统一精度。如果还差,检查Matlab里是不是用了单精度变量。

4.4 现象:梯度下降在多元函数上震荡不收敛

原因:不同维度的特征尺度差异太大,导致梯度方向被大尺度特征主导。解决:先做特征归一化,把每列特征减均值除标准差。Matlab里用 zscore(X),Excel里用 =(B2-AVERAGE(B:B))/STDEV(B:B)。归一化之后学习率可以设得更大,收敛更快。

4.5 现象:迭代次数到了maxIter还没收敛

原因:可能是目标函数本身没有最小值,或者学习率太小。解决:先画J值的曲线,如果还在下降就加大maxIter或者调大学习率。如果J值已经平了但梯度还很大,说明你的tol设得太严,放宽到1e-4试试。

5. 进阶技巧:用Excel做参数扫描和Matlab的收敛验证

5.1 在Excel里做学习率扫描表

Excel的数据表功能可以一次性算多个学习率的结果。建一个两列的表:左列是学习率0.01、0.05、0.1、0.5、1.0、1.5,右列用公式算20步之后的θ值。具体做法:在F2填0.01,G2填 =3-20F2(2*3+2) 这种近似?不行,得老老实实迭代。更实用的办法是用Excel的「模拟分析」→「数据表」,把学习率作为输入,输出最终θ值。这样你能一眼看到哪个学习率收敛最快且稳定。

我一般会做一个二维表:行是学习率,列是初始点,单元格里填最终J值。条件格式标红发散的区域,标绿收敛到最小值的区域。这张表就是你的参数安全区地图。对于J(θ)=θ^2+2θ+1,安全区大概是α在0.01到0.9之间,初始点在-10到10之间。

5.2 用Matlab验证Excel的收敛边界

Excel里看到α=1.0时J值震荡但没发散,α=1.1时发散。Matlab里可以精确找到临界点。写一个循环,让α从0.9到1.1步长0.001,跑100步看最终θ是否在-1附近。代码如下:

% 寻找学习率的收敛边界 alphaList = 0.9:0.001:1.1; finalTheta = zeros(size(alphaList)); for i = 1:length(alphaList) alpha = alphaList(i); theta = 3.0; for k = 1:100 theta = theta - alpha * (2*theta + 2); end finalTheta(i) = theta; end % 找到第一个发散的alpha idx = find(abs(finalTheta) > 100, 1); if ~isempty(idx) fprintf('临界学习率约为 %.3f\n', alphaList(idx)); end

这段代码跑出来,临界α在1.0附近。和Excel的扫描结果对一下,如果一致,说明你对算法的理解到位了。如果不一致,回去检查Excel的迭代步数是不是和Matlab一样。

5.3 把收敛过程录成GIF的土办法

Matlab里可以用drawnow和getframe把每一步的θ位置画出来,存成GIF。Excel里做不到动画,但可以用条件格式做「热力图」:把每一步的θ值映射成颜色,从红到蓝表示从初始点到最小值。我一般用Excel的色阶功能,选中θ列,条件格式→色阶→红白蓝。这样你滚动表格的时候,能看到颜色从红渐变到蓝,直观感受收敛过程。

最后说个我自己的习惯:每次写完梯度下降代码,我一定会在Excel里手算前5步,和Matlab输出对一遍。这个习惯帮我抓过至少三次梯度符号写反的错误。符号一错,程序跑得再顺也是往反方向跑。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进