ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

线性回归算法代码实战:从最小二乘到sklearn模型评估与避坑

线性回归算法代码实战:从最小二乘到sklearn模型评估与避坑 简介线性回归算法代码.zip 是一份面向机器学习初学者、数据分析人员以及需要完成回归课程作业的学生的入门实践资料聚焦线性回归模型从原理推导、代码实现到结果评估的完整链路。压缩包共3个文件包含2个Python脚本问题1.py、问题2.py和1个Word文档线性回归算法.doc整体大小约324KB。两个脚本分别围绕单自变量与多自变量场景演示读取数据、划分训练集与测试集、调用sklearn中的LinearRegression拟合模型、用R²分数评估效果等关键步骤Word文档则系统讲解线性回归的数学原理ywxb、最小二乘法并补充特征缩放、正则化等防过拟合处理思路便于对照代码理解房价预测、销售量预测等实际应用。目前已有1086人学习下载文件少而精涵盖从数据准备到模型评估的完整流程适合快速上手回归算法代码实现、模型评估与结果解读。1. 线性回归算法代码.zip入门简单落地全是细节拿到这份“线性回归算法代码.zip”时我本来觉得这就是个教学用的入门包一个 doc 文档配上两个 py 文件跑通就算完事。但实际拆完问题1.py 和问题2.py 之后发现里面藏着的恰恰是新手最容易栽跟头的地方——数据怎么切、特征怎么处理、评估指标怎么读这些才是线性回归能不能在真实场景里干活的关键。这份资源适合刚学机器学习、正在做课程设计或数值分析作业、以及想快速跑通一个回归预测任务的人。它能让你从“会调用 fit 和 predict”进阶到“知道自己在拟合什么、结果靠不靠谱”。2. 先从数学模型说起为什么线性回归能干活2.1 从 y wx b 到最小二乘模型到底在解什么线性回归的数学形式很简单y wx b多元场景下写成 y w₁x₁ w₂x₂ ... wₙxₙ b。这个模型假设因变量和自变量之间存在线性关系目标就是找到一组权重 w 和截距 b让模型对所有样本的预测误差最小。这里的“误差”在最小二乘法里定义为残差平方和J(w, b) Σ(yᵢ - ŷᵢ)²。之所以用平方而不是绝对值是因为平方函数处处可导能直接求闭式解而且对大误差样本的惩罚更重——这意味着模型会优先照顾那些偏差大的点。sklearn 里的 LinearRegression 默认就是用最小二乘通过求解正规方程 W (XᵀX)⁻¹Xᵀy 一次性算出最优参数不需要像梯度下降那样迭代。有一点新手常忽略闭式解里有个 (XᵀX)⁻¹如果特征之间存在完全线性相关XᵀX 就不可逆代码里的 fit 会报错或算出极大的系数。这正是后面避坑章节要展开的点。理解了这个公式你就能明白为什么线性回归不擅长处理共线性数据。理论上是这么回事但实际用的时候我一般不会直接拿原始数据去 fit。先看维度、看量纲、看缺失值再决定要不要做标准化。这些预处理步骤往往比模型本身更影响结果。2.2 简单回归与多元回归选型决定代码写法简单线性回归只有一个自变量 x模型是一条直线多元线性回归有两个及以上自变量模型是一个超平面。这个区别直接决定了数据矩阵 X 的形状简单回归要求 X 是一维数组或 (n, 1) 的二维列向量多元回归要求 X 是 (n, m) 的二维矩阵。判断自己该用哪一种有个很实用的经验先画散点图或相关系数热力图。如果只关心单个特征和目标的关系先跑简单回归如果手里有多列特征或者明显感觉到目标不应由单变量决定就上多元回归。这份 zip 里的问题1.py 和问题2.py大概率就是按这个思路拆分的两个场景。代码上最大的差异在这几处第一简单回归里 X 在构造时如果是 NumPy 数组要小心变成 (n,) 的形状sklearn 会直接报警告第二多元回归的 coef_ 返回的是长度等于特征数的数组不是你熟悉的单一斜率第三真实项目中多元回归几乎必须做特征缩放否则不同量纲的特征会挤压权重数值。我自己的习惯是先跑一个简单回归做 baseline看 R² 大概什么样再往模型里加特征。这样每加一个特征能明确看到性能是提升还是下降而不是一股脑全塞进去。2.3 数据从哪来用模拟数据把流程先跑通在没有配套数据集的情况下最快验证整个流程的方式就是生成模拟数据。你完全可以先跳过手头的问题用下面这段代码把“生成数据 → 划分 → 训练 → 评估”的链路打通再回头处理 py 文件里的实际数据。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.metrics import r2_score, mean_squared_error # 生成模拟数据y 2x 1 噪声 np.random.seed(42) X np.random.uniform(-10, 10, (500, 1)) true_w, true_b 2.0, 1.0 y true_w * X[:, 0] true_b np.random.normal(0, 2.0, 500) # 切分训练集和测试集 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) # 训练模型 model LinearRegression() model.fit(X_train, y_train) # 预测与评估 y_pred model.predict(X_test) print(斜率 w:, model.coef_[0]) print(截距 b:, model.intercept_) print(R²:, r2_score(y_test, y_pred)) print(RMSE:, mean_squared_error(y_test, y_pred, squaredFalse)) # 画回归线 plt.scatter(X_test, y_test, alpha0.5, label真实数据) plt.plot(X_test, y_pred, colorred, label回归线) plt.legend() plt.show()这段代码的重点在于验证一个完整闭环。random_state42 保证每次运行得到的切分结果一致这在复现实验和对比结果时很重要。test_size0.2 表示用 20% 的数据做最终评估剩下的 80% 用来拟合模型。r2_score 和 mean_squared_error 分别从“解释变异的比例”和“误差的绝对大小”两个角度衡量模型质量。跑完这个流程你会看到即便有强噪声模型仍然能逼近真实的 w2 和 b1。这说明最小二乘法对线性关系有很强的恢复能力。如果跑出来的 R² 只有 0.2 或负值那基本不是模型实现的问题而是数据本身不适合线性假设——这时候就该停下来检查数据了。3. 拆解“问题1.py”与“问题2.py”两种典型问题的完整流程3.1 问题1.py单特征场景的数据读取、训练与评估拿到问题1.py第一步是先看它读的是什么数据、按什么格式读。文件里最典型的写法是用 pandas 读取 CSV然后取出特征列和目标列再做训练测试切分。参考代码如下import pandas as pd from sklearn.model_selection import train_test_split from sklearn.linear_model import LinearRegression from sklearn.metrics import r2_score # 读取数据注意确认分隔符和表头 df pd.read_csv(data.csv) # 单特征回归只取一列作为 X一列作为 y X df[[feature_name]].values # 保持二维形状 y df[target_name].values # 一维数组 # 切分数据 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state1 ) # 训练 model LinearRegression() model.fit(X_train, y_train) # 输出模型参数 print(系数:, model.coef_, 截距:, model.intercept_) # 评估 y_pred model.predict(X_test) print(R²:, r2_score(y_test, y_pred))这里最容易翻车的细节是 X 的形状。df[feature_name] 拿到的是一个 Series形状是 (n,)而 sklearn 的 fit 方法要求 X 是二维的。所以要么用双中括号 df[[feature_name]]要么用 .values.reshape(-1, 1)。我看到不少人在这个问题上报错就是因为这里。系数和截距的解读也有讲究。如果训练出来的 w 是负的说明该特征和目标负相关数值绝对值越大影响越强。但要注意如果特征量纲很大比如数值在几千几万系数就会很小这不代表影响弱而是量纲效应。单特征回归还有一个优势可以直接画散点图和回归线肉眼确认拟合效果。如果你的数据也刚好是单特征千万别跳过可视化这一步它能帮你第一时间发现非线性关系或异常点。3.2 问题2.py多特征场景的多元回归与代码差异问题2.py 是多元回归的典型写法。代码骨架和问题1相同但有几个关键位置变了X 从一列变成了多列coef_ 变成数组评估时还要额外关注是否存在多重共线性。import pandas as pd from sklearn.model_selection import train_test_split from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_squared_error, r2_score from sklearn.preprocessing import StandardScaler df pd.read_csv(data.csv) feature_cols [feat_a, feat_b, feat_c] X df[feature_cols].values y df[target].values # 切分后再做标准化防止数据泄漏 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.25, random_state7 ) scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test) # 只 transform不重新 fit model LinearRegression() model.fit(X_train_scaled, y_train) # 每个特征对应的权重 for col, coef in zip(feature_cols, model.coef_): print(f{col}: {coef:.4f}) y_pred model.predict(X_test_scaled) print(R²:, r2_score(y_test, y_pred)) print(RMSE:, mean_squared_error(y_test, y_pred, squaredFalse))和问题1.py 相比问题2.py 多了两个处理一是特征列从一列变多列二是引入了 StandardScaler。注意标准化的位置必须在 train_test_split 之后先 fit 到训练集上再用同一个 scaler 去 transform 测试集。如果把测试集也拿去 fit_transform会造成信息泄漏评估结果会比真实表现好看很多。多元回归里有个很值得做的事把每个特征的权重按绝对值大小排序。但注意标准化之后权重才适合比较否则量纲差异会误导你。上面代码里用的 StandardScaler 把数据转成均值为 0、方差为 1 的标准正态分布这样权重的大小就能近似反映特征的重要性。3.3 sklearn 线性回归核心参数与手工实现对照LinearRegression 这个类参数不多但每个都有讲究。fit_intercept 控制是否拟合截距默认 True一般不需要改如果数据已经中心化均值为0可以考虑设为 False但实际项目中保持默认即可。normalize 参数在新版 sklearn 里已经废弃替代方案是自己用 StandardScaler 做预处理效果更可控。copy_X 默认 True表示 fit 时复制数据防止原始数据被覆盖内存紧张时可以设为 False。n_jobs 在 LinearRegression 里几乎没有加速效果因为闭式解的计算是稠密矩阵运算不是并行友好的任务。import numpy as np # 手写正规方程验证 sklearn 结果 def linear_regression_closed_form(X, y): X_design np.column_stack([np.ones(X.shape[0]), X]) # 闭式解: theta (X^T X)^-1 X^T y theta np.linalg.inv(X_design.T X_design) X_design.T y return theta[0], theta[1:] X_sample np.array([[1], [2], [3], [4]]) y_sample np.array([2.2, 2.9, 4.1, 4.8]) b, w linear_regression_closed_form(X_sample, y_sample) print(手写结果——截距:, b, 斜率:, w)手写正规方程的价值在于帮你理解 sklearn 背后做了什么。np.column_stack 在特征矩阵前拼一列 1对应的是截距项np.linalg.inv 求逆矩阵然后按矩阵乘法公式算出系数。这段代码输出的斜率和截距与 sklearn 的结果应该保持一致。但你会注意一个问题一旦特征数量多了或者特征间相关性高(XᵀX)⁻¹ 的计算会变得数值不稳定这才是实际项目中我们更推荐用 sklearn 封装的原因——它在底层用了 SVD 分解而非直接求逆数值稳定性好得多。学线性回归时亲手写一遍正规方程很有价值但上线做预测直接用现成库更聪明。4. 评估与调参R² 不是唯一指标损失函数才是底线4.1 训练集/测试集划分random_state 的讲究train_test_split 看着简单但有三个参数直接影响结果的可信度。test_size 是测试集比例数据量小的时候建议设置 0.3 到 0.4数据量大比如上万条就用 0.2。其次是 random_state它控制数据打乱的随机种子不设置的话每次运行切分结果都不同实验就不可复现。最后是 stratify 参数它用于分类任务保持类别比例回归任务通常用不上。我一般会固定 random_state 并记录在项目文档里比如 42 或 2024。这样团队成员复现实验时只要跑同样的种子得到的训练集和测试集完全相同对比不同模型才有意义。另外一个习惯是在大规模数据上先做一次 stratify 之外的分层抽样把验证集单独留出来避免模型在测试集上反复调参过拟合。有个很隐蔽的坑如果你在切分后对数据做了任何基于全样本的统计操作比如删缺失值、算均值填充都会“污染”测试集。正确流程永远是先切分再在训练集上做拟合类预处理把参数固定后 transform 测试集。from sklearn.model_selection import train_test_split # 固定随机种子保证每次运行结果一致 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.25, random_state42, shuffleTrue )参数含义一句话说清shuffleTrue 表示在划分前先随机打乱数据防止原始数据按时间或某种顺序排列时切分出现偏差。如果数据本身是时间序列你反而要设置 shuffleFalse 做按时间切分否则未来信息会泄漏进训练集。4.2 从残差到 R²模型好坏的尺度R² 是最常用的回归评估指标公式是 1 - SS_res / SS_tot其中 SS_res 是残差平方和SS_tot 是 y 的方差乘以样本数。直观上它表示模型解释了总变异的比例。R² 0.9 意味着模型能解释 90% 的数据波动剩下 10% 是模型没能抓住的部分。但 R² 有个让人误判的性质只要往模型里加特征R² 就永远不会下降即使那个特征完全是随机噪声。这就是为什么只盯 R² 会翻车。更稳健的做法是同时看均方根误差 RMSE它直接给出预测误差的量级假设房价中位数是 500 万RMSE 是 20 万说明平均误差在 4% 左右这个信息比 R² 更直觉。另外一个十分重要的诊断手段是残差图。画出残差y_test - y_pred与预测值的散点图如果残差随机分布在零线上下说明模型拟合良好如果残差呈现“漏斗形”或有明显曲线结构说明有异方差性或非线性关系没被捕捉。import matplotlib.pyplot as plt residuals y_test - y_pred plt.scatter(y_pred, residuals, alpha0.6) plt.axhline(y0, colorred, linestyle--) plt.xlabel(预测值) plt.ylabel(残差) plt.title(残差分布图) plt.show()画残差图不是可选项是必选项。线性回归的所有理论推断都建立在残差独立同分布的假设上这个假设崩了前面的显著性检验和数据结论全部作废。我见过不少项目 R² 高达 0.95但残差图一画出来有明显的 U 型曲线说明数据存在强非线性线性模型根本不该用。4.3 特征缩放与正则化什么时候必须做特征缩放不是线性回归的必要步骤——最小二乘本身对特征量纲不敏感因为权重会自动缩放补偿。但有两个例外一是你要比较特征重要性必须在标准化后看权重二是当你改用带正则化的变体时缩放直接影响惩罚项的作用。L2 正则化岭回归的惩罚项是 λΣwᵢ²如果某个特征数值范围特别大它的权重天然就小惩罚也就小导致这个特征不会被有效约束。标准化先把所有特征拉到同一尺度惩罚才公平。LassoL1同理不用 StandardScaler 的话特征选择结果会失真。from sklearn.linear_model import Ridge, Lasso from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test) ridge Ridge(alpha1.0) ridge.fit(X_train_scaled, y_train) print(Ridge 系数:, ridge.coef_) lasso Lasso(alpha0.01) lasso.fit(X_train_scaled, y_train) print(Lasso 稀疏化后的系数:, lasso.coef_)alpha 是正则化强度的超参数越大惩罚越重系数越往零收缩。调 alpha 的常用做法是画岭迹图或交叉验证网格搜索直接拍脑袋设 1.0 等于碰运气。Lasso 有个很实用的特性当 alpha 足够大时部分系数会被压缩到完全为零相当于自动做特征选择。这在特征几十上百的场景里非常有用。但要注意 Lasso 的求解是坐标下降法特征间强相关时系数选择不稳定不同种子可能选出不同特征——这不是 bug是 L1 在共线性数据上的固有行为。5. 避坑指南线性回归实践里最常见的六个坑5.1 拟合前从不看数据分布直接 fit 得到垃圾模型现象模型训练完R² 是负数或接近于零预测值几乎是一条水平线。原因数据根本不是线性关系可能是二次函数、对数关系甚至周期性波动。解决先画 X 和 y 的散点图发现曲线关系直接转多项式回归或改用树模型。线性回归不是万能工具它对数据结构的假设非常强不做探索性分析就上手等于盲人摸象。5.2 多重共线性导致系数符号违反常识现象某个特征的权重是负的但直觉上它应该和目标正相关或者两个高度相关的特征一个权重超大正数另一个超大负数互相抵消。原因两个特征几乎线性相关XᵀX 接近奇异矩阵闭式解对微小扰动极其敏感。解决计算特征间的相关系数矩阵找出 |r| 0.8 的列删除其中一个或合成一个新特征。也可以用岭回归替代普通最小二乘L2 惩罚会抑制这种大系数震荡。5.3 先标准化后切分造成数据泄漏现象测试集上表现完美上线后崩得一塌糊涂。原因你在 train_test_split 之前做了 fit_transformscaler 已经看过测试集的分布测试集不再是“没见过的新数据”。解决永远先切分再对训练集 fit_transform对测试集只 transform。你可以在代码里留一行注释提醒自己这个顺序问题我犯过不止一次。5.4 只看 R²忽略了 RMSE 和残差结构现象R² 有 0.93自信满满交付业务方一问“预测误差平均多少”答不上来。原因R² 是相对指标只能说明模型比“用均值预测”好多少不能反映误差的量级。解决报告模型时同时给出 R²、RMSE 和 MAE并画残差图确认残差没有系统性结构。R² 高但残差有趋势说明模型有未捕捉的模式这时候反而要警惕过拟合。5.5 哑变量陷阱类别特征直接用数值编码现象模型训练不报错但某个类别特征的系数完全不可解释或者预测结果对类别编号的大小敏感。原因把“颜色”“城市”这些无序类别直接编码成 1、2、3相当于强行赋予它们顺序关系。解决对无序类别使用 One-Hot 编码pd.get_dummies 或 OneHotEncoder然后删掉一列避免完全共线性。线性回归对特征的含义很敏感这一点比树模型严格得多。5.6 离群点拖垮最小二乘模型被“带偏”现象去掉两个异常点之后模型的系数从 1.8 变成 0.6结论完全反转。原因最小二乘的损失是平方误差离群点的误差被放大平方倍哪怕只有一个极端值也能显著影响回归线。解决用箱线图或 z-score 检测离群点结合业务判断是数据错误还是真实极端值。如果是数据录入错误直接修正或删除如果是真实值考虑用 HuberRegressor 这种对离群点更鲁棒的估计器。5.7 关于时间序列数据的特殊警示现象按时间排序的数据被随机打乱后切分模型似乎能预测未来但真实预测下一期时效果极差。原因随机打乱破坏了时间顺序相当于让模型看到了“未来”。解决时间序列场景一律按时间顺序切分——shuffleFalse让训练集全部在测试集之前。线性回归里这个坑不太显眼一旦涉及金融或销量预测就是致命的。6. 进阶从线性回归到多项式回归把非线性拽回线性如果散点图显示数据有明显曲线关系不用急着换复杂模型。多项式回归的思路很直接在线性模型里加入 x²、x³ 这样的衍生特征让模型去拟合曲线。底层仍然是线性回归只是特征变多了。from sklearn.preprocessing import PolynomialFeatures from sklearn.pipeline import Pipeline from sklearn.linear_model import LinearRegression # 一次构建管道先做多项式扩展再回归 poly_pipe Pipeline([ (poly, PolynomialFeatures(degree2, include_biasFalse)), (lr, LinearRegression()) ]) poly_pipe.fit(X_train, y_train) y_pred_poly poly_pipe.predict(X_test) print(多项式回归 R²:, r2_score(y_test, y_pred_poly))注意 PolynomialFeatures 的 degree 参数2 表示生成 x² 和交互项 x₁x₂3 及以上通常不要轻易尝试数据量不够时一定会过拟合。判断是否过拟合有个简单方法对比训练集和测试集的 R²两者差距超过 0.15 就说明模型开始死记硬背训练数据了。如果多项式回归仍然不够用再加 xgboost 这类树模型。不是说线性回归被取代了而是线性和树模型各管一段业务要做解释、要算因果、要写报告线性模型永远是首选追求预测精度、特征关系高度非线性时xgboost 才有必要出场。我自己的习惯是让线性回归做基线它跑出来的 R² 是所有复杂模型必须超越的底线。从那以后我每次拿到回归数据都强制先走一遍这个流程画散点图 → 切分数据 → 跑线性回归 → 画残差图 → 再做特征工程。这五个动作帮我挡掉了大多数“调参半天发现数据有问题”的无用功。这份 zip 里的两个 py 文件价值不在那一行 fit而在你顺着它的思路把整个流程走通的过程。希望帮到你。本文还有配套的精品资源点击获取
返回列表