波士顿房价预测全流程解析:从线性回归到集成学习实战
简介本资源是一份面向高校机器学习课程学习者与期末项目实践者的高分回归分析作业聚焦波士顿房价预测这一经典教学案例系统实现多种线性回归算法并完成全流程建模验证。压缩包共12个文件944KB包含3个CSV数据与结果文件、3个核心Python脚本含模型训练、测试与示例调用、3张关键可视化图表系数分析、预测散点、训练曲线、2个说明文本及1份Markdown文档覆盖数据预处理、BGD/SGD梯度下降、岭回归与LASSO四种算法实现、多维度评估对比及可复现的实验报告框架。已有120人学习下载提供从数据加载到模型部署的完整闭环代码、清晰的结果输出格式如coefficient_analysis.csv与model_comparison.csv以及开箱即用的requirements.txt依赖管理特别适合课程设计、大作业提交与算法原理理解巩固。 波士顿房价预测这个题目我在课程作业里见过在面试准备里也见过甚至在一些公司的技术方案评审里都见过。一个上世纪七十年代的数据集能一直火到今天不是没有原因的。它的数据量不大506条特征数量适中13个目标变量是连续值非常适合用来讲清楚回归问题的完整流程。无论是刚开始学机器学习的本科生还是在准备算法岗面试的人拿它来练手都是很顺的选择。这篇博文不打算只贴一份代码了事。我会把从数据探索、特征处理、模型选型到结果分析的完整链路都拆开讲一遍重点解释每一步为什么要这么做以及哪些地方是拿分关键。我还会放出我实际跑的代码和对应的输出说明方便你直接对照。文末整理了一份常见问题和排查方案很多是我自己踩过的坑希望能帮你少走弯路。1. 项目全貌波士顿房价预测到底在预测什么1.1 核心需求解析这是一道标准的回归题先把这个项目的本质说清楚。波士顿房价数据集里的每条样本描述的是一处房产所在区域的各种统计信息目标值MEDV是该区域业主自住房的中位价格单位是千美元。所以这个项目的任务就是根据给定区域的犯罪率、房间数、交通便利度等特征预测该区域的房价中位数。数据集中包含了13个特征下面我按我处理的顺序整理了一个清单方便你对照着认识数据特征名含义类型对房价的直观影响CRIM城镇人均犯罪率连续值通常负相关犯罪率高房价低ZN占地超过25000平方英尺的住宅用地比例连续值较大时多为高档社区可能正相关INDUS城镇非零售业用地比例连续值工业区多的地方房价偏低CHAS是否邻近查尔斯河1是0否二分类景观溢价通常正相关NOX一氧化氮浓度每千万分之一连续值空气污染越重房价越低RM每栋住宅的平均房间数连续值经典正相关特征越大房价越高AGE建于1940年之前的自住单位比例连续值老房子多的地方房价偏低DIS到波士顿主要就业中心的加权距离连续值距离越远房价越低RAD到径向高速公路的可达性指数有序值可达性好房价可能更高TAX每万美元房产税率连续值税率高增加持有成本负相关PTRATIO城镇师生比连续值教育资源越好比值越低房价越高B黑人比例相关指标1000(Bk-0.63)^2连续值按当年统计口径取值越高房价不一定越高LSTAT低收入阶层人口比例连续值强负相关这个特征很关键目标值MEDV的取值范围大约在5到50之间也就是5千到5万美元。放在七十年代这属于正常房价放在今天看数值本身没有实际对照意义但数据集的结构和特征间的关系仍然非常适合教学。这类题目的标准做法是监督学习里的回归任务输入是13维特征向量输出是连续值。评分时大家通常看三个指标均方误差MSE、平均绝对误差MAE和决定系数R²。R²越接近1说明模型解释力越强两个误差越小说明预测越准。1.2 高分作业的评价逻辑代码之外还能做什么很多同学把一个线性回归代码跑通就觉得完事了其实从拿分角度看这远远不够。我当年做这类作业时观察到的评卷偏好大致是这样的基础分模型能跑通指标有输出。这一步只要代码没问题就能拿到但只拿这部分通常是及格到中等水平。加分点一有清晰的探索性数据分析EDA过程。比如画出MEDV的分布直方图、特征之间的相关性热力图能解释哪些特征和房价高度相关这就说明你真的在理解数据而不是在套代码。加分点二有合理的模型对比。不是只跑一个线性回归就结束而是至少对比线性回归、岭回归和一个树模型随机森林或梯度提升树用同一个测试集比较三个模型的指标。加分点三文档里能写清楚“为什么”。比如为什么RM和LSTAT对房价影响大为什么要做特征标准化岭回归的正则化参数alpha怎么选的等等。这比堆代码有说服力得多。说白了波士顿房价预测这项作业的设计初衷不是让你背一个模型而是让你完整走一遍“分析数据—设计模型—评估结果—得出解释”的流程。后面我讲的都是按这个逻辑来的。2. 数据先行拿到数据别急着建模2.1 数据探索要做哪些事我第一次接触这个数据集时第一反应就是一行load_boston()然后关掉数据列直接开训。后来发现这种做法的一大问题就是你根本不知道模型学到了什么规律出了指标也不确定靠不靠谱。正确的打开方式分成三块。**第一块确认数据形态和缺失情况。**用df.info()和df.describe()先看一遍确认506行没有缺失值确认特征的量纲差异非常大——比如CRIM是从0.006到近90的大跨度而CHAS只是0和1。这个发现直接影响后面要不要做标准化。**第二块看目标变量的分布。**画MEDV的直方图你会发现这个分布并不是教科书式的正态分布而是有些右偏甚至尾部有点厚。右偏数据在进行线性回归时残差可能不满足正态性假设所以可以考虑对MEDV取对数或者至少了解一下这一点。我实际对比过取对数之后模型在RMSE指标上有小幅提升但解释起来麻烦作业里如果追求简洁可以不做但在文档里写出来说明你注意到了这个现象本身就是加分项。**第三块看特征与目标的关系。**最直接的方式是画一个相关系数矩阵热力图或者对每个特征和目标做散点图。跑下来之后结论基本是一致的MEDV和RM房间数正相关最强和LSTAT低收入比例负相关最强。这意味着在做线性模型时这两个特征的权重会比较大解释模型时就从这里切入。2.2 特征工程的几个关键点在这个项目里特征工程看起来简单实际上有几个容易踩的坑。缺失值处理这个数据集本身没有缺失值只需要检查确认即可。如果遇到自己处理的数据有缺失可以先看缺失比例用均值/中位数填充还是丢弃要看场景。离群点与异常值比如CRIM这个特征的最大值接近90而中位数只有0.256说明存在一些犯罪率极高的异常区域。对于线性模型来说这种极值会把模型拉偏。处理方式有两种一是直接删除少数极端样本需要说明理由二是做标准化缓解但无法根除。我采用的做法是保留数据但在文档里分析这些点因为删除样本会影响可复现性。特征标准化线性回归、岭回归、Lasso这类基于距离或梯度下降的模型对特征尺度敏感而树模型不敏感。所以我的标准流程是数据划分之后在训练集上做StandardScaler拟合再同时应用到训练集和测试集。注意不能先标准化再划分否则会有数据泄漏的嫌疑作业里这点会被扣分。在做特征工程时我最想强调的一点是不要做无意义的特征删除。很多人为了追求模型的简洁性直接删掉一些看似无关的特征比如ZN、INDUS觉得它们对MEDV的相关系数低。但多变量之间是存在交互效应的A和Y没线性关系不代表A在B存在时对Y没有贡献。所以我的习惯是先让模型自己决定特征重要性而不是人工拍脑袋删列。3. 模型选型与调参从线性回归到集成学习3.1 基准模型先把线性回归跑通对于一个回归任务线性回归永远是那个“你总得先看看它表现如何”的基准模型。它的形式很简单目标值等于特征的线性组合加上偏置项。MEDV w1*CRIM w2*ZN ... w13*LSTAT b线性回归的好处是简单、计算快、可解释性强你可以直接把权重带回去看哪些特征对房价影响大。坏处是它假设特征与目标存在线性关系而现实问题很少有完全线性的。波士顿房价数据里的RM和MEDV的关系其实是有一定非线性成分的这一点先记在心里后面集成模型的效果更好就能说明问题。跑这个模型时有一个指标容易被忽略残差图。训练完之后把预测值和真值的残差画出来如果残差围绕0随机分布说明模型没有明显的系统性偏差如果出现漏斗形状比如预测值越大的时候残差也越大就说明模型在极端值上的表现有改进空间。我在第一次跑时就发现了这个现象这个观察在后面对比树模型时非常有用。3.2 正则化家族岭回归与Lasso的选择线性回归的下一步就是考虑要不要加正则化。为什么要加因为13个特征之间多少存在多重共线性前面说的相关性热力图可以看出来比如TAX和RAD的相关系数很高DIS和NOX也显著负相关。这种共线性会让普通最小二乘法的参数估计方差变大模型泛化能力下降。岭回归Ridge使用L2正则化把权重的平方和加进损失函数约束权重不能太大。它保留了所有特征只是整体缩小权重适合特征间相关性较强的情形。Lasso套索回归使用L1正则化有特征选择能力会把不重要的特征权重压成0。对于这个数据集我实际跑下来岭回归比Lasso稳定因为数据本身没有多到需要做强特征筛选的地步强行让权重稀疏没有明显收益。调参上只需要关注alpha这一个值。alpha太大模型会过于平滑把有效信号也抹掉alpha太小则又退化为普通线性回归。我用的是网格搜索加交叉验证alpha的候选值取[0.001, 0.01, 0.1, 1, 10, 100]最后选出来的是1到10这个区间说明适度的正则化有帮助但不需要太强。3.3 树模型的参数调优随机森林和梯度提升到了这一步才算进入真正能拉开分数差距的环节。树模型尤其是随机森林和梯度提升树是处理这类表格数据的利器。它们能捕捉非线性关系不做特征标准化也一样跑而且往往效果比线性模型好一截。随机森林的关键参数n_estimators树的数量一般500到1000就够再大收益递减。max_depth树的最大深度不限制容易过拟合建议在10到20之间做网格搜索。min_samples_split内部节点再划分所需最小样本数默认2容易过拟合可以放大到5或10。random_state一定要设固定值保证结果可复现。我统一用42。梯度提升树的关键参数n_estimators提升轮数同样500左右。learning_rate学习率一般从0.05到0.1之间调。学习率越小需要更多树来拟合。max_depth在梯度提升里通常比随机森林小3到5就够因为每棵树都在拟合残差深度太深容易过拟合。subsample每次迭代用多少比例的样本设成0.8能增加随机性降低过拟合。我用随机森林跑出来的R²能到0.88左右梯度提升树可以到0.91左右比线性回归高出一截。对比下来就能发现线性模型对非线性关系的建模能力确实有限而树模型在这类中低维表格数据上的优势是实实在在的。3.4 模型对比的坑指标口径要统一这里提醒一个我在作业评审中经常看到的问题不同模型用不同指标评价或是在不同的数据划分上比较导致结论没有可比性。我处理这个项目的固定设置是整个数据集按7:3划分训练集和测试集。所有模型必须使用同一个划分结果我通常会在划分后把数据存成X_train, X_test, y_train, y_test后续模型只和这些变量打交道。统一用RMSE、MAE、R²三个指标评价。随机种子全部固定为42或者你选一个数字关键是一致。这样的好处是你对比的是模型本身的能力差异而不是随机划分带来的波动。如果不固定随机种子同一模型两次跑出来的R²可能相差0.02以上写在报告里就是经不起推敲的结论。4. 完整的代码实现从读数据到结果可视化4.1 环境准备与数据加载我的运行环境是Python 3.9 scikit-learn 1.0.2 pandas 1.4.2 matplotlib 3.5.1。数据加载这一块有一个非常严肃的坑要先说清楚。早期版本的scikit-learn可以直接用from sklearn.datasets import load_boston加载但scikit-learn 1.2之后这个接口被移除了。如果你用的是较新版本一运行就会报错ImportError: load_boston has been removed from scikit-learn since version 1.2。这种“教材代码跑不通”的情况每年都有很多人踩。我的处理方式是从外部直接加载CSV文件这样不依赖sklearn版本也更贴近真实项目的数据读取方式。数据集可以从一些镜像源获取我这里的代码兼容两种加载方式你按需选即可。import pandas as pd import numpy as np from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LinearRegression, Ridge, Lasso from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score import matplotlib.pyplot as plt # 方式一文件加载推荐前提是本地已有boston_housing.csv df pd.read_csv(boston_housing.csv) # 方式二如果环境支持旧接口仅适用于sklearn 1.2 # from sklearn.datasets import load_boston # boston load_boston() # df pd.DataFrame(boston.data, columnsboston.feature_names) # df[MEDV] boston.target # 分离特征与目标 X df.drop(MEDV, axis1) y df[MEDV]4.2 划分数据集与标准化数据划分的核心原则是测试集必须像一个“从没见过的客人”。所以所有从数据中学到的参数包括标准化的均值和方法都只能在训练集上计算。# 划分训练集和测试集 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42 ) # 标准化先fit训练集再transform测试集 scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test)注意fit和transform这两个方法的使用顺序不能弄反。fit_transform只用一次在训练集上测试集只用transform。如果图省事直接对整个X做标准化再划分会让测试集的信息在训练阶段就被模型间接“偷看”到了这在严谨的作业评审中是会被指出的问题。4.3 模型训练与评估这里我写一个统一的训练和评估函数方便后续对比多个模型。def train_evaluate(model, X_train, y_train, X_test, y_test, model_name): model.fit(X_train, y_train) y_pred model.predict(X_test) rmse np.sqrt(mean_squared_error(y_test, y_pred)) mae mean_absolute_error(y_test, y_pred) r2 r2_score(y_test, y_pred) print(f{model_name} 结果) print(f RMSE: {rmse:.4f}) print(f MAE : {mae:.4f}) print(f R² : {r2:.4f}) print(- * 40) return model, y_pred # 1. 线性回归 lr, y_pred_lr train_evaluate( LinearRegression(), X_train_scaled, y_train, X_test_scaled, y_test, Linear Regression ) # 2. 岭回归 ridge Ridge(alpha1.0) ridge, y_pred_ridge train_evaluate( ridge, X_train_scaled, y_train, X_test_scaled, y_test, Ridge Regression ) # 3. 随机森林 rf RandomForestRegressor(n_estimators500, max_depth15, min_samples_split5, random_state42) rf, y_pred_rf train_evaluate( rf, X_train, y_train, X_test, y_test, Random Forest ) # 4. 梯度提升树 gbr GradientBoostingRegressor(n_estimators500, learning_rate0.05, max_depth4, random_state42) gbr, y_pred_gbr train_evaluate( gbr, X_train, y_train, X_test, y_test, Gradient Boosting )跑完能明显看到线性回归和岭回归的R²都在0.72~0.78左右随机森林到0.85梯度提升到0.90左右。这里有个细节树模型使用原始特征未标准化即可因为树模型只关心特征的顺序关系不关心尺度。4.4 两个可视化预测对比和特征重要性可视化的意义不只是“报告好看”它还能帮你发现问题。第一个图是预测值与真实值的散点图。理想情况是点都落在yx这条对角线上偏离越大说明误差越大。我画出来的图中梯度提升树的结果里点更密集地聚在对角线附近而线性回归在房价较高比如大于35的区域明显偏离这就是前面说的“线性模型对极端值拟合不足”的直观证据。第二个图是特征重要性。随机森林和梯度提升树都提供feature_importances_属性直接画条形图就行。importances gbr.feature_importances_ feature_names X.columns indices np.argsort(importances)[::-1] plt.figure(figsize(10, 6)) plt.title(Feature Importances - Gradient Boosting) plt.bar(range(len(importances)), importances[indices]) plt.xticks(range(len(importances)), [feature_names[i] for i in indices], rotation45) plt.tight_layout() plt.show()重要提醒特征重要性排序只是模型层面的选择不完全等于真实因果。LSTAT和RM排在最前是这个数据集的经典结论和相关性分析是一致的。写文档时不要写“LSTAT是房价的唯一决定因素”这类话而要写成“模型认为LSTAT对预测的贡献最大这与其和MEDV的强负相关一致”。5. 常见问题与排查技巧实录5.1 问题速查表每年做这个项目的人都会遇到类似的问题。我整理了一份列表覆盖了我自己以及周围人踩过的常见坑现象可能原因解决方案load_boston()报错ImportErrorscikit-learn版本过高接口被移除改用CSV加载或fetch_openml方式标准化后模型指标还是没提升标准化不是万能药树模型可能更适合对比是否使用非线性模型模型在测试集R²很高但预测图有明显偏差过拟合模型在训练集太好了降低max_depth增加min_samples_split加强正则化同一模型多次运行指标不一致随机森林/梯度提升的随机性固定random_stateLSTAT和RM权重很高但决策失败存在共线性或极端值影响做异常值分析尝试岭回归梯度提升树训练时间过长n_estimators过大或max_depth过大先用小轮数测试再逐步加大5.2 一个隐藏分数点交叉验证如果你的文档只写了“用train_test_split划分”那只是一个常见做法。但严谨一点的作业会期望看到交叉验证的结果。因为单次划分的结果可能受到随机因素影响交叉验证能提供更稳定的模型表现估计。我常用的是5折交叉验证再用网格搜索挑参数这里给一个示例from sklearn.model_selection import cross_val_score, GridSearchCV # 候选参数 param_grid { n_estimators: [100, 300, 500], max_depth: [5, 10, 15], min_samples_split: [2, 5, 10] } gbr_cv GradientBoostingRegressor(random_state42) grid_search GridSearchCV( gbr_cv, param_grid, cv5, scoringneg_root_mean_squared_error, n_jobs-1 ) grid_search.fit(X_train, y_train) print(最优参数, grid_search.best_params_) print(最优交叉验证RMSE, -grid_search.best_score_)注意scoringneg_root_mean_squared_errorsklearn会把负号附在误差前面所以最终要取负值才是常规RMSE。网格搜索跑完后再对测试集进行一次最终评估报告里的结论就会非常扎实。5.3 写文档时容易被忽略的高分细节这些内容和代码本身关系不大但直接影响你的项目评分每个图的上面必须有图解释不能只贴图。比如相关性热力图写完至少写一两句“从图中可以看出RM与MEDV正相关LSTAT与MEDV负相关因此这两个特征可能是预测的关键”。模型对比表格是必备项。把线性回归、岭回归、随机森林、梯度提升的RMSE/MAE/R²放在同一张表里。读者扫一眼就能看出优劣差异。结论部分不要只写“随机森林最好”要交代为什么。比如“树模型能捕捉非线性关系而房价与部分特征的关系明显非线性所以树模型优于线性模型”。代码注释要写“为什么”而不是“是什么”。比如不要在X_train_scaled scaler.fit_transform(X_train)边上写“标准化训练集”而要写“在训练集上拟合标准化参数避免数据泄漏”。最后再聊几句波士顿房价这个数据集放在今天看有很多值得讨论的地方比如某些特征定义带有时代局限性甚至有人指出它存在数据方面的话语争议。但作为教学用例它依然是回归任务里非常标准的“第一课”。你把这个项目的流程走完后面换到任何其他回归数据集框架都是通用的先做数据探索再做特征工程然后从基准模型开始逐步尝试更复杂的模型最后统一口径评估并解释结果。我个人做这个项目最大的体会是分数不是看谁跑出来的R²高而是看你的分析过程有没有逻辑闭环。模型可以不是最优的但你得说清楚你为什么选用它、它在哪些情况下表现好、哪些位置还有改进空间。这个过程练出来的就是做真实机器学习项目最核心的能力——结构化地解决问题。代码我建议你拿过去之后一行一行敲一遍不要直接复制粘贴跑完就关。自己敲的时候你才会注意到每个参数怎么来、每行代码干了什么。等你能脱离参考代码独立写出完整流程的时候这门课基本就稳了。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →