☰
Matlab多元回归实战:线性、多项式、岭回归与逐步回归
2026/10/14 20:30:11 网站建设 项目流程

手里数据少得可怜,还想硬着头皮做预测?别慌,数据不够的时候,模型来凑。今天我不讲虚的,直接用Matlab把四种多元回归挨个走一遍,让你手里的那点数据乖乖开口说话。这篇东西适合刚接触回归分析、或者有数据但不知道怎么下手的朋友,也适合已经会用t检验但想系统捋一遍多元回归思路的人。四种方法分别是多元线性回归、多项式回归、岭回归、逐步回归,各有各的脾气,我会把每个模型的适用场景、Matlab代码、参数含义、结果解读全部拆开揉碎讲清楚。

在开始之前先打个底:回归分析的实质就是寻找一堆自变量X和因变量y之间的数学关系。你可以把它理解成“用已知的信息去猜未知的结果”。比如我手头有某小区过去几十套房的成交记录,面积、房龄、楼层是X,成交价是y,那我就能建一个模型,以后来一套新房,代入X就能估出价格。但现实中经常遇到的情况是:样本量凑不够、变量又多又杂、变量之间还互相纠缠。这时候选对回归姿势,比硬套公式重要得多。

1. 四种多元回归的适用场景与思路拆解

1.1 什么时候该用多元回归

先别急着写代码,想清楚一个核心问题:你的自变量到底有几个?如果只有一个X、一个y,那叫一元回归,初中课本都讲过的y=kx+b。但现实问题几乎都是多因子的,比如预测某产品的销量,影响因子可能有价格、广告投入、季节指数、竞品数量,随便一列就是五六个。这时候就必须上多元回归。

多元回归能解决的典型场景有三类:

  • 预测型:用多个输入预测一个连续输出。例如根据温度、湿度、风速预测电力负荷,根据工作年限、学历层次预测薪资区间。
  • 解释型:弄清楚哪些因素对结果影响显著。比如分析某种药物疗效受哪些因素影响,这个时候关注的是回归系数的符号和显著性。
  • 优化型:找到最优的因子组合。比如调整工艺参数(温度、压力、时间)使产品合格率最高。

但这里有个坑:很多新手看到输出是一串数字就以为万事大吉,实际上多元回归对数据质量极其敏感。数据里有一个异常值,可能就把系数带偏十万八千里。所以后面第二部分我会专门讲数据预处理,那才是真正决定模型生死的关键。

1.2 四种方法各自的长处和短板

我在实际项目中见过太多人一上来就甩一个fitlm,不管数据长什么样。这不行。四种方法虽然有雷同,但适用场景完全不一样。

方法核心思想典型适用场景最大弊端
多元线性回归因变量与自变量呈直线关系,用最小二乘法估计系数数据干净、线性关系明显、自变量数量较少对共线性、异常值极其敏感,线性假设太强
多项式回归在线性基础上加入高次项(平方、立方)和交互项,拟合曲线关系明显非线性(增长、衰减、饱和趋势)阶数一高立马过拟合,外推能力差
岭回归在损失函数中加入L2正则项,让系数不要膨胀到离谱特征多、样本少、多重共线性严重模型解释性变差,所有变量都被压小但不会归零
逐步回归逐个尝试增加或删除变量,根据AIC/BIC等准则自动选变量变量一大堆,想快速筛选出关键因子容易受数据变动影响,变量选择不稳定

选方法不是拍脑袋,而是先看数据的“病”。如果VIF大于10,说明共线性严重,就别用普通线性回归,直接上岭。如果画散点图x和y明显是弯的,那就多项式。如果自变量有几十个但样本只有三十个,那普通回归根本算不稳,改用逐步回归或者岭回归。后面我每一步都会给出对应的判断手段。

2. 数据准备与预处理:让数据先“听话”

2.1 缺失值和异常值处理

这是我每次做回归的第一步,也是最容易被跳过的。你从某个系统导出的数据,很少是整整齐齐的矩阵,经常有两三个空值、几个明显离谱的点。先用table类把数据装起来,用summary看看基本情况。

% 读取数据(示例:表格里有X1 X2 X3三个自变量,y一个因变量) data = readtable('data.csv'); summary(data)

