资讯动态

一元线性回归全解析:从最小二乘推导到Sklearn实战

发布时间:2026/10/19 5:41:01 来源:尧图企业网站定制
很多人学机器学习的第一个模型就是一元线性回归但大多数人是直接在sklearn里调个fit打印个R²就觉得过关了。直到我在项目里被问了一句你这个斜率能说明什么为什么不能用绝对值当损失函数才发现自己其实没把基础吃透。一元线性回归是整个监督学习的基石后面的多元回归、正则化、逻辑回归都从这一层长出来这层地基如果不牢后面全在沙地上盖楼。这篇文章我准备从模型在假设什么讲起到最小二乘的推导逻辑再到手写梯度下降和Scikit-learn的双版本实战最后用一份模拟数据把完整流程走一遍把该踩的坑也一并说出来。内容适合刚入门机器学习的学生也适合用回归做业务分析但想补理论底子的数据从业者。1. 从面积估价说起一元线性回归在解决什么问题1.1 模型长什么样把业务直觉翻译成数学表达回归这个词听着玄乎本质上就是一句话用一个变量去解释或预测另一个变量。比如你手里有一批二手房的成交记录每套房有面积和成交价。面积变大价格往往跟着变高但又不是一个确定性的函数关系——同一个小区的两套房面积一模一样成交价也可能差出好几万。我们想找的是面积每多一平方米价格平均涨多少这条平均趋势线。用数学写出来就是y β0 β1·x εy 是预测目标房价叫因变量x 是输入特征面积叫自变量β0 是截距表示 x 为 0 时的基准值很多时候没有实际业务意义但模型需要它来兜底β1 是斜率表示 x 每增加 1 个单位y 平均变化多少ε 是误差项代表那些说不清的随机波动比如买家心情、楼层、装修这些东西没有被 x 捕捉到。这里要特别澄清一个常见误解线性不是说 x 和 y 必须画成一条直线而是说参数 β0 和 β1 是一次方的。y β0 β1·x² 这种形式画出来是条曲线但它本质上仍然是线性回归因为未知参数没有相乘、没有取指数。这个区分在入门阶段可能感受不明显但等学到多项式回归、特征工程的时候你会发现线性二字指的是参数空间里的直线而不是数据空间里的直线。1.2 五条核心假设模型不是随手拍脑袋我之前带过的某位同学看到数据就往上套线性模型拟合完之后残差乱七八糟R² 也很难看。问题不是模型不行而是他根本没检查数据是否满足线性回归的基本假设。教科书上通常列五条我用大白话翻译一下线性关系x 和 y 之间真有近似直线的趋势。如果真实关系是 U 型曲线你用直线去拟合那两边都会严重偏离。误差独立ε 之间互不相关。如果你的数据是时间序列今天的高价很可能影响明天误差就自相关了此时普通回归的标准误和检验都会失真。误差均值为零模型的预测偏差在平均意义上不偏向任何一方。如果均值不为零说明你系统性地高估或低估了某个群体。同方差性误差的波动幅度不随 x 变化。想象一群人的身高和体重关系矮个子体重差异小高个子体重差异大这就是异方差。正态性在给定 x 时 y 服从正态分布或者说 ε 服从正态分布。这一条主要用于小样本下的显著性检验和置信区间。你要记住一个态度假设不是摆设是模型的适用条件说明书。线性回归做不好八成不是算法不行而是你塞进去的数据根本不满足这些条件。检查假设这件事我会在第 6 章展开讲先在这里留个钩子。2. 参数怎么算最小二乘推导和梯度下降原理2.1 为什么损失选平方不选绝对值数据有了模型形式定了接下来核心问题就是怎么找到一条最好的直线最好的标准是什么最直觉的衡量标准是预测值跟真实值之间的距离。第 i 个样本的误差是 yi - ŷi。把所有误差加起来不行正负误差会互相抵消明明两句都错了加在一起反而等于零这明显不合理。取绝对值求和可以让正负不抵消但它有个麻烦绝对值函数在 0 点有尖角不可导求最优解时数学工具用不上。平方误差完美避开了这两个问题平方运算天然把正负号去掉而且处处可导方便做求导取极值的操作。还有一个更本质的原因平方损失对大误差特别敏感。一次差 10 和两次各差 5绝对值损失一样大平方损失会认为差 10严重得多。这在多数预测场景中符合直觉——一个极端离谱的预测往往比多个小偏差更不可接受。于是目标函数写成Loss(β0, β1) Σ(yi - β0 - β1·xi)²我们要找一组 β0、β1让这个误差平方和最小。这就是最小二乘法名字的由来。2.2 闭式解推导从误差平方和到斜率公式最小二乘法不需要反复迭代可以直接推出参数公式。方法就是对 β0 和 β1 分别求偏导令导数为零。先对 β0 求偏导并令其等于 0可以得到β0 ȳ - β1·x̄也就是说回归直线一定会穿过样本点 (x̄, ȳ) 这个中心点。再对 β1 求偏导整理后得到β1 Σ(xi - x̄)(yi - ȳ) / Σ(xi - x̄)²眼熟吗分子是 x 与 y 的协方差乘以 (n-1)分母是 x 的方差乘以 (n-1)约掉之后就是β1 Cov(x, y) / Var(x)到这里你就能直观理解两件事。第一斜率的符号由协方差决定x 和 y 正相关斜率就是正的负相关斜率就是负的。第二分母是 x 的方差如果 x 自身几乎没什么变化那斜率估计就会非常不稳定一点点噪声都能把斜率撬动——这对应了自变量要有足够变异这一经验规则。用 Python 写闭式解比想象中简单import numpy as np def least_squares(x, y): x_mean np.mean(x) y_mean np.mean(y) beta1 np.sum((x - x_mean) * (y - y_mean)) / np.sum((x - x_mean) ** 2) beta0 y_mean - beta1 * x_mean return beta0, beta12.3 梯度下降让机器自己下山找最低点闭式解简单可靠但它有个致命限制一旦特征多、样本量大或者以后换成逻辑回归这类没有闭式解的模型公式法就没法用了。所以你必须理解梯度下降。我说个生活化的类比。假设你站在一座山上周围一片漆黑你想走到最低的山谷手上只有一个指南针能告诉你哪个方向是下坡。你会怎么做朝下坡最陡的方向跨一步停下来再重新感受一下然后再朝最陡的下坡方向跨一步。梯度下降就是这个摸黑下山的过程。数学上损失函数对参数的偏导数就是这个方向的陡峭程度也就是梯度。参数沿着梯度的反方向更新β β - learning_rate · ∂Loss/∂β其中 ∂Loss/∂β1 -2·Σ xi·(yi - ŷi)∂Loss/∂β0 -2·Σ(yi - ŷi)。学习率learning_rate是步长。步长太大一步跨过了山谷在对面山腰来回震荡甚至越弹越高——我见过学习率设为 1.0 跑出来的损失曲线那真的是天女散花。步长太小走了半天还在山脚磨蹭训练时间感人。经验上常见做法是从 0.01 开始试看损失曲线的走势再调整。这个调参过程不能靠感觉要看曲线损失猛涨学习率太大损失缓慢下降但半天不收敛学习率太小。另外注意一点梯度下降对特征的尺度很敏感。一个特征是面积数量级几百另一个特征是房龄数量级几十梯度更新时面积维度的导数会占绝对主导。所以实际应用中要先做标准化把每个特征缩放到均值 0、方差 1 附近不然收敛速度和稳定性都很差。这个细节在闭式解里无所谓在梯度下降和后续更复杂模型里是性命攸关的事。3. 怎样判断模型靠谱评估指标和显著性检验3.1 R²是解释力不是正确率模型训练完第一个要看的指标通常就是 R²。它衡量的是相对于用平均值预测模型把误差缩小了多少。公式是R² 1 - SS_res / SS_totSS_tot 是用均值预测的总误差平方和SS_res 是用你的模型预测的残差平方和。R² 0.8 的含义是模型解释了 80% 的总方差剩下 20% 是模型说不清的部分。但这里我要泼一盆冷水R² 高不代表模型就没问题R² 低也不代表模型就一定没用。举一个我印象深刻的例子把 y 与 x 的关系做成一条完美的二次曲线你硬用直线去拟合R² 可能也有 0.85——很高吧但残差图里清清楚楚地显示着 U 型弯曲说明模型系统性地误读了数据。另一方面如果数据本身噪声极大R² 只有 0.3但斜率显著且方向正确这样的模型用来判断正相关还是负相关仍然有价值。R² 讲的是解释力不是正确率更不是能不能用于业务决策。3.2 RMSE和MAE告诉你误差的实际大小R² 最大的毛病是没有单位。它说的是解释了 80%但你的预测到底平均偏差 5 万还是 50 万不知道。这时候要看 RMSE均方根误差和 MAE平均绝对误差MAE mean(|yi - ŷi|)直观、抗异常值但数学性质不如 RMSE 顺滑RMSE sqrt(mean((yi - ŷi)²))对大误差更敏感和损失函数的思路一致。我的习惯是两者都打印出来。如果 RMSE 明显大于 MAE说明数据里存在一些预测得特别离谱的样本把平方误差拉上去了。这个信号会指引我去检查异常值而不是单纯觉得模型不好。比如房价预测任务里正常样本误差在 3 万左右RMSE 却到了 10 万那八成是有几套房的价格因为特殊原因比如学区因素被模型误判得特别严重。3.3 t检验和F检验管的是什么评估指标讲的是拟合效果显著性检验讲的是参数是否可信。一元线性回归里有两类检验回归系数的 t 检验原假设是 β1 0即x 和 y 没有线性关系。检验统计量是 β1 的估计值除以它的标准误。如果 p 值小于 0.05你可以说在 95% 置信水平下斜率显著不为零。回归方程的 F 检验原假设是所有回归系数同时为 0。在一元回归里F 检验和 t 检验结论通常一致但在多元回归里两者分工不同t 检验管单独某个变量F 检验管整体模型有没有解释力。我见过不少新手把p 值显著等同于效果很好这是两个维度的事。样本量很大的时候哪怕斜率只有 0.0001p 值也能小于 0.001显著但对业务来说几乎没影响样本量小的时候一个实际影响很大的斜率也可能 p 值大于 0.05。所以我的建议是p 值回答这个相关性是不是碰巧出现的斜率大小回答这个影响有多大业务含义回答这个影响有没有用。三个问题分开看别混在一起。4. 动手写代码手写梯度下降和Scikit-learn双版本4.1 准备环境和模拟数据先准备好工作环境。理论讲再多不动手全是空中楼阁。我们需要numpy做数值计算matplotlib画图scikit-learn做标准实现用pandas处理数据。模拟数据我故意加噪声这样不会拟合出教科书式的完美直线更接近真实情况。import numpy as np import matplotlib.pyplot as plt from sklearn.model_selection import train_test_split from sklearn.linear_model import LinearRegression from sklearn.preprocessing import StandardScaler from sklearn.metrics import r2_score, mean_squared_error, mean_absolute_error np.random.seed(42) # 生成模拟数据面积x在30到120之间真实关系为 y 2.5*x 20 噪声 X np.random.uniform(30, 120, size200) epsilon np.random.normal(0, 12, size200) y 2.5 * X 20 epsilon X X.reshape(-1, 1) X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42)这里固定random_state是很重要的工作习惯。同一次实验里不固定随机种子你每次跑出来的划分不一样、结果不一样你根本没法判断改动代码前后到底是谁带来的差异。团队协作时固定随机种子第一步能让你的结果和我的结果站在同一起跑线。4.2 手写梯度下降每一步都看得见现在从零实现梯度下降。我故意把学习率和迭代次数写成可以调整的参数方便你直观看到它们对收敛的影响。def gradient_descent(X, y, lr0.01, epochs500): X X.astype(float) y y.astype(float) n len(y) # 添加一列1作为截距对应的特征 X_with_intercept np.c_[np.ones(n), X] beta np.zeros(2) loss_history [] for epoch in range(epochs): y_pred X_with_intercept beta error y_pred - y # 梯度向量 grad_beta0 2 * np.sum(error) / n grad_beta1 2 * np.sum(error * X) / n beta[0] - lr * grad_beta0 beta[1] - lr * grad_beta1 loss np.mean(error ** 2) loss_history.append(loss) return beta, loss_history beta_manual, loss_history gradient_descent(X_train, y_train, lr0.0001, epochs500) print(手写梯度下降结果截距%.3f 斜率%.3f % (beta_manual[0], beta_manual[1]))注意我把学习率设成了 0.0001而不是更常见的 0.01。为什么因为面积的数值范围是 30 到 120而误差数量级在十几导数计算出来不小学习率稍大一点前几步就会震荡甚至发散。这就是我说的特征尺度影响收敛的真实演示。你也可以试试把学习率改成 0.01看损失曲线是不是直接涨到天上去。4.3 Scikit-learn三行出结果现在用Scikit-learn做对比。代码量极简但每一步要清楚它在做什么model LinearRegression() model.fit(X_train, y_train) print(sklearn结果截距%.3f 斜率%.3f % (model.intercept_, model.coef_[0])) y_pred model.predict(X_test) r2 r2_score(y_test, y_pred) rmse np.sqrt(mean_squared_error(y_test, y_pred)) mae mean_absolute_error(y_test, y_pred) print(测试集 R2%.4f RMSE%.2f MAE%.2f % (r2, rmse, mae))运行之后你会发现sklearn的截距和斜率跟手写版本几乎一致。这不是巧合两种方法在数学上都在最小化同一个误差平方和一个用解析公式一步到位一个用迭代慢慢逼近。当你手写的简单模型和成熟库给出的结果对得上时说明你对原理的把握是准确的而不是靠调对了一次参数。4.4 两版结果对比意味着什么读完这段你应该建立的一个重要认知是库不是你偷懒的借口而是让你少写重复代码的工具。我用sklearn做绝大多数工作但我依然推荐学习者动手写一遍梯度下降因为你会亲眼看到 β 更新的每一步、看到损失曲线从高处往下爬、看到学习率不合适时曲线的崩溃形态。这些手感用设施完备的库是体会不到的。对比的意义还在于验证同一个目标函数用不同的优化策略得到相近的参数解说明数学推导没有出错。在后续学到逻辑回归、神经网络的时候这个验证思路同样适用——先用小规模数据把闭式解或者简单实现跑通再上复杂库有问题能第一时间定位是数据问题、模型问题还是实现问题。5. 完整实战二手房价从预处理到结果解读5.1 拿到数据第一件事不是建模而是检查假设你现在拿到一份某区二手住宅成交记录字段有面积、总价、朝向、楼层等目标是做价格预测。新手最容易的冲动是赶紧扔进模型跑一遍。我的经验是建模前花的时间越多建模后返工的时间越少。第一步做缺失值检查。面积和总价是关键字段如果缺失比例超过 5%要么找业务方补数据要么用中位数填充但要在心里清楚这是有偏估计。第二步做异常值筛选我常用箱线图的 IQR 方法超过 Q1-1.5·IQR 或 Q31.5·IQR 的点先标记出来再逐个判断是真异常还是分布本身就这么长尾。比如豪宅面积 800 平米放在普通住宅的数据里像一个异常值但它可能是真实成交。直接删除会丢失重要信息我的建议是先保留建模后看残差再决定如果某几个点在残差图上异常突出再回过来处理。第三步一定是可视化。画一张seaborn.scatterplot(xarea, yprice)你可以一眼看到是不是大致线性、有没有明显的弯曲、方差是不是随 x 变大而变大。这一步比任何统计检验都直观先把能不能用线性模型这个方向判断清楚再谈拟合。5.2 训练模型并解读参数的业务含义数据清理完划分训练集测试集训练模型这个过程代码和上一章是同一个套路。真正的分水岭在参数解读。假设模型输出的斜率是 2.3截距是 35业务解释是每多一平方米价格平均上涨 2.3 万元面积为零时的基准价是 35 万元。但你要多想几步这个斜率是平均效应实际成交中学区属性、楼龄、装修可能在局部区域让这个斜率失效截距 35 万通常没有现实意义没有谁买 0 平米的房子它只是为了让直线更好地拟合数据而存在的调节项斜率估计存在不确定性输出结果时应该带上置信区间而不是只给一个点估计。我见过的最大问题出在把相关性当因果性。面积和价格相关但你不能说把房拆掉一部分就能降房价因为价格高的房子可能因为地段好地段同时影响面积和价格。一元回归只能刻画统计关联因果推断需要实验设计或更复杂的因果模型。把这个边界讲清楚你在业务会议上才不会被问到哑口无言。5.3 三个常见的坑数据泄露、随机种子、异常值这一节分享几个我在真实项目里踩过、也看过别人踩的坑。第一个是数据泄露。数据泄露最常见的形式不是用未来预测过去而是一些隐蔽操作比如你提前用全部数据算均值来填补缺失值再切分训练集测试集那测试集的信息就已经混进了预处理参数。正确做法是切分之后缺省填充、标准化等操作只基于训练集去 fit再应用到测试集。这听起来是小事但泄露一旦发生验证集指标会虚高上线后模型表现立刻现原形。第二个是不固定随机种子。这个坑我在 4.1 说过但值得单独拎出来强调。数据分析不是赌博随机种子固定下来每次运行结果可复现你才能排查到底是数据变了、代码变了还是纯随机波动。第三个是异常值处理时机。很多人喜欢在第一步就把看起来不合理的点砍掉结果可能砍掉了真实信号。我的做法是先建模再看残差如果一个点在实际取值范围内、残差却达到十几个标准差那它大概率有害再决定怎么处理。顺序反过来可能误杀真信号。异常值永远不要无脑删要带理由地删并在报告里写明删了多少、为什么删。6. 回归诊断画四张图给模型体检6.1 残差图、Q-Q图模型有没有系统性失灵模型训练完评估指标好看不等于万事大吉。我习惯画一组诊断图像医生做体检指标正常不代表没有潜在病灶。第一张是残差 vs 拟合值的散点图横轴是模型预测值纵轴是残差。如果残差在 0 附近随机分布没有明显的喇叭口或弯曲说明线性假设和同方差假设都还不错如果残差呈现越往右越散开的喇叭口就是典型的异方差常见于价格这类数值跨度大的数据——预测 100 万的房子偏差 5 万预测 1000 万的房子偏差可能到 50 万方差不恒定。第二张是Q-Q 图检查残差是否近似正态。做法是把残差分位数画在坐标轴上如果点大致落在一条对角线上符合正态性如果两头翘起来说明残差分布有厚尾可能是异常值在作怪。正态性对预测本身影响不大但如果你要做置信区间和假设检验这个假设就重要了。第三张是残差 vs 自变量的图检查有没有遗漏非线性关系。如果残差对 x 画出来呈现 U 型或者倒 U 型说明直线捕捉了两头、漏了中间这时候可以考虑加平方项——虽然那已经不是严格的一元线性回归而是多项式特征下的线性模型思路是相通的。6.2 异方差、非线性怎么救发现问题之后补救措施才是重点。遇到异方差常见方案有几个对 y 取对数把乘法关系变成加法关系这是处理价格、收入这类右偏数据最常用的招或者用加权最小二乘给方差大的样本更小权重再或者使用稳健标准误让显著性检验对异方差不那么敏感。取对数之后注意回归系数的解释变了斜率表示x 每增加一个单位y 平均变化百分比而不是绝对值。遇到非线性不要立刻放弃线性模型可以先做特征变换。加一个 x² 项就是二次回归拟合出来的曲线比直线更能贴合非单调的关系。这一步让你真正理解我之前说的线性是指参数线性x² 只是构造出来的一个新特征模型依然是线性的。遇到异常值影响的厚尾分布可以先尝试稳健回归比如sklearn里的HuberRegressor它对异常值的权重会自动降下来。我自己的习惯是先画图再判断再决定是删、是变换、还是换稳健方法而不是一上来就上黑盒模型。最后我想说一句个人体会线性回归看起来简单但越是简单的东西越能暴露你对数据和业务的理解深度。我后来做项目时哪怕计划用更复杂的模型也会先跑一个一元线性回归当哨兵看看最基本的关系是不是合理的、残差有没有明显的模式。这个习惯救过我好几次有一次就是靠着残差图里的 U 型弯曲发现数据采集逻辑出了偏差问题根本不在模型选择上。希望这篇从理论到实战的梳理能让你在下次用fit之前多问自己一句我到底在拟合什么

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