尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

波士顿房价数据集实战:从数据清洗到模型堆叠的完整回归流程

发布时间:2026/9/29 13:55:58

资讯中心
01
ARTICLE

波士顿房价数据集实战:从数据清洗到模型堆叠的完整回归流程

波士顿房价数据集实战:从数据清洗到模型堆叠的完整回归流程
简介这份波士顿房价数据集资源面向机器学习入门者、数据分析学习者以及需要练习回归建模的开发者可用于房价预测、特征工程与模型评估等典型实验场景。压缩包共3个文件包含1个csv数据文件、1个py脚本和1个md说明文档整体约15KB体积轻便便于快速下载与本地运行。csv文件承载波士顿房价的样本特征与目标房价py脚本提供数据读取与建模的参考代码md文档则补充使用说明三者配合可帮助读者省去自行整理数据与搭建代码框架的时间。目前已有1360人学习下载说明该资源在入门练习中具有一定参考价值。读者可借助它熟悉回归任务的完整流程包括数据加载、特征理解、模型训练与结果评估适合作为课程作业、自学练手或项目预研的基础素材。1. 波士顿房价数据集一个被「用烂」却依然值得深挖的回归基准波士顿房价数据集Boston Housing Dataset大概是每个搞机器学习的人绕不开的第一个回归任务。它出现在无数教程、课程作业和入门项目里以至于很多人拿到手就load_boston()一把梭跑个线性回归看到 MSE 零点几就关掉 notebook 了。但如果你真拿它做过完整项目就会发现这个数据集远没有表面那么简单——506 条样本、13 个特征里面藏着伦理争议、特征工程陷阱和模型选择的经典问题。波士顿房价数据集.zip 这个压缩包通常包含的是原始 CSV 或经过整理的版本核心字段包括 CRIM犯罪率、RM房间数、LSTAT低收入人群比例、PTRATIO师生比等目标是预测 MEDV自有住房中位数价格单位千美元。它适合谁适合想系统练一遍「数据清洗→特征分析→模型对比→调参→误差诊断」全流程的人也适合拿它当回归模型鲁棒性测试的基准。但要注意这个数据集因为一个特征的设计问题在 scikit-learn 1.2 之后已经被移除所以你现在拿到的 zip 包很可能就是最后能自由使用的版本之一。接下来我会按实际做项目的顺序把每一步的参数、代码和踩过的坑讲清楚。2. 从 zip 到 DataFrame加载、字段解读与第一轮清洗2.1 解压后的文件结构与字段含义拿到波士顿房价数据集.zip解压后常见的是BostonHousing.csv或boston.csv有时还会带一个data_description.txt。先别急着read_csv用head或文本编辑器看一眼列名。标准字段有 14 列CRIM、ZN、INDUS、CHAS、NOX、RM、AGE、DIS、RAD、TAX、PTRATIO、B、LSTAT、MEDV。其中 CHAS 是查尔斯河虚拟变量1 表示临河RAD 是径向公路可达性指数B 是黑人比例换算值——这个字段就是后来引发伦理争议的根源做项目时建议直接删掉避免模型学到不该学的偏见。MEDV 是目标变量注意它在中位数附近有截断上限 50.0这会导致高价位样本被压缩回归时残差会呈现异方差。加载代码很简单但有几个参数必须显式设置import pandas as pd import numpy as np # 读取时指定列名防止原文件无表头 columns [CRIM,ZN,INDUS,CHAS,NOX,RM,AGE,DIS,RAD,TAX,PTRATIO,B,LSTAT,MEDV] df pd.read_csv(BostonHousing.csv, headerNone, namescolumns, na_values?) # 检查缺失和重复 print(df.isnull().sum()) print(df.duplicated().sum()) # 删除伦理争议字段 B并确认 CHAS 是类别型 df df.drop(columns[B]) df[CHAS] df[CHAS].astype(category)逻辑说明headerNone是因为很多 zip 里的 CSV 没有表头直接读会把第一行数据当列名。na_values?是防止原始数据用问号表示缺失。删除 B 列不是可选项是必须做的——这个特征的设计逻辑本身就有问题保留它会让模型输出不可解释的结果。CHAS 转 category 是为了后续做 one-hot 或直接当类别特征处理避免被当成连续值计算距离。参数说明read_csv的header参数根据实际文件调整如果文件自带表头就设header0。na_values可以接受列表比如[?, NA, null]。删除 B 列后特征数从 13 降到 12目标列不变。2.2 异常值检测与目标变量截断处理清洗完字段后先看目标变量 MEDV 的分布。用df[MEDV].describe()会发现最大值是 50.0而且有 16 个样本正好等于 50.0。这不是巧合是原始数据对高价房做了截断。如果你直接拿它做回归模型会低估高价房残差图会在右侧出现明显下弯。常见做法有两种一是删掉这些截断样本二是保留但用 Tobit 回归或分位数回归。我一般会先保留但在评估时单独看这部分样本的误差。异常值检测用 IQR 或 Z-score 都行但波士顿数据集里 CRIM 和 ZN 的分布极度偏斜Z-score 会误杀。推荐用 IQR 对每个特征做一遍def iqr_outlier_mask(series): q1, q3 series.quantile([0.25, 0.75]) iqr q3 - q1 lower q1 - 1.5 * iqr upper q3 1.5 * iqr return (series lower) | (series upper) outlier_counts {} for col in df.select_dtypes(include[np.number]).columns: if col ! MEDV: outlier_counts[col] iqr_outlier_mask(df[col]).sum() print(outlier_counts)逻辑说明IQR 方法对偏斜分布更稳健1.5 倍是常规阈值。输出会显示 CRIM、ZN、DIS 等列有较多离群点但不要急着删——这些可能是真实的高犯罪率区域或远距离社区删了会丢失信息。我的习惯是先把离群点标记出来在后续建模时用 RobustScaler 或树模型来降低影响。参数说明quantile([0.25, 0.75])计算四分位数1.5可以改成3.0来放宽阈值。如果某列离群点超过 10%建议先做对数变换再检测。3. 特征工程把 12 个字段变成模型真正能用的输入3.1 偏斜特征的对数变换与标准化选择波士顿数据集里 CRIM、ZN、DIS、LSTAT 的偏度都大于 1尤其是 CRIM 偏度接近 5。线性模型对偏斜特征很敏感树模型虽然不要求正态但对数变换后分裂点会更合理。我一般对正偏特征做log1p变换对 LSTAT 这种本身有意义的比例值也做一次。变换后再决定要不要标准化如果后面用线性回归、SVR、KNN必须标准化如果用随机森林、XGBoost可以跳过。from sklearn.preprocessing import RobustScaler, FunctionTransformer from sklearn.compose import ColumnTransformer from sklearn.pipeline import Pipeline skewed_cols [CRIM, ZN, DIS, LSTAT] other_num_cols [INDUS, NOX, AGE, RAD, TAX, PTRATIO, RM] # 对偏斜列做 log1p其他数值列做 RobustScaler preprocessor ColumnTransformer( transformers[ (log, FunctionTransformer(np.log1p, validateTrue), skewed_cols), (scale, RobustScaler(), other_num_cols), (cat, passthrough, [CHAS]) ], remainderdrop )逻辑说明FunctionTransformer(np.log1p)对数据做 log(1x)避免 x0 时出错。RobustScaler用中位数和 IQR 缩放比 StandardScaler 更抗离群点。CHAS 直接 passthrough后续模型自己处理类别。整个 preprocessor 可以塞进 Pipeline避免训练集和测试集变换不一致。参数说明validateTrue会检查输入是否为 2D 数组建议开启。如果某列有负值log1p 会报错需要先确认数据范围。RobustScaler 的quantile_range默认是 (25.0, 75.0)可以改成 (10.0, 90.0) 来适应更极端分布。3.2 构造交互特征与领域知识注入原始 12 个特征直接丢给模型R² 大概在 0.7 左右。加几个交互项能明显提升RM×LSTAT房间数×低收入比例反映购买力NOX×DIS污染×距离反映环境质量TAX×PTRATIO税率×师生比反映社区投入。这些不是拍脑袋是领域常识。构造完用PolynomialFeatures会爆炸所以手动加更可控。def add_interactions(df): df df.copy() df[RM_LSTAT] df[RM] * df[LSTAT] df[NOX_DIS] df[NOX] * df[DIS] df[TAX_PTRATIO] df[TAX] * df[PTRATIO] df[AGE_LSTAT] df[AGE] * df[LSTAT] return df df add_interactions(df)逻辑说明交互项要在划分训练测试集之前构造但变换参数如均值、标准差只能在训练集上拟合。所以正确顺序是先划分再在训练集上 fit preprocessor然后 transform 训练集和测试集。上面这个函数只是构造列不涉及拟合可以提前做。参数说明交互项不是越多越好每加一个都会增加过拟合风险。建议先用 Lasso 或树模型的特征重要性筛一遍保留重要性排名前 60% 的。如果发现 RM_LSTAT 的系数特别大说明它捕获了主要非线性关系。4. 模型对比与调参从线性回归到梯度提升的实战路径4.1 基线模型线性回归、岭回归与 Lasso 的快速对比别一上来就 XGBoost。先用线性回归跑一个基线看 R² 和残差分布。然后岭回归和 Lasso 各跑一遍对比系数收缩情况。波士顿数据集特征不多Lasso 能直接做特征选择很适合看哪些字段真正有用。from sklearn.linear_model import LinearRegression, Ridge, Lasso from sklearn.model_selection import cross_val_score, KFold from sklearn.pipeline import Pipeline kf KFold(n_splits5, shuffleTrue, random_state42) models { OLS: LinearRegression(), Ridge: Ridge(alpha1.0), Lasso: Lasso(alpha0.01, max_iter10000) } for name, model in models.items(): pipe Pipeline([(prep, preprocessor), (model, model)]) scores cross_val_score(pipe, X_train, y_train, cvkf, scoringr2) print(f{name}: R2{scores.mean():.4f} (/- {scores.std():.4f}))逻辑说明cross_val_score用 5 折交叉验证比单次划分更稳。Pipeline 保证每次 CV 的预处理只在训练折上拟合避免数据泄露。Lasso 的max_iter要调大否则可能不收敛。参数说明Ridge 的alpha从 0.1 到 10 试Lasso 的alpha从 0.001 到 0.1 试。如果 Lasso 把大部分系数压到 0说明 alpha 太大。理想情况是保留 5-8 个非零系数。4.2 树模型调参随机森林与梯度提升的关键参数树模型在波士顿数据集上通常比线性模型好但调参不能瞎 grid search。随机森林重点调n_estimators、max_depth、min_samples_leaf梯度提升重点调learning_rate、n_estimators、subsample。我一般先用RandomizedSearchCV粗搜再在最优附近细搜。from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor from sklearn.model_selection import RandomizedSearchCV param_dist { model__n_estimators: [100, 200, 300, 500], model__max_depth: [None, 5, 10, 15, 20], model__min_samples_leaf: [1, 2, 4, 8], model__max_features: [sqrt, log2, 0.5] } rf_pipe Pipeline([(prep, preprocessor), (model, RandomForestRegressor(random_state42))]) search RandomizedSearchCV(rf_pipe, param_dist, n_iter40, cv5, scoringr2, random_state42, n_jobs-1) search.fit(X_train, y_train) print(search.best_params_, search.best_score_)逻辑说明RandomizedSearchCV比 GridSearch 快40 次迭代通常能找到不错组合。max_features用 sqrt 或 log2 能降低树间相关性。min_samples_leaf调大可以防止过拟合但太大会欠拟合。参数说明n_iter根据计算资源调40-100 都行。n_jobs-1用满 CPU。如果发现最优max_depth是 None说明树可以长到纯但要看验证集分数是否过拟合。梯度提升的learning_rate建议从 0.05 开始n_estimators配合调通常 200-500 之间。4.3 用 SHAP 做模型解释与特征重要性验证调完参别只看 R²用 SHAP 看每个特征对预测的贡献。波士顿数据集里 RM、LSTAT、DIS 通常是前三但交互项可能挤进来。SHAP 还能发现非线性关系比如 RM 在 6-7 之间对房价提升最快。import shap best_model search.best_estimator_ X_train_transformed best_model.named_steps[prep].transform(X_train) explainer shap.TreeExplainer(best_model.named_steps[model]) shap_values explainer.shap_values(X_train_transformed) shap.summary_plot(shap_values, X_train_transformed, feature_names...)逻辑说明TreeExplainer 对树模型快且准。X_train_transformed是预处理后的数组特征名需要从 preprocessor 里提取。summary_plot 会显示每个特征的 SHAP 值分布颜色代表特征值高低。参数说明如果特征名对不上用preprocessor.get_feature_names_out()获取。SHAP 计算量随样本数增加506 条很快不用抽样。5. 避坑与排查波士顿房价数据集项目里最容易翻车的 5 个点5.1 现象scikit-learn 1.2 加载报错提示load_boston已移除原因scikit-learn 因为 B 字段的伦理问题在 1.2 版本彻底删除了load_boston。很多老教程还在用直接复制就报错。解决不要降级 scikit-learn。直接从 zip 里的 CSV 读或者用pandas.read_csv加载。如果只有load_boston的代码把数据导出成 CSV 再读。记住任何依赖load_boston的代码都需要重写加载部分。5.2 现象模型在训练集上 R² 0.95测试集只有 0.6原因典型过拟合。要么是交互项加太多要么是树模型max_depth太深要么是预处理在划分前做了导致泄露。解决检查 Pipeline 是否在交叉验证内部。交互项用 Lasso 筛一遍。树模型限制max_depth在 10 以内min_samples_leaf至少 2。如果还不行用learning_curve看学习曲线确认是过拟合还是欠拟合。5.3 现象MEDV 预测值大量集中在 20-25高价房全被低估原因MEDV 在 50.0 处截断模型学到的是截断后的分布。另外 LSTAT 和 RM 的非线性关系没捕获。解决要么删掉 MEDV50 的样本要么用分位数回归或 Tobit 模型。更简单的做法是加一个二分类任务先预测是否高于 30再对高于 30 的样本单独回归。树模型对截断不敏感但线性模型必须处理。5.4 现象CHAS 字段做 one-hot 后维度爆炸或系数异常原因CHAS 只有 0 和 1但有些版本里它被当成连续值或者 one-hot 后和截距项共线。解决CHAS 直接用astype(category)或做 one-hot 后删掉一列。如果模型有截距one-hot 必须dropfirst。树模型不需要 one-hot直接当类别特征处理即可。5.5 现象交叉验证分数波动极大每次运行结果不一样原因没有固定随机种子或者 KFold 没 shuffle。波士顿数据集只有 506 条划分方式对结果影响很大。解决所有涉及随机的步骤都设random_state包括KFold(shuffleTrue, random_state42)、模型初始化、RandomizedSearchCV。如果波动还是大用 10 折或重复交叉验证。另外检查是否有离群点被分到不同折里导致分数跳变。6. 进阶技巧用目标变换和堆叠模型把 R² 推到 0.9 以上如果你已经把前面的流程跑通R² 大概在 0.85-0.88 之间。想再往上走有两个方向值得试目标变换和模型堆叠。目标变换不是简单的 log(MEDV)而是用 Box-Cox 或 Yeo-Johnson 自动找最优变换参数。波士顿数据集的 MEDV 轻度右偏Box-Cox 的 lambda 通常在 0.2-0.4 之间。变换后残差更接近正态线性模型的 R² 能提升 2-3 个点。from sklearn.preprocessing import PowerTransformer from sklearn.compose import TransformedTargetRegressor # 对目标做 Yeo-Johnson 变换再套模型 model TransformedTargetRegressor( regressorGradientBoostingRegressor(n_estimators300, learning_rate0.05, max_depth4, random_state42), transformerPowerTransformer(methodyeo-johnson) ) pipe Pipeline([(prep, preprocessor), (model, model)]) pipe.fit(X_train, y_train)逻辑说明TransformedTargetRegressor在训练时对 y 做变换预测时逆变换回来。yeo-johnson支持负值比 Box-Cox 更通用。梯度提升的参数是粗调后的经验值learning_rate0.05配 300 棵树通常比 0.1 配 150 棵更稳。参数说明PowerTransformer的standardizeTrue会同时做标准化建议开启。如果 y 有零或负值只能用 yeo-johnson。变换后的 R² 是在原始尺度上算的不用担心解释问题。堆叠模型是另一个提升点。用线性回归、随机森林、梯度提升作为基模型再用 Ridge 做元学习器。基模型的预测值作为新特征元学习器学怎么组合。注意堆叠必须用StackingRegressor并设置cv5否则基模型在训练集上的预测会泄露。from sklearn.ensemble import StackingRegressor estimators [ (lr, Ridge(alpha1.0)), (rf, RandomForestRegressor(n_estimators300, max_depth10, random_state42)), (gb, GradientBoostingRegressor(n_estimators300, learning_rate0.05, max_depth4, random_state42)) ] stack StackingRegressor(estimatorsestimators, final_estimatorRidge(alpha1.0), cv5) stack_pipe Pipeline([(prep, preprocessor), (model, stack)]) stack_pipe.fit(X_train, y_train)逻辑说明StackingRegressor内部用 cv5 生成基模型的 out-of-fold 预测避免泄露。元学习器用 Ridge 是因为它简单且能处理共线性。基模型要有多样性线性树提升的组合比三个树模型好。参数说明final_estimator可以换成 Lasso 或 ElasticNet但 Ridge 最稳。如果基模型太多元学习器容易过拟合3-4 个足够。堆叠后 R² 通常能到 0.90-0.92但训练时间翻倍看值不值得。最后说一个我踩过的坑别在测试集上反复调参。波士顿数据集小你每看一次测试集分数就多一分过拟合的风险。我的习惯是先把训练集切出 20% 做验证集所有调参在验证集上做测试集只在最后跑一次。如果测试集分数和验证集差超过 0.05说明调参过头了回退到更简单的模型。这个数据集虽然老但用来练「怎么不骗自己」特别合适。希望帮到你。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。