看一眼每列的缺失数量。如果有缺失,最省事的方式是插值填补,用线性插值还是平均值得看数据波动:

% 对数值列统一用线性插值填补缺失 data = fillmissing(data, 'linear');

异常值处理更关键。我常用的方法是先画箱线图,把超出1.5倍四分位距的点标出来,再结合业务逻辑判断是保留还是剔除。比如某个特征“用户年龄”出现了300岁,这明显是录入错误,直接删掉这一行:

figure; boxplot(data{:, 1:3}); title('自变量箱线图');

还有一种更通用的方法是3σ准则:超过均值±3个标准差的点视为异常。但注意,如果数据本身就是偏态分布,这个准则并不可靠。我的习惯是异常值单独记录,先剔除建模,再对比不剔除的结果,如果系数变化不大就无所谓,变化剧烈说明模型对异常点太敏感,得想办法。

2.2 归一化与训练集/测试集划分

为什么要归一化?因为多元回归里的系数受量纲影响。假如X1是“面积”,单位是平方米,数值在几十到几百;X2是“房龄”,单位是年,数值在0到20。两个变量量纲差异很大,如果不归一化,最小二乘法在求解时会把主要精力放在拟合大数值的变量上,导致结果偏向性严重。更关键的是岭回归自带正则项,归一化缺失就会让惩罚项作用于不同的尺度,完全失去意义。

Matlab里做归一化我喜欢用zscore,把每列变成均值为0、标准差为1的分布:

% 只对特征矩阵X归一化,y也可以归一化 X_norm = zscore(X);

这里有个非常重要的细节:归一化的均值、标准差,必须在训练集上计算,然后直接应用到测试集。不要拿整个数据集一起算,否则会引入数据泄漏。正确的做法是先随机划分,再分别算训练集和测试集的zscore参数。我见过不止一个项目因为这一步搞错,模型在在线验证时效果大打折扣。

rng(42); % 固定随机种子,保证可复现 cv = cvpartition(size(X,1), 'HoldOut', 0.3); % 留出30%当测试集 idxTrain = training(cv); idxTest = test(cv); Xtr = X(idxTrain, :); ytr = y(idxTrain); Xte = X(idxTest, :); yte = y(idxTest); % 在训练集上计算均值和标准差 muX = mean(Xtr); sigX = std(Xtr); Xtr_norm = (Xtr - muX) ./ sigX; Xte_norm = (Xte - muX) ./ sigX; % 用训练集的参数归一化测试集

划分数据集的时候记得用rng固定种子。如果不固定,每次运行结果都不同,别人想复现你的实验都做不到,这是做数据分析的基本素养。

3. 核心实操:四种多元回归的Matlab实现全流程

3.1 多元线性回归:regress与fitlm

第一种先干最基础的。Matlab里做多元线性回归有两个常用函数:老牌的regress和统计工具箱里的fitlm。我的建议是直接用fitlm,它返回的对象里包含了完整的统计信息,各种指标一次到位。

% 假设Xtr_norm是归一化后的特征矩阵,ytr是目标向量 mdl = fitlm(Xtr_norm, ytr); disp(mdl);

输出结果里你会看到这样一张表:

  • Estimate:每个自变量的回归系数。
  • SE:系数标准误,反映系数估计的稳定性。
  • tStat、pValue:每个变量显著性检验的结果。p小于0.05通常认为该变量显著。
  • R-squared:模型解释力,越接近1说明模型拟合越好,但注意样本量小时容易虚高。
  • F-statistic:整体显著性检验。

除了整体看,还必须单独检查每个变量的方向是否符合业务逻辑。比如做房价预测时,面积系数应该是正的,房龄系数应该是负的。如果出现符号反了,先不要急着宣称模型成功,大概率是共线性或者异常值在作怪。

预测新数据用predict函数:

yhat = predict(mdl, Xte_norm);

然后评估预测精度,我常用两个指标:均方根误差RMSE和平均绝对百分比误差MAPE。

rmse = sqrt(mean((yte - yhat).^2)); mape = mean(abs((yte - yhat)./yte)) * 100;

顺便提醒一句:用fitlm时它默认自动加截距项,也就是常数项,所以你的特征矩阵里不需要手动加一列1。但如果用老函数regress,就必须自己加一列全1:

