
做回归分析这件事我在实际项目里用的频率比想象中高得多。不管是做房价预测、销量预估还是实验数据处理多元线性回归几乎都是第一个要尝试的模型。它的原理不复杂但在MATLAB里真正落地时有不少细节容易坑到人——比如数据没标准化导致矩阵奇异比如只盯着R²看结果却忽略残差比如变量之间共线性严重却浑然不觉。这篇文章我打算从数据准备、数学原理、代码实现到结果诊断完整走一遍流程把每一步的“为什么”也一并讲清楚。代码部分是可以在MATLAB里直接跑的适合刚接触回归分析的学生也适合想把建模流程过一遍的工程师。1. 做回归分析前先想清楚几件事1.1 什么样的场景适合多元线性回归多元线性回归解决的根本问题是有一个连续型的因变量 y它可能受到多个自变量 x1、x2、x3……的影响我们想知道这些自变量分别怎么影响 y影响有多大以及能不能用这些自变量去预测新的 y 值。举个例子。你在处理一组二手房数据因变量是成交价格自变量可以包括面积、房龄、所在楼层、距离地铁站的距离等。用多元线性回归你可以得到一条形如“价格 2.8 万×面积 (-0.5) 万×房龄 1.2 万×楼层 320 万”的关系式。这个式子里每个系数都代表了在其他变量不变的情况下该变量每增加一个单位因变量平均变化多少。但是要注意多元线性回归不是万能的。它本质上假设 y 与 x 之间是线性关系且误差项满足一些统计假设。你遇到的问题是“连续值预测”比如价格、温度、产量、浓度那优先考虑它没问题。如果预测的是类别比如“会不会违约”“图片里是猫还是狗”那就该考虑逻辑回归或分类模型。还有如果你的数据关系明显是非线性的比如 y a·ln(x) 或 y a·e^(bx) 这种那可以尝试对变量做变换取对数、取平方等后再用线性回归这在工程上相当常见。另外多元线性回归还有一个特性能派上大用场它自带可解释性。系数的大小和正负能让业务方直观理解变量影响的方向和程度。深度学习模型虽然预测精度高但解释起来费劲。很多工程项目里客户和评审专家都要问你“为什么这个变量系数是正的为什么它影响这么大”回归模型这时候就比黑盒模型好说话得多。1.2 数据准备阶段最容易踩的坑很多新手拿到数据就急着开跑结果模型跑出来要么报错要么结果离谱。回归分析里有一句老话垃圾进垃圾出。数据准备花的时间应该占整个项目的一半以上这不是夸张。首先要严格区分自变量和因变量而且因变量必须是连续数值型。如果你的因变量是“好评/差评”这种二分类或者“优、良、中、差”这种等级型就不适合直接用线性回归。其次检查缺失值。MATLAB里读入表格数据后如果发现某些变量大量为空要么删掉样本要么用均值、中位数填充但千万别带着 NaN 去拟合fitlm 通常会直接把包含 NaN 的行整行剔除有时这会悄无声息地减少样本量导致结果和预期不一致。然后是异常值。异常值对最小二乘估计影响非常大因为最小二乘是让所有样本的残差平方和最小一个离群点会把回归线“拉”向自己。我习惯的做法是先用箱线图或散点图快速扫一遍再用“均值±3倍标准差”或者“四分位距”筛选掉明显异常的点。当然如果异常值本身代表真实业务信号比如极端天气造成的能耗尖峰就要结合业务来判断不能一刀切删掉。最后数据的量纲问题。多元线性回归对量纲是敏感的只是这个敏感主要体现在“系数解释”和“数值稳定性”上。如果 x1 的单位是万元、x2 的单位是元两者数字量级差了几千倍设计矩阵 XX 的条件数就会很大求解时容易产生数值误差严重时甚至导致矩阵奇异。这时就需要对数据进行标准化或归一化让每个变量都在相近的量级上。具体做法后面讲核心实现时会详细展开。2. 多元线性回归的核心数学用大白话讲清楚2.1 模型表达式与矩阵形式的由来先建立最基本的数学模型。如果因变量 y 和自变量 x1, x2, ..., xk 之间是线性关系我们可以写成y β0 β1·x1 β2·x2 ... βk·xk ε这里的 β0 是截距项β1 到 βk 是各变量的回归系数ε 是随机误差项它代表模型没解释掉的部分。我们观测到 n 组数据每组都满足这个式子把所有方程摞在一起就得到矩阵形式Y X·β ε其中 Y 是 n×1 的列向量X 是 n×(k1) 的设计矩阵——注意第一列全为1对应截距项 β0。β 是 (k1)×1 的系数向量ε 是 n×1 的误差向量。为什么要转化成矩阵形式因为矩阵的好处是公式简洁而且能直接套用线性代数的成熟计算方法。MATLAB里写代码的时候大部分操作都是在处理矩阵理解了这个形式后面看代码会轻松很多。有些初学者搞不清楚为什么设计矩阵要多加一列1。你想想如果没有这列1拟合出来的方程就没有截距强制要求当所有 x 都等于0时 y 也必须等于0这在实际中很少成立。加上这一列β0 就相当于在最小二乘求解时由数据自己估计出来的一个偏置项模型的灵活度完全不一样。2.2 最小二乘法到底在做什么最小二乘法OLSOrdinary Least Squares是求解回归系数最经典的方法。它的目标简单粗暴找到一组 β让所有样本的预测误差平方和最小。残差定义为 ei yi - ŷi其中 ŷi 是第 i 个样本的预测值ŷ X·β。残差平方和 SSE Σ ei² (Y - Xβ)ᵀ(Y - Xβ)。把 SSE 对 β 求导并令导数为0经过几步矩阵求导运算就得到了正规方程β̂ (XᵀX)⁻¹ XᵀY这就是最终答案。在MATLAB里我一般不会直接写 inv(X*X)*X*Y而是用左除运算符beta (X*X) \ (X*y)或者直接X \ y。原因在于左除运算使用了LU分解、QR分解等数值更稳定的算法比直接求逆在数值上更可靠。当矩阵病态严重时inv 方式算出的结果可能完全不靠谱而左除能扛住更差的条件数。这里还要补充一点正规方程并不是唯一的求解路径。对于特征数很多或者样本量很大的情况还可以用梯度下降法迭代求解。但 MATLAB 的量级下正规方程配合矩阵分解一般够用而且实现简单、结果稳定。初学者先把正规方程吃透后续如果想扩展到大规模数据再研究迭代法也不迟。3. MATLAB代码实战手动实现与内置函数两条路3.1 准备示例数据集为了方便演示我构造一个模拟的房价数据集。假设房价 y单位万元与房屋面积 x1单位平方米、房间数 x2单位间、所在楼层 x3单位层有关真实关系为y 50 2.5·x1 30·x2 5·x3 噪声我们在MATLAB里生成这个数据rng(42); % 固定随机种子保证可复现 n 200; x1 50 40 * rand(n,1); % 面积在50~90平米 x2 randi([1, 4], n, 1); % 房间数1~4间 x3 randi([1, 30], n, 1); % 楼层1~30层 noise 20 * randn(n,1); % 白噪声模拟真实世界的波动 y 50 2.5*x1 30*x2 5*x3 noise;把数据装入表格方便后面用表变量名引用data table(x1, x2, x3, y, VariableNames, {面积, 房间数, 楼层, 价格});这里我特意加了一个固定随机种子rng(42)其实在工作中这点也很重要。如果你不固定种子每次运行得到的数据都不一样可能这次跑通了下一次又报错无法复现结果。凡是模拟实验、需要随机性的代码我建议都这样处理。3.2 用正规方程手写回归核心代码先走一遍纯手写流程理解底层原理。首先构建设计矩阵 X也就是在原自变量矩阵前面加一列全1X [ones(n,1), x1, x2, x3]; beta_hat (X * X) \ (X * y);三行代码搞定。beta_hat是一个 4×1 的向量第一项是截距第二到第四项分别是 x1、x2、x3 的系数。然后计算预测值和残差y_hat X * beta_hat; residual y - y_hat;如果一切正常你会发现拟合出来的系数和真实系数非常接近和 2.5、30、5 的偏差在合理范围内。这里实际动手过的朋友可能会遇到一个问题如果我用X \ y而不是(X*X) \ (X*y)结果会怎样结果几乎一样但X \ y在数值上更稳定因为它直接对 X 做 QR 分解求解不把计算量集中在 XX 上。你可以做个小实验构造一个条件数很大的 X分别用两种方式求解对比结果差异往往能看出明显不同。3.3 用 fitlm 一键获取完整统计结果手写代码能让你看清内部原理但实际项目里我一般直接用fitlm它返回的模型对象包含几乎全部你需要的统计量mdl fitlm(data, 价格 ~ 面积 房间数 楼层); disp(mdl);屏幕上会输出一大段表格里面包含系数估计、标准误差、t统计量、p值模型整体的R²、调整R²、F统计量和p值以及误差方差估计。这些指标是后续模型评价的基础。如果你不想写公式也可以直接传入自变量矩阵和因变量向量mdl2 fitlm(X(:, 2:4), y, VarNames, {面积, 房间数, 楼层, 价格});这里X(:, 2:4)是去掉截距列之后的自变量数据VarNames的最后一个名字对应因变量。用mdl.Coefficients可以拿到系数表用mdl.Rsquared.Ordinary和mdl.Rsquared.Adjusted分别获取R²和调整R²后续做结果解读时非常方便。3.4 模型预测与置信区间模型拟合完最重要的应用是预测。用predict函数就能对新数据做预测newData table(75, 2, 15, VariableNames, {面积, 房间数, 楼层}); [pred, ci] predict(mdl, newData);pd是预测值ci是置信区间的上下界。比如一个75平米、2间房、15楼的房子模型给出的预测价格会落在一定区间内这就是预测的不确定性表达。实际项目里向业务方交付预测结果时最好连区间一起给单纯给一个点估计很容易让人误以为预测是精确的。可视化拟合效果用散点图叠加上预测线也是常规操作figure; plot(y_hat, y, o); xlabel(预测值); ylabel(实际值); axis equal; hold on; plot([min(y) max(y)], [min(y) max(y)], r--, LineWidth, 1.5);如果散点紧密分布在红线附近说明预测效果不错。4. 模型评价与诊断结果不能只看R²4.1 回归系数、置信区间与p值怎么读拿disp(mdl)输出的系数表来说每一行是一个变量包含 Estimate、SE、tStat、pValue 四列。Estimate是系数估计值就是 β。SE是系数标准误差它衡量的是这个系数估计的精确程度SE 越小说明系数估得越稳定。tStat是 t 统计量等于系数除以标准误差用来检验系数是否显著不为0。pValue是显著性 p 值一般以 0.05 为界小于 0.05 就认为该变量对 y 有显著影响。这里有个关键细节p值不显著不代表变量没影响可能只是样本量不够或者该变量和其他变量存在共线性导致标准误被抬高。所以不要见到 p 值大于 0.05 就急着把变量删掉。我的经验是先看系数方向是否符合业务常识再结合后续多重共线性分析综合判断。系数置信区间也是判断显著性的常用工具。在MATLAB里用coefCI(mdl)可以得到每个系数的95%置信区间如果区间不包含0说明该系数在统计上显著。4.2 残差分析奇怪的残差图怎么看残差分析是回归诊断的重头戏。plotResiduals函数非常方便figure; plotResiduals(mdl, fitted);横轴是拟合值纵轴是残差。理想的残差图应该像一个随机散布的云团没有任何明显结构。如果你看到残差随拟合值增大而呈喇叭状发散说明方差非齐性也就是说模型的误差在不同预测值水平上不稳定。这种情况下最简单的应对方式是对因变量取对数或者用加权最小二乘。再看正态概率纸上的残差分布figure; plotResiduals(mdl, probability);如果残差接近正态点会大致落在直线上。如果尾部严重偏离说明残差不服从正态假设这可能影响置信区间和p值的准确性。在实际工程项目里残差轻微偏离正态通常影响不大但如果严重偏态或者残差图呈现明显的曲线模式那就要考虑加二次项、交互项或者换非线性模型。还有一种常见情况是残差随时间或实验顺序有规律波动。如果在采集数据时存在时间趋势残差图上就会出现明显的波浪形。这时候可以在模型里加入时间序号变量或者把数据按时间排序后做差分处理。4.3 多重共线性VIF计算与处理思路多重共线性是多元回归里特别隐蔽的问题。它的本质是一些自变量之间存在较强的线性相关关系导致设计矩阵 X 的列向量近似线性相关矩阵 XX 接近奇异系数估计变得极不稳定。怎么识别常用的指标是方差膨胀因子 VIF。第 j 个自变量的 VIF 等于 1/(1-R_j²)其中 R_j² 是把它作为因变量、其余所有自变量作为自变量进行回归时得到的R²。VIF 大于 10 通常认为存在严重的共线性。MATLAB 里可以直接手写VIF计算依赖fitlm即可Xvars table2array(data(:, 1:3)); p size(Xvars, 2); VIF zeros(p, 1); for j 1:p yj Xvars(:, j); Xj Xvars; Xj(:, j) []; % 去掉当前列 mdl_temp fitlm(Xj, yj); R2j mdl_temp.Rsquared.Ordinary; VIF(j) 1 / (1 - R2j); end disp(array2table(VIF, VariableNames, {VIF}, RowNames, {面积, 房间数, 楼层}));如果发现某些变量 VIF 很高处理方法有三种。第一种是直接删除共线性严重的变量保留业务上更核心的那个。第二种是用主成分分析先降维再用主成分做回归不过这样会损失可解释性。第三种是改用岭回归或LASSO等带正则化的方法牺牲一些无偏性换取系数的稳定性。对于初学者我建议先走“删除冗余变量”这条路简单直接且解释清晰。5. 常见问题排查与避坑经验5.1 矩阵奇异导致无法求解怎么办使用正规方程求解时如果设计矩阵 X 的列之间存在完全线性相关比如你不小心把“房间数”和“卧室数”同时放进了模型而它们恰好完全相等XX 就是奇异的MATLAB会报错或者给出 NaN 系数。遇到这种情况第一步是检查变量列表。是否有某个变量可以由其他变量线性组合而成是否有变量是常量所有样本值相同常量变量放进设计矩阵会导致秩缺失。使用rank(X)可以看到矩阵秩是否等于列数如果秩小于列数说明列之间存在线性相关。在 fitlm 内部它对矩阵做了检测遇到完全共线时会自动省略某些列输出中会显示“coefficients with estimated value set to zero”或类似提示。所以有时模型能跑出来但是某些变量的系数为0这是 MATLAB 在提醒你变量有冗余。我遇到这种提示会立刻回去审视变量选取而不是直接接受结果。5.2 数据标准化到底需不需要这个问题每次讲课时都有人问。我的回答是如果你只做预测标准化不是必须的如果你关心系数解释或数值稳定性标准化就很有必要。不标准化的好处是系数直接保留原始量纲解释起来直观面积每增加1平米房价平均增加2.5万元。但坏处是如果各变量量级差太多数值稳定性会受影响。标准化的做法很简单X_std (X - mean(X)) ./ std(X);标准化之后所有变量均值为0、方差为1此时回归系数代表的是“自变量变化一个标准差时因变量的平均变化量”可以用系数绝对值来比较各变量的相对重要性。这在变量筛选和报告分析中是常用技巧。有一点要注意标准化之后截距会发生变化而预测值需要反标准化回去所以如果要把公式交给业务人员使用记得把标准化系数还原成原始尺度否则对方拿去做预测时会一头雾水。5.3 过拟合的识别与变量筛选思路多元回归里变量越多R²越高但这不代表模型越好。模型把所有噪声都学进去之后新数据上的预测效果反而变差。所以调整R²比R²更有参考价值它会对自变量数量做惩罚。在MATLAB里可以用mdl.MSE查看均方误差或者用交叉验证来公平评价模型。简单的做法是把数据分为训练集和测试集比如cvpartition做随机划分cv cvpartition(n, HoldOut, 0.2); trainIdx training(cv); testIdx test(cv); mdl_cv fitlm(data(trainIdx, :), 价格 ~ 面积 房间数 楼层); pred_test predict(mdl_cv, data(testIdx, :)); RMSE sqrt(mean((pred_test - data.价格(testIdx)).^2));这个 RMSE 就是模型真实泛化误差的估计。我在实际项目里测试集 RMSE 一定比训练集的大差别太大就说明过拟合。变量筛选方面初学者常用的思路是逐步回归MATLAB 里的stepwiselm可以直接做前向选择、后向剔除的双向筛选mdl_step stepwiselm(data, 价格 ~ 面积 房间数 楼层, Criterion, BIC);stepwiselm会自动找出贡献显著的变量组合并删掉不显著的。不过我要提醒一句自动筛选只能作为参考它完全基于统计准则不会理解业务逻辑。很多时候业务上必须保留某个变量哪怕它在统计上不显著这时就要在自动筛选结果基础上人工调整。5.4 保存与导出模型部署到实际使用模型训练好了如果要在别的机器上用可以保存模型对象save(housing_model.mat, mdl);之后在另一边加载load(housing_model.mat, mdl); pred predict(mdl, newData);这样模型就实现了轻量级部署。如果对方机器没有MATLAB也可以用matlab.engine或把模型导出为 PMML不过这些属于进阶话题。实际操作中直接把模型系数表格导出成文本或Excel让业务方在公式里套用也是一种很实用的交付方式。写在最后回归分析这件事你只要完整跑通一遍上面的流程后面再遇到类似问题基本就有底气了。我个人在实际项目里最大的体会是不要把“跑通代码”当成目标要把“理解每一步为什么这么做”当成目标。参数估计背后的最小二乘逻辑、残差图里揭示的数据结构问题、共线性导致的系数不稳定这些问题今天没碰上不等于以后不会碰上。先花半小时把原理捋一遍再花一小时把代码逐行看一遍比你盲目调参一周更有价值。最后分享一个小技巧我在做回归分析之前习惯先画一张相关系数矩阵热力图把所有变量两两之间的相关系数可视化出来。这一步能帮你快速发现明显的共线性问题也能让你对变量间的关系有个整体直觉做好之后再进建模环节效率会高很多。希望这篇文章能帮你把多元线性回归这条路走顺下次拿到数据时直接少踩几个坑。