ARTICLE DETAIL

资讯详情

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

波士顿房价预测实战:线性回归代码与避坑指南

波士顿房价预测实战:线性回归代码与避坑指南 简介面向机器学习初学者的线性回归实战资源包以波士顿房价预测这一经典回归任务为案例完整覆盖从数据加载、缺失值检查、特征工程到模型训练、评估调优的端到端流程。压缩包共20个文件、约228KB由11个Python脚本和9个CSV数据集构成脚本按数据获取、模型训练、结果可视化等模块拆分CSV文件则提供训练集、测试集以及MSE/参数变化曲线数据便于与代码输出对照验证。已有346人学习下载。资源不仅实现基础线性回归还延伸了多项式特征构造、正则化变体岭回归、Lasso以及误差熵分析并通过曲线图直观展示过拟合与调参效果。代码包括梯度下降实现、训练/测试数据生成、重复与随机特征实验等脚本可帮助读者动手实践数据标准化、损失函数计算、R²评估等关键步骤适合希望系统掌握回归建模与参数调试技巧的数据分析初学者。1. 波士顿房价预测没数据了这份实战代码为什么还是值得跑你刚拿到一份名为 boston预测实战以及线性回归基础代码大全.zip 的压缩包时大概率和我当年一样先解压再找 README然后对着满屏的load_boston()发呆。更扎心的是如果你用的 scikit-learn 版本高于 1.2这行代码直接报ImportError——波士顿房价数据集因为伦理和数据偏见问题被官方移除了。但我想先说一个反直觉的结论数据集被移除反而让这个入门项目变得更值得跑一遍。它逼着你学会处理真实项目里最常见的“API 说没就没、接口说变就变”的迁移问题而不是只会抄一段能跑的代码。这份代码解决的是从“看懂线性回归公式”到“写出一套能预测、能评估、能保存的完整流程”之间的那段路。适合正在入门机器学习、想用 python 把最小二乘变成可运行代码的人也适合准备做机器学习线性回归实验、但不想只跑通一个 demo 就交差的同学。它不适合指望靠调参就把房价预测做到排行榜前排的人——线性回归的天花板就摆在那里它的价值不在精度在于让你理解模型、数据和评估三者之间如何互相约束。波士顿房价预测没数据了这份实战代码为什么还是值得跑2. 线性回归的数学底子最小二乘与梯度下降什么时候用哪个2.1 最小二乘的闭式解506×13 的规模为什么直接求逆就行线性回归的目标是找一组系数 θ让预测值 ŷ Xθ 与真实值 y 的残差平方和最小。损失函数写成 J(θ) (1/2m) Σ(hθ(xᵢ) - yᵢ)²对 θ 求导并令导数为零就能得到闭式解θ (XᵀX)⁻¹Xᵀy这就是正规方程。波士顿数据集是 506 行、13 个特征加上偏置项之后 X 的形状是 506×14。这个规模下直接求逆矩阵的耗时完全可以忽略连优化迭代都不需要。我一般会先给新手看这个公式再用 numpy 手写一遍确认和 sklearn 的LinearRegression结果一致才算真正把线性回归吃透。import numpy as np # 手动加一列全 1作为偏置项截距 X_with_bias np.hstack([np.ones((X.shape[0], 1)), X]) # 正规方程θ (XᵀX)⁻¹Xᵀy theta np.linalg.inv(X_with_bias.T X_with_bias) X_with_bias.T y # 预测 y_pred X_with_bias theta # 和 sklearn 对比 from sklearn.linear_model import LinearRegression lr LinearRegression().fit(X, y) print(手动求解截距:, theta[0], 斜率:, theta[1:]) print(sklearn 截距:, lr.intercept_, 系数:, lr.coef_)这里有个前提np.linalg.inv能稳定求逆要求 XᵀX 可逆且条件数不过大。如果两个特征高度相关XᵀX 会接近奇异矩阵求逆结果会剧烈震荡——这就是下一章要讲的多重共线性问题。另外当特征数量超过几万时求逆的时间复杂度是 O(n³)内存和算力都撑不住这时候就要换梯度下降。我的习惯是特征数小于 1000 用闭式解大于这个量级直接上梯度下降不要纠结。2.2 梯度下降学习率、批量大小与收敛判断梯度下降的思路是沿着损失函数负梯度方向反复更新 θ直到损失不再下降。公式很简单θ : θ - α·∇J(θ)。但实际跑起来有四个参数需要你自己定学习率 α、批量大小、初始值、迭代次数。import numpy as np def batch_gradient_descent(X, y, alpha0.01, epochs1000): m, n X.shape theta np.zeros(n) losses [] for _ in range(epochs): grad (1 / m) * X.T (X theta - y) theta - alpha * grad loss (1 / (2 * m)) * np.sum((X theta - y) ** 2) losses.append(loss) # 收敛判断损失变化小于阈值就提前停 if len(losses) 1 and abs(losses[-2] - loss) 1e-6: break return theta, losses学习率是最大的玄学点。α 太大损失会震荡甚至发散α 太小跑几百轮还在半山腰磨蹭。我手上的经验值标准化之后的数据α 从 0.01 起步观察 loss 曲线如果前 50 轮震荡剧烈就除以 10如果曲线平得像一条直线就乘以 10。批量大小则是另一种取舍批量梯度下降用全部数据算梯度方向准但每轮都慢随机梯度下降每轮只用一个样本快但噪声大实践中更常用小批量梯度下降batch 取 16 或 32兼顾稳定性和速度。波士顿这种 506 行的数据集三者差别不大但你得知道这个权衡因为换到视频流量预测、金融时序预测那种上万行的数据时批量大小直接决定训练能不能收敛。2.3 评估指标R²、RMSE、MAE 各自在看什么模型训练完最怕只盯一个指标。波士顿房价预测这个场景里我习惯同时看四个数指标公式含义关注点波士顿场景参考R²1 - SS_res / SS_tot模型解释了百分之多少的方差0.7 左右算及格0.8 以上不错RMSEsqrt(Σ(ŷ-y)²/n)大误差会被平方放大4~7千美元都算合理MAEΣ|ŷ-y|/n平均误差不受离群点过度影响3~5千美元MAPEmean(|ŷ-y|/y)相对误差适合汇报给业务10%~15% 可接受RMSE 和 MAE 的差值很有信息量如果 RMSE 远大于 MAE说明存在少量预测误差极大的样本在拉高整体误差这是离群点的典型信号。R² 则要小心假象它在测试集上可能因为样本量小波动很大。我的习惯是训练集和测试集都打印一份放在一起对比训练 R² 高、测试 R² 低是过拟合两个都低是欠拟合或特征没选好。3. 把 boston 预测跑起来加载数据到模型保存的完整代码3.1 先看 zip 里的文件与 load_boston 的兼容写法解压这份 zip 后常见的文件组织形式是一个 Jupyter Notebook 或 python 脚本、一份数据文件csv 或自带数据的加载代码、可能还有一个说明文档。不要急着从头到尾跑一遍先确认里面是直接用load_boston()还是从 csv 读取。如果是前者你要先处理 scikit-learn 1.2 移除波士顿数据集的兼容问题。try: from sklearn.datasets import load_boston data load_boston() X, y data.data, data.target print(旧版 API 加载成功特征名:, data.feature_names) except ImportError: import pandas as pd # 从本地 csv 读取csv 可以由 UCI 波士顿房价原数据整理而来 df pd.read_csv(boston_housing.csv) X df.drop(columns[MEDV]).values y df[MEDV].values print(新版 sklearn 已移除 load_boston改用本地 csv 加载)这段兼容代码的意义不只是“让它能跑”而是给你一个通用套路任何项目从旧版本迁移到新版本时入口 API 变了数据源本身不会消失。把数据落成 csv就是给自己留后悔药。另外注意csv 里的MEDV是目标列特征列包括 CRIM、ZN、INDUS、CHAS、NOX、RM、AGE、DIS、RAD、TAX、PTRATIO、B、LSTAT 这 13 个顺序最好和原数据集保持一致否则之后对照系数含义时会混乱。3.2 房价预测的最小闭环训练与评估一次走完拿到 X 和 y 之后完整的训练流程包括切分训练测试集、标准化、训练、预测、评估。这里最关键的一个顺序问题是标准化必须在切分之后拟合绝对不能先对全量数据做标准化再切分否则会造成数据泄漏。数据泄漏会让你的测试分数虚高部署到真实场景立刻翻车。为了从流程上堵住这个坑我一般直接用 Pipeline。from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LinearRegression from sklearn.pipeline import Pipeline from sklearn.metrics import r2_score, mean_squared_error, mean_absolute_error # 切分固定随机种子保证结果可复现 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) # 用 Pipeline 串起标准化和线性回归 pipe Pipeline([ (scaler, StandardScaler()), # 先对训练集拟合均值方差再变换 (lr, LinearRegression()) # 在标准化后的特征上训练 ]) pipe.fit(X_train, y_train) y_pred pipe.predict(X_test) # 评估 print(R2:, r2_score(y_test, y_pred)) print(RMSE:, mean_squared_error(y_test, y_pred, squaredFalse)) print(MAE:, mean_absolute_error(y_test, y_pred))test_size0.2意味着 506 个样本里留约 101 个做测试剩下的 405 个训练这个比例对波士顿这种小数据集是合理的测试集再小评估波动会很大。random_state42的作用是固定切分结果你换机器、重跑脚本得到的训练测试划分完全一致方便排查问题。Pipeline 在这里的核心价值是StandardScaler只在训练集上执行fit拿到均值和方差后在测试集和未来新数据上只做transform。如果你手动写scaler.fit_transform(X)再切分测试集的统计信息早就混进 scaler 里了相当于考试时偷看了答案。正式场合不推荐分开写。虽然scaler StandardScaler()再X_train_scaled scaler.fit_transform(X_train)也行但后续做交叉验证时容易忘记对每一折重新 fitPipeline 逼着你用统一流程这是我认为线性回归 python 实战里最值得养成的习惯。3.3 可视化与模型保存残差图、joblib 与持久化训练评估结束后不要急着关电脑。先画两张图一张是预测值和真实值的散点图另一张是残差图。残差图的横轴是拟合值纵轴是残差y - ŷ它能暴露线性回归是否遗漏了非线性结构——这一点下一章会详细讲。确认模型没大问题后再把模型保存到磁盘这样下次预测不用重新训练。import matplotlib.pyplot as plt import joblib # 画残差图 plt.scatter(y_pred, y_test - y_pred, alpha0.6) plt.axhline(y0, colorred, linestyle--) plt.xlabel(预测房价千美元) plt.ylabel(残差) plt.title(残差图) plt.show() # 保存整个 Pipeline而不是只保存模型 joblib.dump(pipe, boston_lr_pipeline.pkl, compress3)compress3是压缩级别取值范围 0 到 9数字越大压缩率越高但保存和加载越慢。for 一个几十 KB 的模型来说取 3 就够用没必要拉满。保存整个 Pipeline 而不是只保存LinearRegression模型是为了让新数据预测时自动走标准化流程。如果你只保存了lr而丢了scaler预测时还得手动拿着训练集的均值和方差去变换新样本一旦忘记预测结果会偏到离谱。加载模型用joblib.load(boston_lr_pipeline.pkl)然后直接调用.predict(new_data)即可。4. 线性回归实战避坑数据泄漏、共线性与回归假象的 5 条踩坑记录4.1 数据泄漏先标准化再切分测试集分数虚高现象训练集 R² 只有 0.75测试集 R² 却高达 0.82甚至比训练集还好。不懂行的会以为是模型泛化能力超强实际上十有八九是数据泄漏。原因你在切分之前就对全量数据做了标准化StandardScaler用了测试集的均值和方差来做变换测试集信息提前进入了模型。解决把标准化放进 Pipeline或者严格按“先切分再在训练集上 fit再 transform 测试集”的顺序执行。验证方法也很简单把random_state换成另一个值比如 7再跑一遍如果测试分数波动巨大说明流程有问题。4.2 多重共线性删掉一个特征系数符号直接反转现象LSTAT 的系数明明是负数贫民比例越高房价越低但加了某个特征后它变成正数逻辑上说不通。原因波士顿数据里 B、INDUS、TAX、RAD 这几个特征之间相关性极高XᵀX 接近奇异系数估计的方差被放大细微的样本变化就会让系数大幅摆动。解决先看相关系数矩阵再决定要不要删特征或做正则化。import pandas as pd df pd.DataFrame(X, columns[CRIM,ZN,INDUS,CHAS,NOX,RM,AGE,DIS,RAD,TAX,PTRATIO,B,LSTAT]) corr df.corr() # 找出和某个特征相关系数超过 0.7 的特征对 for i in range(len(corr.columns)): for j in range(i1, len(corr.columns)): if abs(corr.iloc[i, j]) 0.7: print(f{corr.columns[i]} ~ {corr.columns[j]}: {corr.iloc[i, j]:.2f})输出里你会看到 TAX 和 RAD 的相关系数在 0.9 以上DIS 和 INDUS、NOX 也高度相关。处理方式有两种一是人工删掉业务上不太重要的那个比如 RAD 和 TAX 留一个二是改用 Ridge 回归通过 L2 正则让系数稳定下来。真实项目里我更推荐 Ridge因为它不需要你手动做特征取舍代价是损失一点可解释性。4.3 离群值与截断值MEDV50 的样本在干扰拟合现象残差图右侧出现一排几乎水平的点预测值在 50 附近堆成一团。原因波士顿数据里 MEDV 大于等于 50 的样本被统一记录为 50这是当年数据采集时的截断操作censoring导致大量真实房价超过 50 的样本被压成一个平台。解决先别急着删。用y_train[y_train 50]看一眼数量如果只有几条可以单独分析如果占比不小说明模型天然无法拟合这个截断要么对目标变量做 log 变换压缩高端值要么明确告诉业务方“模型对 50 千美元以上的预测不可信”。我见过有人把 MEDV50 的样本全部删掉后 R² 升到 0.85但那是自欺欺人——现实预测里你根本不知道新样本会不会超过 50。4.4 残差图呈扇形或 U 形模型缺了非线性项现象残差图不是均匀分布在零线两侧而是呈喇叭口扇形或明显弯曲。原因线性回归假设特征和目标之间是直线关系但波士顿数据里 RM平均房间数和 MEDV 的关系更接近曲线房间数超过 6 之后房价增速变缓一条直线根本描述不了这种形态。解决对特征做多项式扩展比如加 RM² 项再重新训练。这也解释了为什么很多人在跑 LSTM 预测时序数据、或者想用 Transformer 预测正弦数据之前连线性回归的残差图都没看过——先把简单模型的残差诊断做扎实再上复杂模型你会少走很多弯路。4.5 版本迁移scikit-learn 1.2 把 load_boston 移除了现象from sklearn.datasets import load_boston直接报错错误信息写着 “load_boston has been removed from scikit-learn since version 1.2”。原因官方认为该数据集存在伦理问题和数据偏见决定移除。解决不要硬改代码去兼容旧 API而是按 3.1 的做法把数据源换成本地 csv 或者用fetch_openml读取替代版本。我的建议是本地 csv 最稳不依赖网络、不受 sklearn 版本迭代影响这份 zip 里的数据文件就是为这个准备的。这个坑真正教会你的是任何机器学习项目都要把“数据获取”和“模型代码”解耦开。5. 模型诊断进阶学习曲线与交叉验证确认模型不是瞎拟合5.1 学习曲线用一条图区分欠拟合与过拟合模型训练完、R² 看着还行不代表真的没问题。我会再画一张学习曲线横轴是训练样本数量纵轴是 R²分别画出训练集分数和验证集分数随样本量变化的曲线。如果两条线最终都收敛在 0.7 附近且差距很小说明模型已经用足了数据如果训练线很高但验证线一直上不去说明过拟合两条线都贴着 0.5 附近拉不上去则是欠拟合。画法很简单用learning_curve一行就能出数据再交给 matplotlib 渲染。5.2 交叉验证5 折是性价比最高的默认值如果你是在做机器学习线性回归实验或者要给模型一个更可信的评估数字单次 train_test_split 的分数说服力不够。我一般会加一个cross_val_scorefrom sklearn.model_selection import cross_val_score scores cross_val_score( pipe, X, y, cv5, scoringneg_mean_squared_error ) rmse_scores np.sqrt(-scores) print(5折RMSE:, rmse_scores, 均值:, rmse_scores.mean())这里有个容易踩的坑sklearn 的 scoring 约定是“越大越好”所以 MSE 要写成neg_mean_squared_error取负号后计算。cv5 表示把 506 个样本分成 5 份轮流拿一份做验证、其余训练平均分数比单次切分稳健得多。如果数据量更少可以用 cv10 或留一法但波士顿这个量级 5 折足够。5.3 部署前最后一个动作用保存的 Pipeline 预测新样本模型确认没问题后最终要落到“拿到新数据能出预测结果”这一步。由于我们保存的是完整 Pipeline预测代码非常短import joblib pipe joblib.load(boston_lr_pipeline.pkl) new_sample [[0.02, 18, 2.3, 0, 0.5, 6.5, 60, 2.5, 4, 300, 17, 380, 8.5]] pred pipe.predict(new_sample) print(预测房价千美元:, pred[0])注意传入的new_sample必须是 13 个特征、排列顺序和训练时一致的二维数组。Pipeline 会在内部自动完成标准化你不用手动减均值除标准差——这也是当初坚持保存整条 Pipeline 的原因。这几年我摸爬滚打下来最深的教训就是单次测试分数漂亮不算数能稳定复现、能在新数据上不走样才算真跑通。这套流程你完整走一遍比搜二十篇教程都有用希望帮到你。本文还有配套的精品资源点击获取
返回列表