ARTICLE DETAIL

资讯详情

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

波士顿房价数据集回归实战:从CSV到随机森林全流程

波士顿房价数据集回归实战:从CSV到随机森林全流程 简介波士顿房价数据集是机器学习与数据分析领域的经典入门数据常被用于回归预测、特征工程与模型评估练习。资源适合正在学习Python机器学习、希望用真实数据完成房价预测实践的学习者也适用于教学演示与快速原型验证。压缩包共3个文件分别为csv格式的房屋属性数据、py格式的模型训练脚本以及md格式的使用说明整体大小仅15KB结构精简便于下载后即刻查看。其中数据文件包含犯罪率、房屋面积、房龄等关键特征脚本覆盖数据加载、切分与常用回归模型训练过程说明文档则梳理了字段含义与运行方式。已有1361人学习下载对初学者而言该资源能有效降低上手门槛免去自行整理数据的麻烦可直接依托脚本理解波士顿房价预测的完整流程并为后续改进模型、尝试不同算法提供基础。1. 波士顿房价数据集最容易被低估的回归入门数据集很多人在入坑机器学习时第一份拿来练手的就是波士顿房价数据集。它不大506 条样本13 个特征一个连续型目标变量 MEDV恰好覆盖了回归任务里最常见的一整套动作数据清洗、特征选择、标准化、模型训练、结果评估。我见过不少人在 Kaggle 上跑完几个竞赛项目后反而回头用这份数据补基础因为它的特征之间有明显相关性、有缺失值、还有少数异常样本这些恰恰是真实业务数据里最常见的三种情况。这份 zip 里装的就是这个经典数据集的 CSV 文件包适合刚接触回归的新手照着走一遍完整流程也适合带学生的讲师拿来做课堂样例。接下来我会从文件结构、字段含义、加载方式、训练流程和常见坑五个层面把它拆开让你下载后能直接复现一整套回归建模流程。2. 拆开 zip 先看数据文件结构、字段字典与数值单位2.1 压缩包里装了什么文件下载后解压里面通常是一个 CSV 文件加一份简短说明文档。CSV 是这类数据集最通用的分发格式既能用 Excel 打开也能用 pandas、R 或者 SQL 直接导入。有的打包版本会把描述文件单独放一个README.txt里面写明了字段来源和数据说明建议解压后先把这份文档打开看一遍。我在处理这类打包数据时习惯先建立一个工作目录把数据和脚本分开mkdir boston-housing cd boston-housing unzip -o 波士顿房价数据集.zip ls -la-o参数表示覆盖同名文件防止重复解压时提示交互。执行完ls -la后你会看到.csv文件和说明文档文件名通常会带年份或版本标记。这一步骤本身没有技术难度但能帮你确认解压是否完整——如果 CSV 文件只有几 KB说明可能解压不完整需要重新下载。2.2 十四个字段逐个过含义、边界值与用途数据集包含 13 个特征和 1 个目标变量所有字段全部是数值型没有字符串类型。下面是完整的字段字典单位标注清楚训练前最好对照这个表格检查一遍数据范围。字段名含义单位边界情况CRIM城镇人均犯罪率每千人犯罪数最大值接近 89严重右偏ZN占地超过 25,000 平方英尺的住宅用地比例比例大量样本为 0INDUS城镇非零售商业用地比例比例与 NOX 高度相关CHAS是否临查尔斯河0/1只有 35 条为 1类别极度不平衡NOX一氧化氮浓度每千万分之一范围 0.385 到 0.871RM平均房间数间最小 3.561最大 8.78AGE自住单位中建于 1940 年前的比例比例大量分布在 50 到 100DIS到五大就业中心的加权距离英里与 NOX 呈负相关RAD径向公路可达性指数指数取值离散1 到 24TAX房产税率每 1 万美元与 RAD 存在强相关PTRATIO城镇师生比比值范围 12.6 到 22B城镇黑人比例相关的综合指标指数按公式 1000 * (Bk - 0.63)^2 计算LSTAT低收入人群比例百分比与 MEDV 负相关最强MEDV自住房屋价格中位数千美元最大 50疑似截断这里最容易被新手忽略的是 MEDV 的上限问题。数据集源自 1978 年当时统计时对超过 50,000 美元的房价做了统一处理全部按 50 记录所以目标变量在 50 处有一个明显的平台。这不是数据损坏而是采集时的截断行为。如果你做后续的回归分析要注意模型可能在这个位置出现系统性的低估。2.3 数据口径与版本差异波士顿房价数据集最早由 Harrison 和 Rubinfeld 在 1978 年整理后来被卡内基梅隆大学维护的 UCI 机器学习仓库收录再后来被 sklearn 直接内置。这份 zip 里的 CSV 和 sklearn 内置版本仅有一个字段名的差异sklearn 内置版把目标变量命名成target而原始 CSV 版本里叫MEDV。其他字段名完全一致都是小写。这带来一个实际问题如果你在网上搜到的大部分教程都是基于 sklearn 内置版本写的直接套用到 CSV 版本上会出现列名报错。解决办法很简单读取之后把列名改一下就行。import pandas as pd df pd.read_csv(housing.csv) df.rename(columns{MEDV: target}, inplaceTrue)我一般不建议只依赖 sklearn 的load_boston()接口因为它的数据经过了标准化预处理新手容易误以为原始数据就是这样干净整洁的。用 CSV 版本才能看到真实的脏数据形态这对理解什么叫做缺失值处理更有帮助。3. 用 pandas 把 CSV 读进 DataFrame最小可用代码与关键参数3.1 读文件前先搞清楚的三个参数CSV 读取看起来简单但有几个参数直接决定你能不能拿到一份干净的数据。第一个是表头header这份数据的 CSV 第一行就是字段名所以header0即可不需要额外设置第二个是缺失值标记na_values早期版本的数据里空缺位置可能填的是字符串NA或空字符串读取时要告诉 pandas 这两种情况都算缺失第三个是数据类型dtype保险起见用float64统一读入CHAS 列虽然是 0/1 整数但存成浮点数对建模没有影响。import pandas as pd import numpy as np df pd.read_csv( housing.csv, header0, na_values[NA, , N/A], dtypenp.float64 ) print(f原始样本数: {len(df)}) print(f缺失值统计:\n{df.isnull().sum()})这段逻辑里最关键的是na_values。如果你不指定它NA会被当成普通字符串读成一个 object 列后面做df.dtypes检查时会发现 14 列里有 3 列是object类型模型直接报错。指定为np.float64之后所有特征列统一为浮点数后续才能直接喂给 sklearn 的接口。3.2 缺失值处理MEDV 的缺失与填充策略原始数据集本身缺失值不多但如果用的是网络流传的修正版本可能包含人工删除或添加过的样本导致某些特征出现少量缺失。处理缺失值时我一般先看缺失比例。少于 1% 就删除该行超过 5% 就得认真考虑用均值或中位数填充。# 删除缺失值占比过高的行 before_count len(df) df df.replace([np.inf, -np.inf], np.nan).dropna() after_count len(df) print(f删除前后样本数变化: {before_count} - {after_count})注意代码里的replace([np.inf, -np.inf], np.nan)这行它把正负无穷也统一转成缺失值。CSV 文件里偶尔会出现INF字符串pandas 默认不会把它解析成NaN而是读成一个字符串对象这行代码就是为了堵住这个漏洞。如果缺失值发生在 MEDV 上我倾向于直接删掉对应行而不是填充因为目标变量的填充会引入错误的监督信号哪怕只填一条也可能让模型学到一个根本不存在的标签分布。3.3 划分训练集与测试集随机种子决定能不能复现数据划分是回归任务里最容易出问题也是最少被重视的一步。很多人在同一个数据集上反复调参测试集成绩越调越好但模型换到新数据上效果惨淡就是因为测试集泄漏了信息。正确的做法是划分一次之后固定住后续所有操作都在训练集内完成。from sklearn.model_selection import train_test_split feature_cols [ CRIM, ZN, INDUS, CHAS, NOX, RM, AGE, DIS, RAD, TAX, PTRATIO, B, LSTAT ] X df[feature_cols] y df[MEDV] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) print(f训练集样本数: {len(X_train)}, 测试集样本数: {len(X_test)})random_state42是让每次运行得到同一个切分结果的关键参数。如果你不设置系统会取当前时间作为随机种子跑两次代码得到的训练集不一样模型结果自然也不一样后续任何调参对比都失去参考价值。至于这个值本身42 只是个习惯约定换成其他任意整数都可以只要固定住就行。4. 特征工程与训练从线性回归到随机森林的完整流程4.1 特征尺度差异与标准化波士顿房价数据集的 13 个特征尺度差异极大。CRIM 的均值是 3.6方差接近 74而 CHAS 只有 0 和 1 两个取值。如果你直接拿原始数据训练线性模型模型会把更多权重放在数值大的特征上这并不代表这些特征对房价的贡献更大纯粹是数值尺度的偏差。svm、线性回归和 knn 这类基于距离的模型对尺度敏感必须做标准化。树模型不受尺度影响可以跳过这一步。常见做法是只对训练集做标准化测试集用训练集的统计结果做变换防止信息从测试集流向训练过程。from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test)这里fit_transform和transform的用法是很多教程会少讲一笔的地方。fit_transform先计算训练集的均值和方差再用它完成标准化测试集上只能用transform不能重新计算统计量。如果对测试集单独fit训练过程和测试过程使用的特征分布基准不一致在线性模型上会导致预测结果偏移。4.2 训练线性回归模型与评估指标标准化之后就可以上模型了。线性回归是第一个必须跑的模型它不是用来拿高分的而是用来做基线的。如果线性回归的 R² 就达到了 0.7说明特征和目标的关系接近线性用复杂模型提升的空间有限如果线性回归只有 0.4 而随机森林能到 0.8说明特征之间明显存在非线性交互这时候才值得上更复杂的模型。from sklearn.linear_model import LinearRegression from sklearn.metrics import r2_score, mean_squared_error lr_model LinearRegression() lr_model.fit(X_train_scaled, y_train) y_pred_lr lr_model.predict(X_test_scaled) mse mean_squared_error(y_test, y_pred_lr) rmse mse ** 0.5 r2 r2_score(y_test, y_pred_lr) print(f线性回归 RMSE: {rmse:.3f}) print(f线性回归 R²: {r2:.4f})RMSE 的单位和 MEDV 相同都是千美元所以 3.5 意味着模型预测的平均误差在 3500 美元左右。只看 RMSE 不够还要结合 R² 看解释度。如果 RMSE 低但 R² 也低可能只是数据本身方差小模型并没有真正抓住特征和目标的规律。4.3 树模型的优势与交叉验证随机森林在中小型表格数据上几乎总是能超越线性回归因为它的特征交互不需要人为构造。不用先做特征交叉直接把 13 个特征丢进去就行树分裂过程会自动搜索交互关系。from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import cross_val_score rf_model RandomForestRegressor( n_estimators200, max_depth8, min_samples_leaf4, random_state42 ) scores cross_val_score( rf_model, X_train_scaled, y_train, cv5, scoringr2 ) print(f交叉验证 R²: {scores.mean():.4f} (± {scores.std():.4f})) rf_model.fit(X_train_scaled, y_train) y_pred_rf rf_model.predict(X_test_scaled) print(f随机森林测试集 R²: {r2_score(y_test, y_pred_rf):.4f})n_estimators200是树的棵数200 棵对 404 条训练样本来说足够再大收益很小max_depth8限制树的最大深度防止单棵树完全记住训练样本min_samples_leaf4要求每个叶子节点至少 4 条样本进一步抑制过拟合。交叉验证的cv5代表 5 折全部 404 条训练数据被切成 5 份每份轮流做验证集。R² 达到 0.85 以上属于正常水平这份数据本身噪声不大再往上压需要做特征筛选或尝试梯度提升类模型但对练手来说到这里已经完成闭环了。5. 排查与避坑从加载数据到复现结果之间的 6 个高频问题5.1 不同来源的版本混用导致特征列错位现象代码是从 sklearn 内置示例改装来的直接把它作用于 CSV 版本读取时报错或者模型训练结果明显偏离教程数值。原因sklearn 内置版目标变量名为target而 CSV 版是MEDV两个版本在内部存储上也有细微差别。解决统一以 CSV 文件字段为准用df.columns打印字段列表逐一核对后再决定是否重命名。5.2 缺失值被当成了数值 0现象模型能跑通但系数里 CHAS 的权重异常高交叉验证结果忽高忽低。原因READCSV 时na_values未配置NA被强制转成了 NaN但某些填充逻辑里用了fillna(0)把 0 当作了一个有效取值输入模型。解决缺失值的填充策略要分列处理CHAS 列的缺失应填众数或直接删行不能统一填 0。5.3 标准化时把训练集和测试集一起 fit现象训练集评估指标很好测试集指标差了一截而且每次跑结果漂移。原因对 X 全量数据做了一次fit_transform再切分训练和测试测试集的统计信息提前进入了归一化参数等价于轻微的数据泄漏。解决先切分再在训练集上fit_transform在测试集上只transform。5.4 目标变量被选成了特征现象feature_cols列表里多写了MEDV训练时特征矩阵和目标向量包含同一个字段模型 R² 接近 1看起来完美但完全不可用。原因列名筛选时没做排除直接把全部列丢进特征矩阵。解决用feature_cols [col for col in df.columns if col ! MEDV]过滤或者打印df.columns.tolist()人工核对列表。5.5 RMSE 计算时单位混乱现象算出来的 RMSE 是几千甚至几万但教程里都是个位数怀疑是数据或模型出了问题。原因MEDV 原始单位是千美元如果你的代码在某个环节乘了 1000比如试图把单位转成美元RMSE 会同步放大。解决建模全流程保持原始单位仅在结果展示时用文字说明等价美元。RMSE 的对比必须在同一单位下进行。5.6 随机划分导致相同代码得到不同结果现象同一个模型同一个参数两次运行 R² 分别是 0.82 和 0.86差距明显且无法解释。原因train_test_split没有设置random_state两次划分的测试集组成不同。解决所有涉及随机过程的接口包括模型本身的random_state都设置成同一个整数。这份数据总共只有 506 条样本测试集只有 100 条左右一次划分的差异足以造成 0.04 以上的 R² 波动。6. 最后一步把完整流程合并成一键脚本6.1 全流程脚本前面的步骤拆开讲是方便理解实际使用时我需要一个能反复执行的脚本输入 CSV输出模型评估报告。下面给出一份可直接运行的完整流程包含数据加载、清洗、切分、标准化、三模型对比和结果汇总。import pandas as pd import numpy as np 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 r2_score, mean_squared_error # 1. 数据加载 df pd.read_csv(housing.csv, na_values[NA, , N/A]) df df.replace([np.inf, -np.inf], np.nan).dropna() # 2. 特征与目标分离 feature_cols [ CRIM, ZN, INDUS, CHAS, NOX, RM, AGE, DIS, RAD, TAX, PTRATIO, B, LSTAT ] X df[feature_cols] y df[MEDV] # 3. 数据划分 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) # 4. 标准化 scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test) # 5. 模型训练与评估 models { LinearRegression: LinearRegression(), RandomForest: RandomForestRegressor( n_estimators200, max_depth8, min_samples_leaf4, random_state42 ), } for name, model in models.items(): cv_r2 cross_val_score( model, X_train_scaled, y_train, cv5, scoringr2 ).mean() model.fit(X_train_scaled, y_train) y_pred model.predict(X_test_scaled) test_r2 r2_score(y_test, y_pred) rmse mean_squared_error(y_test, y_pred) ** 0.5 print(f{name:16s} | CV R²{cv_r2:.4f} | Test R²{test_r2:.4f} | RMSE{rmse:.3f})这段脚本覆盖了前面提到的所有关键点缺失值统一处理、切分固定随机种子、标准化区分训练和测试、交叉验证与测试集双重评估。输出里同时给出交叉验证和测试集两个指标交叉验证反映模型稳定性测试集反映最终泛化能力两个数值差距越小越好。如果相差超过 0.1基本可以判断训练集和测试集的分布不够一致需要检查切分代码。6.2 自检清单脚本跑完之后我习惯用四个条件核对结果是否符合预期。检查项预期值偏差说明样本数506 行 x 14 列小于 500 行说明有丢数据特征数13 个少于 13 个说明列名有误线性回归 R²0.65 - 0.75低于 0.6 检查标准化是否漏做随机森林 R²0.80 - 0.88高于 0.95 怀疑数据泄漏这份数据我拆过很多次每次跑到随机森林这一步都能稳定得到 0.82 以上的 R²线性回归稳定在 0.7 左右。如果你跑出来的数值偏差很大优先回查随机种子和缺失值处理这两个环节九成问题出在这两处。从那以后我每次拿到一份新的数据集都会强制走一遍「加载 — 检查缺失 — 固定随机种子 — 训练基线 — 交叉验证」这套流程再去碰参数调优才没再被那些莫名其妙的指标波动带偏过。希望这份笔记对你有帮助。本文还有配套的精品资源点击获取
返回列表