Xtr_with_ones = [ones(size(Xtr_norm,1),1), Xtr_norm]; [b, bint, r, rint, stats] = regress(ytr, Xtr_with_ones);

看一下stats,数据不大,也就几百样本量,普通回归能跑。但如果特征之间高度相关,这个结果会非常不稳定。怎么判断共线性?在fitlm生成的mdl.Coefficients里看不了VIF,得自己算。我用一个简单函数:

VIF = @(Xmat) diag(inv(corrcoef(Xmat))).'; vif_values = VIF(Xtr_norm);

VIF是方差膨胀因子,高于10说明该变量与其它变量有严重共线性。这时候你再看系数表,可能发现某个变量的p值很高、符号不对劲,但单独做一元回归又是显著的,典型共线性症状。

解决办法就是下面要讲的岭回归。

3.3 岭回归:ridge和lasso解决共线性

岭回归的核心就是在最小二乘的损失函数后面加一个惩罚项:λ乘以所有系数的平方和。这样一来,那些相互拉扯的变量会被整体压缩,不会出现某一个系数大到离谱的情况。

Matlab里最直接的函数是ridge。但有个坑——它默认返回的是标准化后的回归系数,而且要还原回原始尺度才方便解释。

% 注意ridge的输入参数顺序是(y, X, lambda, 'scale') lambda = 0.1; [ridgeCoef, fitInfo] = ridge(ytr, Xtr_norm, lambda, 'scale');

ridgeCoef的第一行是截距项,后面的才是各变量的系数。因为入参X已经是标准化过的,所以得到的系数就是标准化系数,可以直接比较哪个变量更重要。如果要用它预测新数据,需要把测试集也做同样的标准化(用训练集的mu和sigma),然后按公式y = ridgeCoef(1) + Xte_norm * ridgeCoef(2:end)计算。

λ怎么选?这是整个岭回归里最让人头疼的地方。我的土办法是画岭迹图:把λ从0到1之间打几十个点,观察每个系数的变化轨迹。当λ增大,系数会逐渐变小并趋于稳定。选那个让所有系数开始稳定、不再剧烈震荡的值作为正则化参数。

lambdas = logspace(-4, 1, 50); % 从0.0001到10 coefs = zeros(length(lambdas), size(Xtr_norm,2)+1); for i = 1:length(lambdas) tmpCoef = ridge(ytr, Xtr_norm, lambdas(i), 'scale'); coefs(i,:) = tmpCoef'; end plot(lambdas, coefs(:,2:end), 'o-'); xlabel('Lambda'); ylabel('Coefficients'); grid on;

如果手头数据量没那么小,我更推荐用lasso函数中的'Alpha'参数设置为0,这样它等价于岭回归,而且带交叉验证,能自动选λ:

% Alpha=0 表示L2正则(岭回归);Alpha=1 才是Lasso [B, FitInfo] = lasso(Xtr_norm, ytr, 'Alpha', 0, 'CV', 10); lassoPlot(B, FitInfo, 'PlotType', 'CV'); bestIdx = FitInfo.IndexMinMSE; lambda_best = FitInfo.Lambda(bestIdx);

这里有个需要留意的点:lasso默认会标准化输入和输出,返回的系数也是基于标准化数据的。要得到原始尺度的预测公式,还得做一层反标准化变换。具体还原公式为:原始系数 = 标准化系数 × (某原始y标准差 / 某原始特征标准差),截距另算。我不建议手算,直接保留归一化后的数据、用归一化后的模型去预测,最后再把预测值反归一化,这样更不容易算错。

3.4 逐步回归:stepwiselm自动筛选变量

最后一种思路跟前面完全不一样。前面三种都是“把所有变量都塞进模型”,逐步回归则是让算法自己决定哪些变量该留、哪些该滚。它的逻辑是:先从一个最简单的空模型或包含所有变量的全模型出发,每一步尝试增加或删除一个变量,如果增加某个变量后AIC下降得多,就留下;反之就剔除,直到无法再优化为止。

Matlab里直接用stepwiselm:

mdl_step = stepwiselm(Xtr_norm, ytr, 'linear', 'Criterion', 'aic');

