简介:面向统计学入门者、数据分析人员和研究者的MATLAB多元线性回归分析资源,演示从建模、评估到预测的完整流程。压缩包共2个文件、大小仅5KB,含一个.m脚本和一个.xls数据表,前者为可运行的回归分析代码,后者提供某省1970—1987年工农业产值与运输业产值的实际数据,便于对照练习,目前已有2364人学习下载。内容从回归基本原理出发,先介绍多元线性方程中模型参数与随机误差项的含义,再讲解利用fitlm函数建立模型、查看系数与R-squared评估指标,通过残差图、Q-Q图检验线性、独立、正态、方差齐性等假设,并查看系数显著性检验结果判断各自变量的影响程度,最后演示使用predict函数对新数据进行预测。还涉及多重共线性问题的处理思路,如逐步回归、岭回归与套索回归的适用场景。代码结构清晰,读者可直接替换数据运行,快速掌握多元线性回归在科研与业务分析中的实际应用。
1. 多元线性回归分析:不只是跑个 fit 就完事
拿到一份包含十几个特征、一个连续目标变量的数据集,老板让你解释“哪些因素真正影响了结果,影响有多大”——这时候第一个想到的、也最容易被用错的方法就是多元线性回归分析。它做两件事:一是预测,给一组新输入估算出因变量的值;二是解释,每个自变量的回归系数告诉你它在其他变量不变时每变化一个单位,因变量平均变化多少。适合的场景很广:销售额预测、广告投放效果评估、定价模型、风险评估,只要因变量是连续数值,都可以先用多元线性回归打底。但有个反直觉的结论你早晚会遇到:R² 高不等于模型能用,把十几个高相关的变量一起塞进模型,系数会变得极不稳定,正负号都可能反掉。
2. 建模前三步:相关性初筛、共线性摸底与预处理
2.1 用相关系数矩阵筛掉明显重复的变量
很多人拿到数据直接model.fit(X, y),结果系数一团糟才开始排查。我一般先看相关系数矩阵,这一步能省掉后面大半的返工。当两个自变量的 Pearson 相关系数超过 0.8,回归系数估计值的标准误会快速膨胀,等于让模型在“抢功劳”:到底是谁在影响因变量,模型自己也分不清。
import pandas as pd import numpy as np df = pd.read_csv("your_data.csv") # 换成你自己的数据文件 features = ["temperature", "traffic", "price", "promotion"] X = df[features] y = df["sales"] # 计算相关系数矩阵 corr = X.corr() print(corr) # 找出相关系数绝对值大于 0.8 的变量对 pairs = [] for i in range(len(features)): for j in range(i + 1, len(features)): if abs(corr.iloc[i, j]) > 0.8: pairs.append((features[i], features[j], round(corr.iloc[i, j], 3))) print("highly correlated pairs:", pairs)这段代码的核心是X.corr()生成相关系数矩阵,然后遍历上三角找出超过阈值的变量对。0.8 是常用阈值,业务上如果样本量小或变量本身波动大,我会放宽到 0.7。筛出来的变量对要人工判断去留:留下业务上更可解释、不容易出错的哪一个,而不是机械地删后者。比如“门店面积”和“店员人数”相关性高,如果业务口径上面积更稳定、采集成本更低,就保留面积。
2.2 用 VIF 找出多重共线性:计算与阈值选择
相关系数矩阵只能发现“两两之间”的共线性,但三个变量互相之间虽然没有一个超过 0.8,组合起来却可能高度共线。这时候需要 VIF(方差膨胀因子)。VIF 的计算逻辑是:把第 j 个特征作为因变量,用其余特征对它做一次线性回归,得到 R²,然后 VIF = 1 / (1 - R²)。VIF 等于 1 表示该特征和其余特征毫无线性关系,超过 10 说明共线性已经严重到系数不可信。
from statsmodels.stats.outliers_influence import variance_inflation_factor from statsmodels.tools.tools import add_constant # VIF 计算必须加常数项,否则结果没有截距基准 X_with_const = add_constant(X) vif_data = pd.DataFrame({ "feature": X_with_const.columns, "VIF": [variance_inflation_factor(X_with_const.values, i) for i in range(X_with_const.shape[1])] }) print(vif_data)注意add_constant这一步不能省,variance_inflation_factor的计算依赖截距项。输出里“const”那行的 VIF 不用管,只看实际特征。判断标准:VIF < 5 安全,5~10 需要留意,> 10 必须处理。处理方式有两种:先删掉 VIF 最大的那个特征,然后重新算一遍,因为 VIF 是相对的,删掉一个其他都会跟着变;或者对这个特征做中心化/标准化再检查,虽然标准化不改变 VIF 的理论值,但在交互项场景里能缓解问题。
2.3 连续变量标准化和类别变量哑变量处理
预处理里最容易翻车的是类别变量。直接把“华南/华北/华东”编码成 1、2、3,等于强行给类别排序——模型会认为华东是华南的三倍。正确做法是生成哑变量。连续变量的标准化则取决于你是否关心系数的可比性:不标准化,系数解释是“其他变量不变时,x 每变化 1 个单位,y 平均变化多少”;标准化之后,解释变成“x 每变化 1 个标准差,y 平均变化多少”,方便比较不同量纲特征的重要性。
from sklearn.preprocessing import StandardScaler num_features = ["temperature", "traffic", "price"] cat_features = ["region"] # 类别变量转哑变量,drop_first=True 避免哑变量陷阱 df_dummy = pd.get_dummies(df[cat_features], drop_first=True) # 连续变量标准化 scaler = StandardScaler() X_num_scaled = scaler.fit_transform(df[num_features]) X_num_scaled = pd.DataFrame(X_num_scaled, columns=num_features) # 拼接成最终建模特征 X_final = pd.concat([X_num_scaled, df_dummy], axis=1) print(X_final.head())drop_first=True是哑变量处理里最关键的参数。对 k 个类别,只生成 k-1 个哑变量,第一个类别作为参照组。如果不丢第一列,模型里会存在一列等于其他所有哑变量之和的线性关系,和截距项形成完全共线性,这就是经典的“哑变量陷阱”。我在实际项目里见过不下三次因为没丢第一列导致系数无法收敛的情况。标准化这边,fit_transform在训练阶段做一次,验证或上线时只做transform,后面避坑章再展开说原因。
3. 用 statsmodels 跑 OLS:最小命令与回归结果逐项拆解
3.1 为什么建模用 statsmodels 而不是 sklearn
做多元线性回归分析,我几乎不用 sklearn 的LinearRegression做最终结论输出,原因很简单:sklearn 只给你系数和预测值,不给你 p 值、置信区间、F 检验这些统计推断必需的指标。如果建模的目的只是预测,sklearn 没问题;但只要你需要回答“哪个变量显著”“这个系数靠不靠谱”,就必须用 statsmodels。statsmodels 是专门做统计推断的库,OLS自带一整套回归诊断工具,拟合一次就能输出十几个检验指标。另一个差别是常数项:sklearn 的LinearRegression默认自动拟合截距,statsmodels 的OLS默认不加截距,忘记add_constant是新手最常见的报错源头。
3.2 最小可复现的 OLS 拟合代码
承接上一章处理好特征之后,拟合本身只需要三行。但三行和一整套可复现的建模流程之间,差着对设计矩阵的理解。
import statsmodels.api as sm # X_final 已包含标准化后的连续变量和哑变量 X_design = sm.add_constant(X_final) # 拟合 OLS 模型 model = sm.OLS(y, X_design).fit() # 输出完整回归报告 print(model.summary())这段代码的逻辑:sm.add_constant在特征矩阵最左边插入一列全 1,对应截距项 β₀。sm.OLS(y, X_design)里第一个参数是因变量,第二个是设计矩阵,顺序不能反。.fit()执行最小二乘估计,本质是解正规方程 β = (XᵀX)⁻¹Xᵀy。如果特征之间高度共线,(XᵀX)⁻¹ 会不稳定,这也是前面查 VIF 的原因。数据量不大时,OLS 的求解是精确解,没有迭代、没有学习率——这也是它作为基线模型的最大优势:你不需要调任何优化器参数,结果白盒可见。
3.3 回归结果表逐项拆解:系数、p 值、R²、F 检验
model.summary()输出的表很长,我只看下面这几栏,按重要性排序:
| 输出项 | 含义 | 判断标准 |
|---|---|---|
| P> | t | 每个系数对应的 p 值 |
| coef | 回归系数估计值 | 正负号代表影响方向,结合业务逻辑判断是否合理 |
| R² / Adj. R² | 自变量对因变量变异的解释比例 | Adj. R² 用于比较不同变量数的模型 |
| F-statistic / Prob (F-statistic) | 整体模型显著性 | Prob < 0.05 说明至少有一个系数不为零 |
| Durbin-Watson | 残差自相关检验 | 接近 2 正常,远小于 2 说明存在正自相关 |
| AIC / BIC | 模型复杂度与拟合度的权衡 | 用于模型间比较,越小越好 |
解读时最容易犯的错是只看 R²。R² = 0.95 的模型可能因为共线性导致每个系数的 p 值都大于 0.05,整个模型没有一块砖是实的。另一个常见误读是“系数越大影响越大”——如果变量没有标准化,量纲差异会让系数不可比,所以我在上一章才做了标准化。还有一个细节:model.summary()底部还有 Omnibus 和 Jarque-Bera 检验,这两个是残差正态性的快速诊断,下一章里会展开。
# 如果检测到异方差,可以用稳健标准误重新拟合 model_robust = sm.OLS(y, X_design).fit(cov_type="HC1") print(model_robust.summary())cov_type="HC1"是异方差稳健标准误的常用选项,它的作用是修正系数标准误的估计,不改变系数值本身。实际经验:p 值在 0.04-0.06 边缘的变量,换 HC1 之后经常越过 0.05 门槛;而 p 值在 0.2 以上的变量,换什么标准误都不会显著,不用抱侥幸心理。处理异方差的顺序,我一般是先看残差图确认形态,再决定要不要用 HC1——直接无脑用稳健标准误虽然不会错,但会掩盖数据本身的问题。
4. 回归诊断:四个假设验证与修正路径
4.1 线性关系与残差图:怎么看、怎么修
OLS 的第一个假设是自变量与因变量之间存在线性关系。检验方法很简单:画残差对拟合值的散点图。残差是每个样本的真实值减预测值,如果模型正确,残差应该随机散布在零线两侧,没有明显形状。
import matplotlib.pyplot as plt fitted = model.fittedvalues resid = model.resid plt.scatter(fitted, resid, alpha=0.5) plt.axhline(0, color="red", linestyle="--") plt.xlabel("Fitted values") plt.ylabel("Residuals") plt.show()如果图上残差点呈现“喇叭形”——拟合值越大,残差波动越宽,说明存在异方差,这往往伴随 Y 的分布偏态。一种常见修法是给因变量取对数,把乘法关系变成加法关系;另一种是对自变量加平方项或交互项,解决“遗漏非线性项”导致的结构性残差。判断残差图有没有问题,标准不是“点是否落在零线上”,而是“散布宽度是否随拟合值变化”。如果残差在左侧密集、右侧散开,即使所有点贴近零线,模型也是病态的。
4.2 残差正态性:Q-Q 图与 Shapiro 检验
正态性假设影响的是系数置信区间和 p 值的准确性。样本量足够大时(比如 n > 200),中心极限定理让系数估计量近似正态,这时对残差正态性的要求可以适当放宽。但小样本下,残差严重偏态会直接影响 p 值的可信度,需要专门检验。
import scipy.stats as stats # Q-Q 图:点越贴近对角线越符合正态分布 stats.probplot(resid, dist="norm", plot=plt) plt.show() # Shapiro-Wilk 正态性检验 shapiro_stat, shapiro_p = stats.shapiro(resid) print("Shapiro-Wilk p-value:", shapiro_p)Q-Q 图我习惯叫“对线图”,样本分位数对理论分位数,点如果排成一条直线就符合正态。Shapiro 检验的 p 值大于 0.05 时不能拒绝正态性假设,结论是“残差近似正态”。但你实际用的时候会发现一个尴尬:样本量一大,Shapiro 对微小的偏离也极其敏感,p 值永远小于 0.05。这时候以 Q-Q 图为主,只要点没有系统性弯曲(比如 S 形或两端翘起),就当成可接受。
4.3 异方差检验:Breusch-Pagan 与稳健标准误
异方差的危害是低估标准误,导致 t 统计量虚高、p 值偏小,把不显著的变量判成显著。这和上面相关系数膨胀正好反着来——一个是系数乱跳,一个是显著性假象。用 Breusch-Pagan 检验做定量判断:
from statsmodels.stats.diagnostic import het_breuschpagan bp_test = het_breuschpagan(resid, X_design) labels = ["LM stat", "LM p-value", "F stat", "F p-value"] print(dict(zip(labels, bp_test)))het_breuschpagan的第二个参数要传完整的设计矩阵(含截距),不是原始特征。输出里看 LM p-value:小于 0.05 判定存在异方差。处理路径两条:一是对 y 做 Box-Cox 变换或用 log(y),从数据形态上消除异方差;二是保留原模型,用cov_type="HC1"重新拟合得到稳健标准误。我的一般做法是:先试 log(y),如果业务解释能接受(“每增加 10% 的曝光,转化率平均增加 0.3 个百分点”),就用变换后的模型;如果 log 后业务解释太绕,回到原模型配 HC1。
4.4 自相关检验:Durbin-Watson 与时间序列数据注意
多元线性回归分析里,自相关检验经常被忽视。原因是经典 OLS 假设样本独立,但你的数据如果带时间属性——比如按周统计的销售数据、按月统计的用户增长——相邻两周的残差天然相关。model.summary()底部已经给了 Durbin-Watson 值,不需要额外代码。判断标准:DW ≈ 2 无自相关;显著小于 2(比如 < 1.2)存在正自相关,系数标准误会被低估;显著大于 2 存在负自相关,少见但也要警惕。
# 自相关存在时,改用 Newey-West 标准误 model_nw = sm.OLS(y, X_design).fit(cov_type="HAC", cov_kwds={"maxlags": 1}) print(model_nw.summary())cov_type="HAC"配合maxlags=1是处理一阶自相关的常见做法,Newey-West 估计量同时修正异方差和自相关。但这里有个坑:加了 HAC 标准误只是修正了推断,如果你的目标是用模型做预测,自相关意味着预测误差会连续几天同方向偏移,模型给出的预测区间会严重失真。更彻底的做法是加入滞后因变量作为特征,把时间依赖显式建模。我自己的经验:只要数据带时间标签,先画残差对时间的散点图,比直接看 DW 值更直观——如果残差随时间呈现“波浪形”,一定有问题。
5. 多元线性回归最常踩的 5 个坑:现象、原因、对策
5.1 R² 高得惊人,但所有系数都不显著
现象:模型摘要里 R² 0.95,F 检验显著,但每个自变量的 P 值都大于 0.1,没有一个“能打”的。原因:多重共线性。多个变量高度相关时,模型整体能解释因变量的大部分方差,但无法把贡献拆到具体变量上,系数估计值的方差被同步放大,t 统计量失去说服力。解决:回到第 2 章做 VIF 排查,删除 VIF > 10 的变量组合中的重复项。如果 VIF 都正常,再检查是不是样本量太小——变量数和样本量的经验比例至少 1:10,少于这个数字,显著性检验的把握度不够。
5.2 把类别变量直接编码成 1、2、3
现象:模型拟合没有报错,结果里某个“地区”变量的系数是负数,业务上完全无法解释——“难道华南地区比华北差这么多?”原因:你给了模型错误的先验知识,告诉它类别之间有等差关系。地区编码成 1/2/3 后,模型只能学到“每升一级,因变量线性变化一个固定量”,而真实关系大概率不是这样。解决:用pd.get_dummies(drop_first=True)生成哑变量。这个坑几乎每个做过回归分析的人都踩过,我最早做房价模型时也干过,后来在特征工程阶段就把类别变量排查作为固定检查项。
5.3 标准化时对整个数据集 fit,造成数据泄露
现象:训练集上模型 R² 0.85,验证集上一塌糊涂,RMSE 翻了三倍,整个团队怀疑数据对不上。原因:你在建模前置处理里用了scaler.fit_transform(df),其中fit用到了全量数据的均值和标准差,验证集的信息已经渗入训练阶段。解决:训练和验证必须严格分离,标准化的均值和标准差只在训练集上计算,验证集和上线环境用同一个 scaler 做纯转换。
from sklearn.model_selection import train_test_split X_train, X_val, y_train, y_val = train_test_split( X_final, y, test_size=0.2, random_state=42 ) scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) # 只 fit 训练集 X_val_scaled = scaler.transform(X_val) # 只 transform这块“后悔药”唯一的作用是让你记住:凡是涉及全局统计量的预处理(标准化、PCA、缺失值均值填充),都必须在训练集内完成 fit,然后应用到所有后续数据。
5.4 删除离群值后,关键系数符号反转
现象:建模时发现有两个样本的残差特别大,删掉之后模型的 R² 明显提升,但某个关键变量的系数从正数变成了负数,你开始怀疑人生。原因:离群值同时是杠杆点——它们对回归线的牵制远大于普通点。删除离群值这个动作本身没有错,但要注意你是在“调整数据以得到想要的结论”,还是在“修正数据质量问题”。解决:先确认离群值的来源,是录入错误、仪器故障还是真实业务极端情况。录入错误删除没毛病;真实极端值建议保留,或者用 Huber 回归这类稳健方法做交叉验证,看系数是否真的稳定。我现在的习惯是:离群值占比小于 1% 且来源明确是采集错误才删除,其他情况一律稳健方法处理。
5.5 把“相关”当“因果”,模型结论被业务挑战
现象:模型算出“推广费用每增加 1 万元,销售额增加 8 万元”,市场部拿着结果去申请预算,实际投放后效果远不如预期。原因:回归分析只证明了相关关系,不证明因果关系。推广费用高的月份往往也是旺季、竞品活动少、产品新版本上线——这些遗漏变量同时影响推广费用和销售额,造成了虚假的相关。解决:在输出结论前自查三个问题:第一,有没有同时影响自变量和因变量的第三个变量?第二,自变量和因变量之间有没有反向因果(销售额高反而追加推广预算)?第三,样本范围是否有选择性偏差?如果三个问题里有一个回答“可能有”,结论就必须加限定词“在控制其他变量不变的前提下”,而不是“如果多投 X 就能多赚 Y”。多元线性回归分析的价值在于找到值得进一步验证的相关性,而因果推断需要实验设计或工具变量法,那是另一个方向的工程。
6. 落地技巧:交叉验证、稳健标准误与变量筛选的实战配合
模型诊断做完、坑也排完,最后一步是把模型真正落地成决策依据。我做多元线性回归分析收尾时,一定会加三个动作:交叉验证看预测稳定性、稳健标准误确认显著性结论不依赖假设、AIC/BIC 对比确认变量组合没有冗余。
先说交叉验证。统计推断的 p 值和 R² 是样本内的结论,业务方真正关心的是“下个月用这个模型准不准”。用 K 折交叉验证计算 RMSE,能直接回答这个问题:
from sklearn.linear_model import LinearRegression from sklearn.model_selection import cross_val_score lr = LinearRegression() # 5 折交叉验证,用负均方误差打分 scores = cross_val_score(lr, X_final, y, cv=5, scoring="neg_mean_squared_error") rmse_scores = (-scores) ** 0.5 print("每折 RMSE:", rmse_scores) print("平均 RMSE:", rmse_scores.mean())如果交叉验证的平均 RMSE 比训练集 RMSE 高出 30% 以上,说明模型过拟合,变量删少了或者交互项加多了。反过来,如果每折之间的 RMSE 差距巨大,说明数据分布不稳定,做预测要格外谨慎。
关于变量筛选,我的习惯是不用盲目逐步回归——逐删变量再重新拟合会让 p 值失效,你是在同一份数据上反复试,最后选出的模型对“这一次的数据”是好的,但换一批数据就原形毕露。更稳妥的做法是设定几个有业务含义的候选变量组合,用 AIC/BIC 做对比:
import itertools best_aic = float("inf") best_combo = None for k in range(1, len(features) + 1): for combo in itertools.combinations(features, k): X_sub = sm.add_constant(X_final[list(combo)]) m = sm.OLS(y, X_sub).fit() if m.aic < best_aic: best_aic = m.aic best_combo = combo print("AIC 最低的变量组合:", best_combo, "AIC:", round(best_aic, 2))这个穷举只适合特征数少于 10 的场景,特征多了计算量爆炸。AIC 的含义是“在拟合优度和模型复杂度之间取平衡”,它不关心 p 值,只关心这个变量组合在新数据上的期望表现。
最后一个习惯和教训:我会把模型摘要保存成 HTML 或 CSV 存档,连同变量说明、处理步骤一起放进项目目录里,防止三个月后回头重构模型时忘了当时为什么删掉某个变量。多元线性回归分析最大的优势是透明、可解释,但这份优势如果只留在交互式 notebook 里,就等于没有。数据科学里最贵的不是建模,是让别人能复现你的建模。希望帮到你。
本文还有配套的精品资源,点击获取