ARTICLE DETAIL

资讯详情

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

波士顿房价预测实战:线性回归、随机森林与XGBoost对比

波士顿房价预测实战:线性回归、随机森林与XGBoost对比 标题已经剧透我先把完整代码扔上来镇楼。这是一份可以直接复跑的波士顿房价预测实战脚本从数据加载、缺失值检查、相关性可视化到线性回归、随机森林、XGBoost三套模型的训练与评估覆盖了回归任务最常见的一整条链路。波士顿房价预测是机器学习入门的经典题目数据集不大、特征好解释、模型效果反馈快非常适合用来建立完整建模流程的肌肉记忆。这份代码适合刚学完Python和sklearn、想完整跑通一个项目的朋友也适合想快速回顾回归建模流程的人。我先把代码放在最前面接下来的内容再逐段拆解设计逻辑和踩坑点。1. 先扔完整代码可以直接复跑的波士顿房价预测脚本1.1 代码全文# -*- coding: utf-8 -*- 波士顿房价预测实战完整代码 包含: 数据加载 - EDA - 数据预处理 - 三模型训练对比 - 可视化 运行环境: Python 3.9, sklearn, xgboost, matplotlib, seaborn import warnings import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns from sklearn.datasets import fetch_openml from sklearn.model_selection import train_test_split, cross_val_score from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LinearRegression from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score from xgboost import XGBRegressor warnings.filterwarnings(ignore) # 1. 加载数据 def load_data(): try: data fetch_openml(nameboston, version1, as_frameTrue) df data.frame df.columns data.feature_names [MEDV] return df except Exception as e: print(在线加载失败请准备本地文件 boston_housing.csv列名应与特征名一致。) print(错误信息:, e) df pd.read_csv(boston_housing.csv) return df df load_data() print(数据集形状:, df.shape) print(\n前5行数据:) print(df.head()) print(\n数据类型检查:) print(df.info()) print(\n描述性统计:) print(df.describe().T) # 2. 缺失值检查与删除 print(\n缺失值统计:) print(df.isnull().sum().sum()) df df.dropna() # 3. 相关性分析 plt.figure(figsize(12, 10)) sns.heatmap(df.corr(), annotTrue, fmt.2f, cmapcoolwarm, squareTrue) plt.title(特征相关性热力图) plt.tight_layout() plt.savefig(correlation_heatmap.png, dpi150, bbox_inchestight) plt.show() # 4. 划分训练集与测试集 X df.drop(MEDV, axis1) y df[MEDV] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) print(f训练集样本数: {X_train.shape[0]}, 测试集样本数: {X_test.shape[0]}) # 5. 标准化 scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test) X_train_scaled pd.DataFrame(X_train_scaled, columnsX.columns) X_test_scaled pd.DataFrame(X_test_scaled, columnsX.columns) # 6. 模型定义与训练 models { LinearRegression: LinearRegression(), RandomForest: RandomForestRegressor( n_estimators200, max_depth8, random_state42 ), XGBoost: XGBRegressor( n_estimators300, max_depth4, learning_rate0.05, subsample0.8, colsample_bytree0.8, random_state42, ), } results [] for name, model in models.items(): model.fit(X_train_scaled, y_train) y_pred model.predict(X_test_scaled) mse mean_squared_error(y_test, y_pred) rmse np.sqrt(mse) mae mean_absolute_error(y_test, y_pred) r2 r2_score(y_test, y_pred) cv_scores cross_val_score(model, X_train_scaled, y_train, cv5, scoringr2) results.append({ 模型: name, R2: round(r2, 4), RMSE: round(rmse, 4), MAE: round(mae, 4), 5折交叉验证R2均值: round(cv_scores.mean(), 4), }) print(f\n{name} 训练完成R2{r2:.4f}, RMSE{rmse:.4f}) result_df pd.DataFrame(results) print(\n模型对比结果:) print(result_df.to_string(indexFalse)) # 7. 残差分析以XGBoost为例 best_model models[XGBoost] y_pred_best best_model.predict(X_test_scaled) plt.figure(figsize(8, 6)) plt.scatter(y_test, y_pred_best, alpha0.6) plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], r--) plt.xlabel(真实房价(千美元)) plt.ylabel(预测房价(千美元)) plt.title(XGBoost预测值与真实值对比) plt.tight_layout() plt.savefig(pred_vs_true.png, dpi150, bbox_inchestight) plt.show() # 8. 特征重要性树模型 importance pd.Series( best_model.feature_importances_, indexX.columns ).sort_values(ascendingFalse) print(\nXGBoost特征重要性:) print(importance) plt.figure(figsize(10, 6)) importance.plot(kindbarh) plt.title(XGBoost特征重要性排序) plt.tight_layout() plt.savefig(feature_importance.png, dpi150, bbox_inchestight) plt.show() # 9. 线性回归系数解读 lr models[LinearRegression] coefficients pd.Series(lr.coef_, indexX.columns).sort_values(ascendingFalse) print(\n线性回归系数:) print(coefficients)1.2 代码模块速览模块位置作用数据加载load_data()尝试在线获取数据集失败时回退到本地CSVEDA第3-4步缺失值检查、数据形状、描述性统计、相关性热力图预处理第5步标准化消除特征量纲差异模型训练第6步线性回归、随机森林、XGBoost三模型对比评估第6步后段R2、RMSE、MAE、5折交叉验证可视化第7-8步预测值对比图、特征重要性排序可解释性第9步线性回归系数解释这份代码跑完大概会输出三张图和三组评估指标。在我本机实测线性回归R2大约0.69随机森林R2大约0.83XGBoostR2大约0.88不同随机种子会有些浮动。别急着只看数字后面我会详细讲为什么是这个结果以及哪些环节改了之后结果会明显变化。2. 波士顿房价预测任务拆解与数据集注意事项2.1 这到底是一个什么任务波士顿房价预测本质上是回归任务用13个特征去预测房屋价格中位数目标是连续值。你完全可以把它理解为给房子做一次体检每个特征就像血常规、心电图、血压之类的指标模型需要根据这些指标推断出房子大概值多少钱。因为特征不多、关系相对线性它非常适合用来理解建模全流程特征理解、数据清洗、训练测试划分、模型对比、指标解读。数据集的样本量只有506条放在今天的工业级项目里属于典型的小样本数据集。小样本意味着模型很容易过拟合也意味着深度学习方案的收益不大传统机器学习模型反而能发挥稳定优势。这也是我在代码里同时上线性回归、随机森林和XGBoost的原因三套模型代表三种复杂度层次可以很直观地看到模型复杂度和效果的关系。还有一点需要新人提前知道sklearn.datasets从1.2版本开始不再内置load_boston因此代码里改用fetch_openml在线获取这算是最常见的改动之一。2.2 13个特征字段逐一拆解理解每个字段的含义比直接跑模型更重要。我建议新手拿到数据先做一次字段翻译把无关特征识别出来把强势特征记住字段含义直观理解CRIM城镇人均犯罪率治安水平ZN占地超过25000平方英尺的住宅用地比例居住密度INDUS非零售商业用地比例商业活跃度CHAS是否位于查尔斯河沿岸区位优势0/1变量NOX一氧化氮浓度空气质量RM平均每套住宅房间数户型大小AGE1940年前建成的自用房屋比例房龄结构DIS到五大就业中心的加权距离通勤便利度RAD到径向公路的可达指数交通便捷度TAX每1万美元房产税率持有成本PTRATIO城镇师生比教育配套B城镇人口统计折算指标人口结构指标LSTAT低收入人群占比圈层属性MEDV房价中位数千美元标签在做相关性分析时你会发现两个非常突出的特征LSTAT与MEDV呈强负相关相关系数大约-0.74RM与MEDV呈强正相关相关系数大约0.70。这说明房价本质上受社区人口结构和房屋大小驱动朴素直觉和统计结果是吻合的。CRIM、NOX、TAX这几个特征与房价负相关也很明显反映了治安、环境、持有成本的影响。2.3 初学者最容易踩的三个数据坑第一个坑也是前面提到的load_boston被移除的问题。新版sklearn不再内置这个数据集原因包括部分字段涉及社会人口统计层面的争议以及维护层面的一些考虑。我的建议是不要执着于老教程里的load_boston写法直接用fetch_openml(nameboston, version1, as_frameTrue)或者提前把CSV下载到本地代码里我已经写了本地回退逻辑。第二个坑是量纲问题。CRIM的取值范围从0到90NOX则是0.385到0.871TAX从188到711如果直接丢给线性回归系数会比较难解释惩罚类模型也会被大数值特征带偏。所以线性模型必须做标准化树模型其实无所谓因为决策树的分裂只依赖特征排序线性缩放不会改变排序。我在代码里统一做标准化是为了让三个模型在同一份数据上横向对比避免无谓干扰。第三个坑是样本量太少且存在离群值。比如MEDV等于50的样本其实是截断值早期数据采集时把超过50的全部记成50这部分样本会对损失函数造成明显影响。新手第一次跑完如果发现残差图中有几个点离对角线特别远不要慌先检查一下是不是这些截断值。另外固定random_state非常重要否则每次跑分都不一样你都不知道改进到底来自模型还是来自数据划分。3. 从代码反推设计逻辑为什么这些环节不能省3.1 基线模型从来不是摆设很多人一上来就直接跑XGBoost觉得把复杂模型跑通就是胜利。我的习惯是先上一个最简单的线性回归原因有三个。第一它训练速度快几秒钟就能告诉你数据集大概能被拟合到什么程度。第二系数直接可解释能看到每个特征的贡献方向比如RM系数是正的CRIM系数是负的如果符号和常识相反那大概率是数据预处理出了问题。第三它是后续所有模型的比较基准如果随机森林或XGBoost的表现连线性回归都比不上说明要么特征中全是噪声要么模型参数有问题。波士顿房价数据集用线性回归能达到0.7左右的R2这说明特征与目标之间的线性关系相当强。如果你自己实验时线性回归只有0.5以下我建议先不要急着调参回头检查一下数据清洗和标准化环节。把基线跑稳了再去追复杂模型这比盲目调参要有底气得多。3.2 标准化到底做不做标准答案要分清场景我见到过很多新手在全量数据上先fit再split这是严格禁止的数据泄露行为。StandardScaler必须只用训练集拟合再用训练集的均值方差去转换测试集我在代码里就是fit_transform(X_train)之后用transform(X_test)这一步一定不能省。做标准化的真实收益体现在线性回归上。如果特征量纲不一样线性回归虽然能训练但参数估计不稳定尤其是未来要做Lasso、Ridge这类正则化模型时不标准化就等于给不同特征分配了不同的惩罚权重结果会非常影响我们对系数重要性的判断。随机森林和XGBoost属于树模型它们对单调变换天然不敏感标准化不影响分裂点的相对位置所以理论上不标准化也行。但为了代码简洁和结果可复现统一标准化是性价比最高的做法。3.3 指标选择R2、RMSE、MAE分别在看什么模型对比表里我同时输出了R2、RMSE、MAE三个指标很多人不知道它们侧重点完全不同指标优点缺点波士顿房价场景下的理解R2无量纲直观反映拟合程度极端值影响大0.88意味着模型能解释约88%的房价方差RMSE单位与目标一致大误差被放大对离群值非常敏感大约3.5千美元平均错3500美元MAE不易受离群值影响对小误差不够敏感大约2.5千美元更稳健如果你更关心大额预测误差就盯RMSE如果你担心数据里的50截断值把指标拉偏就多看看MAE。交叉验证的R2均值则是看模型在不同数据划分上的稳定性比单次测试集的R2更可信。我在代码里打印的5折交叉验证R2均值通常会比单次测试集低一点这是正常现象因为交叉验证把数据切成了多份每份都参与过训练和验证相当于对模型做了一次压力测试。3.4 XGBoost参数选择的现场思路代码里我用的参数是n_estimators300、max_depth4、learning_rate0.05、subsample0.8、colsample_bytree0.8。这套参数不是凭空定的而是从两个原则出发第一学习率调低树的数量就要相应增加小学习率配合多树泛化能力通常更好第二数据量只有506条树的深度不能太深否则每棵树都在死记硬背深度4到5是一个安全的起点。如果你想让代码更自动可以在X_train_scaled上做GridSearchCV比如把max_depth设成[3,4,5]n_estimators设成[200,300,500]learning_rate设成[0.05,0.1]from sklearn.model_selection import GridSearchCV param_grid { max_depth: [3, 4, 5], n_estimators: [200, 300, 500], learning_rate: [0.05, 0.1], } gs GridSearchCV( XGBRegressor(random_state42, subsample0.8, colsample_bytree0.8), param_grid, cv5, scoringr2, ) gs.fit(X_train_scaled, y_train) print(gs.best_params_)实测下来网格搜索的结果大概率会落在深度4、学习率0.05、树数量300到500这个区间。调参的正确思路是先粗后细先固定学习率和树数量确定深度范围再反过来微调学习率不要一开始就把所有参数丢进网格里那会让搜索空间爆炸小数据集上纯粹浪费时间。4. 环境配置与常见报错排查4.1 环境依赖清单别小看环境问题很多时候模型没跑通不是代码问题而是库版本冲突。我建议用Python 3.9到3.11之间的版本太老或太新都可能出现兼容问题。依赖库的用途和验证命令如下依赖库用途检查命令scikit-learn模型、预处理、交叉验证python -c import sklearn; print(sklearn.__version__)xgboostXGBoost模型python -c import xgboost; print(xgboost.__version__)pandas数据处理python -c import pandas; print(pandas.__version__)matplotlib绘图python -c import matplotlib; print(matplotlib.__version__)seaborn热力图python -c import seaborn; print(seaborn.__version__)安装建议顺序是先装pandas和matplotlib再装scikit-learn最后装xgboost这样可以避免很多依赖冲突。pip install pandas matplotlib scikit-learn之后单独pip install xgboost大部分情况下就能跑通。4.2 找不到msvcp140.dll怎么解决Windows用户跑xgboost时经常遇到由于找不到msvcp140.dll无法继续执行代码这个问题我也踩过。原因很简单xgboost作为编译型扩展包依赖微软Visual C运行库提供的dll文件而系统里刚好缺了这些库。解决方案是去微软官网下载并安装Microsoft Visual C Redistributable 2015-2022注意选择x64版本装完之后重启终端再验证。装完运行库后可以顺手检查一下xgboost是否能正常导入如果导入成功之前那些奇奇怪怪的报错基本会一起消失。这种事不需要觉得丢人Windows环境下的机器学习项目十个人里有八个会遇到运行库问题。最简单的方法是在安装xgboost之前就把运行库装好后面可以少一肚子气。4.3 模型训练阶段典型报错速查表报错信息原因解决方案ImportError: cannot import name load_boston from sklearn.datasetsscikit-learn新版移除了旧数据集使用fetch_openml或本地CSVSSLError或联网失败fetch_openml需要联网网络环境受限下载CSV到本地走pd.read_csv分支ValueError: could not convert string to float某些特征如CHAS是字符串或category类型先df[CHAS] df[CHAS].astype(float)XGBoostError: Invalid Parameter format for max_depth参数名或参数类型写错了检查参数名max_depth接收整型KeyError: MEDVCSV列名与代码里不一致检查数据文件首行列名手工改名中文标签乱码matplotlib默认字体不支持中文加plt.rcParams[font.sans-serif] [SimHei]4.4 训练时间和硬件要求这个数据集很小纯CPU训练完全没有压力。在我的笔记本上线性回归几乎秒出随机森林训练大约1到2秒XGBoost大约5秒左右。用GPU反而有点浪费因为数据量根本不足以体现GPU优势。如果你在网上的云端环境跑注意一下环境里的Python版本以及是不是已经预装xgboost这两点最容易卡人。5. 进一步优化13个特征里还能挖出什么5.1 特征工程方向别只盯着模型模型对比做完进一步优化的重点应该转向特征工程和交叉验证而不是把n_estimators调到2000。波士顿房价数据里特征间的关系并非完全独立我们可以构造交特征来提升性能。比较有效的做法是把LSTAT和RM做交互比如新增RM * LSTAT或RM / LSTAT因为这两种特征对房价的贡献方向相反组合之后往往能捕获更细腻的规律。另外TAX和RAD、NOX和INDUS的相关性本身就高可以考虑合并或做PCA降维。实际操作时建议先看特征重要性排序我跑XGBoost时LSTAT和RM稳定排在前两名后面才是DIS、NOX和CRIM。手动构造交互特征之后再做一次交叉验证对比如果R2提升不明显说明数据噪声已经盖过了这些组合特征的信息量那就适可而止不要继续在特征工程上无限投入。5.2 交叉验证与网格搜索的实际意义单一测试集上R2高并不代表模型稳定。我在代码里用5折交叉验证把训练数据切成5份轮流做验证相当于评估了5个模型的表现最后取均值。波士顿房价数据集样本量小交叉验证的稳定性优势尤其明显。线性回归的交叉验证R2均值一般0.70左右随机森林大概0.82XGBoost大概0.87。网格搜索的意义在于避免人工盲猜。比如我习惯先固定n_estimators300只搜max_depth确定深度之后再搜learning_rate。这样搜索空间从指数级降到了线性时间开销很少结果也更可控。我在调参时另外加了一个小习惯每次只改一个变量不多个变量同时动。否则即使R2涨了你也不知道是谁的功劳。5.3 为什么LSTM这类模型在这里不合适代码分享出来之后有朋友私下问我能不能用LSTM跑这个项目。我的回答是不建议真没必要。LSTM擅长处理序列数据比如时间序列、文本序列而波士顿房价预测是典型的表格回归任务每行样本是一户房子的静态特征不存在明显的先后顺序依赖。强行把特征转成序列喂给LSTM等于让一个阅读长篇小说的人去判断一张体检单思路完全错位。而且LSTM对数据量的要求比树模型高得多506个样本对于深度模型来说连塞牙缝都不够容易产生严重过拟合。类似的还有bilstm、mobilenetv2这类网络它们有自己的主场但在波士顿房价这个场景下没有任何优势。选模型不是越花哨越好而是先想清楚数据形态再决定用什么工具表格数据在中小样本场景下树模型的实用性和稳定性很难被替代。6. 写在最后一点实操体会我个人在实际操作中的体会是波士顿房价预测这个项目技术难度不算高但它能把一堆机器学习经验串联起来数据公开后要先做字段语义验证预处理阶段要防止数据泄露模型选型要考虑数据形态调参要由粗到细而不是蛮力搜索。它真正训练的是你面对一个陌生数据集时形成完整判断流程的能力。如果你能在这个项目上把自己训练到对每一个环节都能回答出为什么那以后遇到更复杂的数据集至少心里不会慌。最后再分享一个小技巧跑模型时把random_state统一固定成同一个值对比实验才有意义我习惯固定42一旦发现结果异常先检查数据泄露和特征符号再怀疑模型本身。这套习惯比多会几个花哨模型重要得多。
返回列表