这个函数输出的结果比fitlm更详细,因为它会打印每一步的决策过程。你会看到类似“Adding X2, FStat=14.32, pValue=0.00032”这样的日志,能直观看到变量进入了模型还是被移除了。

使用中有几个参数要弄清楚:

  • 'linear'表示只考虑一次项,不含交互项和高次项。如果你想让它也尝试交互项,可以改成'quadratic'或'interactions'。
  • 'Criterion'可以选择'aic'或'bic'。BIC对模型复杂度惩罚更重,在样本量不大时用BIC更不容易选出一堆没用的变量。
  • 'Upper'参数可以限制搜索范围,比如设置成'linear',那它就不会去尝试高次项。

我个人的经验是,变量多但样本不够时,先用逐步回归快速筛选出小模型,再用这个小模型做线性回归或多项式回归,效果往往稳健很多。

有一个新手常犯的错误:用逐步回归选完变量之后,直接拿那个选出来的子模型去报告R方和p值,然后宣称找到了显著因子。这样做在统计上不严谨,因为变量选择过程本身消耗了数据提供的信息。但如果你只是做预测、不是做严格的推断,那用筛选后的模型做交叉验证来评估预测能力,问题倒不大。我会在下一部分细说。

4. 常见问题与排查技巧实录

4.1 数据量很少、特征很多怎么办

这正是标题里“数据不够”最容易遇到的死局。比如你手上只有40个样本,自变量却列了15个,这时候直接用fitlm跑出来的模型,R方可能高得吓人,但换个测试集就崩盘。为什么?因为最小二乘在高维低样本的情况下,会去“强记”每一个训练点,模型自由度不够,只能靠疯狂调整系数来迎合噪声。

我踩过这个坑之后总结出两条路:第一条,用逐步回归先把特征砍到3~5个。砍完以后再用线性回归拟合,风险就小很多。第二条,直接上岭回归,让惩罚项把无关变量的系数压缩到接近0,保住模型的稳定。更极端的情况(样本比特征还少)可以试试偏最小二乘,这套路今天不展开,但思路跟岭回归有点相似,都是用降维或正则化换稳定性。

最重要的一点是:永远要用交叉验证来评估模型,别信训练集上的R方。我把一个40样本、15特征的数据塞进fitlm,训练R方0.98,但留一法交叉验证的RMSE翻了好几倍,这就是典型的“看着强,实际废”。

4.2 R方很高但预测不准,怎么排查

很多人一听说R方0.9就觉得完美,然后拿新数据一测,错得离谱,就开始怀疑人生。问题往往出在三处:

第一,数据划分没做随机化,测试集和训练集分布不一样,导致“范围外预测”。比如训练集里的面积都在80到120平米,测试集里全是200平米以上的,模型当然不可能准确。解决办法是看测试集特征是否落在训练集范围内,不在的话结果不要强求。

第二,模型太灵活,记住了训练噪声。多项式回归最容易犯这个错,阶数一高,曲线在训练点上弯来弯去,一旦跳出边界就剧烈震荡。解决办法是把阶数控制在2~3,或者用交叉验证选择阶数。

第三,没有做残差诊断。回归模型的残差应该随机分布在0附近,如果残差跟预测值之间有明显的喇叭形或弯曲形状,说明模型遗漏了某个非线性关系或方差齐性问题。画残差图的代码很简单:

figure; plotResiduals(mdl, 'fitted');

看到明确的模式,别犹豫,换多项式试一试,或者对某些变量做对数变换。

4.3 多重共线性怎么快速识别

识别方法前面已经提过VIF,这里补充一个更直观的:直接把自变量之间的相关系数矩阵打印出来,看有没有绝对值大于0.8的:

corrMat = corr(Xtr_norm); disp(round(corrMat, 2));

如果发现X1和X2高度相关,比如相关系数0.95,那你在模型里同时放两个变量,它们会拼命争抢解释份额。系数估计变得非常不稳定,你今天跑一个结果系数是5,明天加两个样本变成2。这根本没法用。

遇到这种场景,我的做法是:

  • 如果两个变量在业务上含义相近,比如“房屋面积”和“房间数量”,优先保留一个更精确或更容易获取的。
  • 如果都重要,就用岭回归或者主成分回归,让它们把信息合并表达。
  • 千万不要用逐步回归去“解决”共线性,因为逐步回归选择变量有随机性,稍微换一下数据选择结果可能就变了。

4.4 常见问题速查表

症状可能原因首选处理方案
某个变量系数符号不合理多重共线性或异常值干扰计算VIF,VIF>10改用岭回归;同时清洗异常值
训练集R方0.99,测试集RMSE爆炸过拟合降低多项式阶数,加正则化,或者用交叉验证选择模型
残差图呈喇叭形异方差对y做对数变换或用加权最小二乘
变量很多但样本很少高维小样本问题先用逐步回归降维,再用岭回归稳定系数
特征量纲差异巨大系数解释困难做zscore标准化,并明确标准化系数的含义
加了某个变量后其它变量p值大变共线性计算相关系数矩阵,合并相关变量或上岭回归

4.5 一个实用的预测模型工作流

最后把我摸索出来的完整流程放在这里,供你直接抄作业:

% 1. 数据读入 data = readtable('mydata.xlsx'); X = data{:, {'X1','X2','X3','X4'}}; y = data.y; % 2. 清洗:填补缺失、剔除明显异常 data = fillmissing(data, 'linear'); X = data{:, {'X1','X2','X3','X4'}}; y = data.y; % 3. 划分训练测试集 rng(42); cv = cvpartition(height(data), 'HoldOut', 0.25); idxTrain = training(cv); idxTest = test(cv); Xtr = X(idxTrain,:); ytr = y(idxTrain); Xte = X(idxTest,:); yte = y(idxTest); % 4. 训练集上标准化 muX = mean(Xtr); sigX = std(Xtr); Xtr_norm = (Xtr - muX)./sigX; Xte_norm = (Xte - muX)./sigX; % 关键:用训练集的参数 % 5. 先跑逐步回归筛选变量 mdl_step = stepwiselm(Xtr_norm, ytr, 'linear', 'Criterion', 'bic'); % 6. 对筛选出的变量进一步做岭回归稳定系数 % 这里你需要先知道逐步回归选出的列,假设是第1、3列 lambda_best = 0.05; % 用交叉验证得到的值 coef_ridge = ridge(ytr, Xtr_norm(:, [1 3]), lambda_best, 'scale'); % 7. 用测试集评估 yhat_ridge = coef_ridge(1) + Xte_norm(:, [1 3]) * coef_ridge(2:end); rmse = sqrt(mean((yte - yhat_ridge).^2));

这只是一个例子,实际项目中你还需要回到业务维度检查变量的意义。如果某个变量是纯凑数的,就算它显著也别太当真,因为显著性检验本身会随着样本量变化,样本多的时候一堆不重要的变量都会显著。

5. 我自己的实操体会与几个容易翻车的小细节

跑回归跑得多了,我发现新手翻车的点往往不在统计理论上,而在一些特别琐碎的细节上。

第一个细节:fitlm默认就把所有变量当成连续数值变量,如果表格里有分类变量(比如“楼层朝向”这类文本),它并不会自动编码。你需要自己把分类变量转成哑变量,用dummyvar函数,生成0/1矩阵,再拼进特征矩阵。

D = dummyvar(data.Layer); % 假设Layer是分类变量 % 注意dummyvar生成的列数等于类别数,一般要删掉一列避免共线

第二个细节:用predict函数时,传入的新数据必须跟训练模型时的特征列顺序完全一致。有一次我把测试集的列顺序写错了,结果没报错,但预测值明显不对,排查了半天才发现是列顺序问题。建议在建模之前就把特征列名统一好,用varfun或者table2array前仔细检查。

第三个细节:如果你觉得模型预测效果还是不行,别急着换更复杂的算法,先回头看数据采集范围。模型本质上是在表达“当前数据范围之内的规律”,超出这个范围,哪怕十种模型也救不回来。你要做的是收集更多覆盖不同场景的数据,而不是无限增加模型复杂度。

我个人最推荐的一种打法:先用逐步回归快速摸清变量的大致组合,再用岭回归或者带约束的最小二乘把系数稳定下来,最后用交叉验证确定最终的预测精度。这套组合在我的多个项目里都表现稳当。如果你手头正好也有个“数据不够模型来凑”的活儿,试试这个流程,应该能帮你少走不少弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询