首页 / 资讯中心 / 文章详情

机器学习房价预测系统源码实战:数据处理与模型调参全解析

机器学习房价预测系统源码实战:数据处理与模型调参全解析 ★ FEATURED ARTICLE
简介这是一套基于机器学习完成房价预测的开源项目源码适合具备一定Python基础的计算机专业学生、数据分析初学者以及房地产评估或金融风控方向的技术人员学习参考。项目围绕数据预处理、特征工程、模型选择与房价预测四个环节展开包含对异常值处理、缺失值填充、分类特征编码和构造新特征等典型操作并提供了岭回归、Lasso回归和随机森林回归的横向对比与调参思路便于读者快速掌握完整的回归建模流程。资源为zip压缩包共计4个文件包括两个csv数据文件、一个py脚本和一个md说明文档压缩包整体约176KB结构紧凑便于本地运行调试。目前已有148人学习下载。拿到资源后可直接运行房屋价格预测脚本结合数据文件与说明文档理解模型调用与输出结果适合用作课程设计、入门实战或相关业务场景的参考模板。1. 机器学习房价预测系统源码四件套到底能解决什么问题下载过几个课程设计源码的人多半遇到过这种尴尬压缩包解压出来三四个文件README 写了两行一跑不是缺库就是报错。这套基于机器学习的房价预测系统在同类资源里算结构完整的——train.csv、test.csv、house_price_predict.py、README.md 四件套齐全把“数据预处理 → 特征工程 → 模型选型 → 预测输出”这条完整链路都覆盖了。它适合两类人一是要交课程设计或期末作业的计算机相关专业学生二是想快速上手 sklearn 回归建模的从业者。我按自己的复现习惯把每一步的参数设置和踩坑点拆开讲读完你就能拿着它改出自己的版本。2. 先把数据捋顺预处理与特征工程的可复现细节2.1 拿到 train.csv 先干什么缺失值、异常值与数据类型检查第一次跑这种源码包我建议你别急着双击运行。先用 pandas 把 train.csv 读进来看 shape、dtypes、describe 结果心里有数再往下走。常见做法是先写一段检查脚本把数据的基本面摸清楚而不是直接跳到模型。import pandas as pd df pd.read_csv(train.csv) print(df.shape) print(df.dtypes) print(df.describe().T) print(df.isnull().sum())这段代码做的事情很直接shape 告诉你样本量和列数dtypes 帮你找出应该转成数值却没转的列describe 的 min、max 和分位数能直接暴露异常值isnull().sum() 则是一张缺失值地图。我一般会重点看 max 和 75% 分位数的差距如果“面积”列的 75% 分位数是 120 平方米max 却到 900十有八九是录入错误或者包含了非住宅物业。这种样本不处理岭回归和随机森林都会被拉偏。数据处理逻辑上缺失值少就直接删除行多就分情况填充分类特征如地段、朝向用标签编码或独热编码异常值常用 IQR 或分位数截断。这套源码包里的 house_price_predict.py 走的也是这个路子只是把检查步骤压缩进了主脚本。你复现的时候我建议把检查环节单独存成一个脚本改阈值的时候不用去翻主流程代码。2.2 缺失值和异常值处理不是所有缺失都该同一种填法缺失值的处理策略取决于缺失列的类型和缺失比例。我一般按这样的优先级处理数值列缺失率低于 5%直接删除行关键数值列缺失率较高用中位数填充因为房价这类数据普遍右偏均值会被高价房拉高分类列缺失用众数填充并在后面编码时单独保留一个“未知”类别。至于异常值最常用也最容易被课程设计忽略的是 IQR 截断法。import pandas as pd import numpy as np df pd.read_csv(train.csv) # 只对数值列做异常值截断分类列不参与 numeric_cols df.select_dtypes(include[np.number]).columns for col in numeric_cols: q1 df[col].quantile(0.25) q3 df[col].quantile(0.75) iqr q3 - q1 lower q1 - 1.5 * iqr upper q3 1.5 * iqr df[col] df[col].clip(lower, upper) # 缺失值按列缺失率决定删行还是填充 for col in df.columns: missing_rate df[col].isnull().mean() if missing_rate 0.05: df df.dropna(subset[col]) elif df[col].dtype object: df[col] df[col].fillna(df[col].mode()[0]) else: df[col] df[col].fillna(df[col].median())这里的 clip 方法用的是 1.5 倍 IQR 的上下限把超出范围的值直接钳制到边界而不是删除整行。这个取舍很关键房价数据里“面积大”可能是高端住宅直接把行删掉会损失样本钳制到上限则保留了它的部分信息代价是略微压低极值。缺失值填充时数值列用 median 而不是 mean也是防止极端值把填充结果带偏。在源码包里这一步大概率只是简单 dropna 或 fillna我建议你改成上面的版本。它不会让你在课程答辩时多一个亮点但能明显减少后面模型验证集 R² 的波动幅度尤其是使用岭回归时。2.3 分类特征编码LabelEncoder 与 OneHotEncoder 的适用边界分类特征编码是课程设计里最容易翻车的一步。像“地段等级”这种有序程度明显的特征用 LabelEncoder 简单省事但像“朝向”这种无序分类直接标成 0/1/2 会让模型误以为数字大就代表更优这时 OneHotEncoder 更合适。from sklearn.preprocessing import OneHotEncoder, LabelEncoder import pandas as pd # 对有序分类特征用标签编码 df[street_type] LabelEncoder().fit_transform(df[street_type]) # 对无序分类特征用独热编码保留列名便于排查 encoder OneHotEncoder(sparse_outputFalse, dropfirst, handle_unknownignore) encoded encoder.fit_transform(df[[direction]]) encoded_df pd.DataFrame(encoded, columnsencoder.get_feature_names_out([direction])) df pd.concat([df.drop(columns[direction]), encoded_df], axis1)注意 dropfirst 是为了避免哑变量陷阱也就是多重共线性这对岭回归和 Lasso 这类线性模型尤其重要。get_feature_names_out 可以拿到编码后的列名后面做特征重要性分析时不用面对一堆没名字的 0/1 列。handle_unknownignore 是我额外加的它保证 test.csv 里如果出现训练集没见过的分类取值transform 时不会直接抛异常。这里要单独提醒LabelEncoder 只适合有序分类不适合无序分类。如果你把“朝向”用 LabelEncoder 编成 0/1/2/3随机森林还好岭回归和 Lasso 会真的把数字大小当作朝向优劣来建模预测结果会出现系统性偏差。源码包里如果只用了 LabelEncoder建议你按上面的方式改成 OneHotEncoder这是复现时最值得动的一刀。2.4 特征工程从原始字段里“挖”出更稳的入模特征特征工程这一步源码里通常会做一点但备课的人很少把理由写全。以房价预测最常见的字段来说建筑面积、建造年份、卧室数、所在楼层都是常见输入直接喂模型也不是不行但效率不高。常见做法是构造三类衍生特征房龄、单价、密度。import pandas as pd import numpy as np df[house_age] 2024 - df[built_year] df[unit_price] df[price] / df[area] df[room_density] df[bedrooms] / (df[area] / 100)house_age 比 built_year 更直观模型不用自己去学“年份越大越新”这个隐含关系unit_price 是房价分析里的核心指标后续做异常值筛选时单价异常比总价异常更能说明问题room_density 是每百平米的卧室数能反映户型紧凑度对中低价位房型有明显区分度。加入这些特征后模型的输入维度通常能扩展三到四个随机森林对这种组合特征尤其敏感。不过别忘了test.csv 里也要做一模一样的处理否则模型训练和预测的特征空间对不上轻则警告重则直接报维度错误。源码包的 README 里如果没强调这一点说明这是留给你补的坑我建议把特征工程写成独立函数train 和 test 各调一次。3. 三个回归模型怎么选岭回归、Lasso 与随机森林的调参与对比3.1 为什么是这三种模型线性带正则与树模型互为补充项目里的模型选型是岭回归、Lasso 和随机森林回归这个组合是有讲究的。岭回归用 L2 正则处理特征共线性适合特征间相关性高的房价数据Lasso 用 L1 正则自带特征选择能力能把不重要特征的系数压到零随机森林回归则是非线性模型能捕捉交互效应比如“地段好但面积小”这种组合逻辑。三者放一起对比本质是看这份数据到底是线性主导还是非线性主导。我在复现时会先用默认参数各跑一遍记录 R² 和 RMSE再考虑调参。因为默认参数往往已经很能说明问题如果 Lasso 的 R² 明显高于另外两个说明特征冗余度高如果随机森林大幅领先说明特征之间有明显非线性关系。这个对比结论可以直接写进课程设计的实验报告里比单跑一个模型有说服力得多。3.2 调参主线GridSearchCV 的候选值与评估指标调参不建议一上来就全参数网格搜索样本量不大时会很慢而且容易过拟合验证集。常见做法是先定关键参数再小步幅收缩。对于岭回归和 Lasso关键参数是 alpha它控制正则化强度对随机森林关键是 n_estimators、max_depth、min_samples_split这三个参数分别控制树的规模、深度和分裂门槛。from sklearn.linear_model import Ridge from sklearn.model_selection import GridSearchCV, train_test_split from sklearn.metrics import r2_score import pandas as pd X df.drop(columns[price]) y df[price] X_train, X_val, y_train, y_val train_test_split(X, y, test_size0.2, random_state42) ridge_param {alpha: [0.01, 0.1, 1, 10, 100]} ridge GridSearchCV(Ridge(), ridge_param, cv5, scoringr2) ridge.fit(X_train, y_train) print(ridge.best_params_, ridge.best_score_)这段代码的核心逻辑是用 5 折交叉验证配合 R² 作为评分比只看单一验证集更稳。alpha 从 0.01 到 100 跨越四个数量级是为了覆盖正则化强度从几乎为零到很强的区间。random_state42 保证每次切分可复现这对课程设计里实验结果的稳定性很重要答辩时老师很可能会问“为什么两次运行结果不一样”这个参数就是答案。Lasso 的调参基本一样只是要注意 Lasso 在 alpha 很大时会把所有系数压成零这时 R² 会变成一个难看的负数属正常现象不用慌张。随机森林的调参通常这样写from sklearn.ensemble import RandomForestRegressor rf_param { n_estimators: [100, 200, 300], max_depth: [None, 10, 15], min_samples_split: [2, 5, 10] } rf GridSearchCV( RandomForestRegressor(random_state42), rf_param, cv3, scoringr2, n_jobs-1 ) rf.fit(X_train, y_train) print(rf.best_params_, rf.best_score_)这里把 n_jobs 设为 -1让所有 CPU 核心一起跑否则 300 棵树乘以 27 组参数组合单核跑起来非常煎熬。max_depth 的候选值里包含了 None也就是不限制深度这是随机森林的默认行为对小数据集通常没问题但如果你发现训练集 R² 接近 1 而验证集明显下跌说明过拟合了下一轮就把 None 去掉。3.3 特征重要性排序与结果对比不只是看 R² 高低调完参之后最后一步是统一评估。我习惯把三个模型在验证集上的结果放进一个表格里对比这个表可以直接贴进实验报告模型R²RMSE最佳参数岭回归0.xxxxxxxalpha1.0Lasso0.xxxxxxxalpha0.1随机森林0.xxxxxxxmax_depth10, n_estimators200判断标准不只看 R²还要看 RMSE 的量级。如果房价均值是几十万RMSE 也是几十万说明预测基本是瞎猜RMSE 落在均值的 10% 到 20% 之间算合格。另一个值得关注的点是随机森林如果 R² 远高于岭回归但在测试集上表现明显下滑说明过拟合这时需要把 max_depth 调小或增大 min_samples_split。对于随机森林还可以顺手输出特征重要性这一步在答辩时很加分import pandas as pd importance pd.Series( rf.best_estimator_.feature_importances_, indexX_train.columns ).sort_values(ascendingFalse) print(importance.head(10))feature_importances_ 是随机森林自带的结果数值越大代表该特征对预测的贡献越高。输出后你会发现unit_price 和 house_age 这类衍生特征通常排在前列这就是特征工程价值的直接证据。如果 Lasso 的系数稀疏性也比较明显可以用类似方式列出保留的特征和随机森林的重要性排序互相印证。4. 房价预测实战从 train.csv 到 test.csv 的完整跑通流程4.1 运行前的环境准备与目录结构检查源码包里的四件套是 house_price_predict.py、train.csv、test.csv、README.md。第一步不是直接运行而是确认环境。确认你的计算机已安装 Python 3.7 以上版本然后建一个干净的虚拟环境再安装依赖。python -m venv house_price_env source house_price_env/bin/activate # Windows 下执行 activate.bat pip install pandas scikit-learn numpy matplotlib注意scikit-learn 的版本会影响 OneHotEncoder 的参数写法新版本用 sparse_output老版本用 sparse。如果你在旧版 sklearn 上运行源码遇到 TypeError优先检查是不是版本问题。README.md 里一般写了依赖但写得不够细的话以本机实际报错为准缺什么就补什么。4.2 主脚本执行逻辑逐段拆解house_price_predict.py 的典型执行流程是读 train.csv → 数据清洗 → 特征工程 → 切分训练集和验证集 → 训练模型 → 读 test.csv → 做同样的预处理 → 用最优模型预测 → 输出预测结果。我们可以把主流程拆成下面这样import pandas as pd from sklearn.model_selection import train_test_split from sklearn.linear_model import Ridge from sklearn.metrics import r2_score train pd.read_csv(train.csv) test pd.read_csv(test.csv) def preprocess(data): data data.copy() data[house_age] 2024 - data[built_year] data[unit_price] data[price] / data[area] if price in data else None data data.dropna(subset[price] if price in data.columns else None) return data train preprocess(train) test preprocess(test) X train.drop(columns[price]) y train[price] X_train, X_val, y_train, y_val train_test_split(X, y, test_size0.2, random_state42) model Ridge(alpha1.0) model.fit(X_train, y_train) val_pred model.predict(X_val) print(R2:, r2_score(y_val, val_pred)) test_pred model.predict(test) test[pred_price] test_pred test.to_csv(prediction_result.csv, indexFalse)这段代码的逻辑是先把预处理封装成函数保证训练集和测试集走完全相同的流程避免目标泄漏和维度不一致。然后切分数据、训练岭回归、评估验证集 R²、最后对 test.csv 预测并落盘。prediction_result.csv 就是你交给老师或者给业务方的最直接产物。需要注意的参数dropna 只在训练集上有意义测试集本来就不该有 price 列。但如果你的 test.csv 里也带了 price 列说明这是一份带标签的测试集可以直接把预测值和真实值合并做最终评估课程设计里这种“半监督式”的数据划分很常见记得在报告里说明清楚。4.3 运行脚本命令行输出、结果文件与手动验证环境装好、脚本确认无误后直接运行即可python house_price_predict.py如果脚本本身没有输出文件路径的功能运行后可以手动查看控制台输出。若脚本封装得好通常会在命令行里打出每个模型的 R²、RMSE 以及预测结果的文件名。看到以下输出格式时说明流程已经完整跑通Ridge R2: 0.71, RMSE: 32000 Lasso R2: 0.68, RMSE: 35000 RandomForest R2: 0.83, RMSE: 25000 Prediction saved to prediction_result.csv如果控制台显示维度错误优先检查 train 和 test 的特征列是否完全一致。一个很常见的翻车场景是test.csv 里少了一列 OneHotEncoder 生成的哑变量导致 predict 时的特征数和模型训练时不匹配。这时候回到 2.3 节的统一编码函数确保处理流程一致基本都能解开。5. 避坑与常见问题运行源码时的五个典型翻车点5.1 现象sklearn 报 TypeErrorOneHotEncoder 参数不认原因新版 sklearn1.2把 OneHotEncoder 的 sparse 参数改成了 sparse_output源码包里老写法 sparseFalse 直接报错。解决先把 scikit-learn 升到 1.2 以上再把 sparse_outputFalse 写进去。如果不想动环境也可以把 sklearn 固定到和 README 匹配的版本但更推荐前者因为新版本修复了旧版里不少数值稳定性问题。改完之后重新跑一遍训练确认编码后的列数和改之前一致。5.2 现象train 和 test 特征数量不一致predict 时报维度错误原因对 test.csv 做预处理时漏掉了某些编码步骤比如只在训练集上做了 OneHotEncoder测试集没做或者某个分类特征的取值只在训练集出现过。这是课程设计里最典型的翻车点。解决把预处理封装成统一函数用 fit_transform 处理训练集用 transform 处理测试集。如果测试集出现训练集没有的分类取值在 OneHotEncoder 里设置 handle_unknownignore这算是最快的后悔药既不会报错也不会影响已训练好的系数。从那以后我每次提交代码前都会强制检查一遍 train 和 test 的列名集合是否完全相同。5.3 现象缺失值填充后验证集 R² 反而下降原因盲目对“价格”这类目标列做了 fillna或者用均值填充了像“面积”这种偏态分布特征导致分布失真。尤其是用均值填充价格列会直接把目标变量的方差压小模型学不到有效规律。解决缺失值先看占比。低于 5% 直接删除对应行高于 5% 再用中位数填充偏态特征。目标列的缺失值一定不能随便填要么删行要么用其他列的回归模型去预测填充。课程设计里直接删除更稳妥毕竟训练数据量通常够用。5.4 现象随机森林 R² 非常高但预测结果里出现明显异常高价原因随机森林只能“插值”无法外推。当测试集中某些特征组合超出训练数据范围时树模型会给出训练集里见过的相近样本的平均值如果训练集本身有异常高价样本就直接带偏了。解决在特征工程阶段先把 unit_price 的异常控制住用 IQR 法对单价做截断。正则化的线性模型对这类问题容忍度更高所以如果随机森林的极端预测出现多次可以考虑把岭回归作为最终上线模型。这也是为什么这份源码里同时放三个模型而不是只放一个——它们互为兜底。5.5 现象脚本运行时间非常久GridSearchCV 一直转圈原因随机森林的候选参数组合太多而且 n_jobs 没设置单核跑完所有树。一个 300 棵树的随机森林配上 5 折交叉验证还要跑 27 组参数组合单核可能要跑十几分钟。解决给 GridSearchCV 加 n_jobs-1让所有核心并行。控制 max_depth 让单棵树别太深深度越大训练越慢。如果还是慢就把 n_estimators 的候选值从 [100, 200, 300] 缩小到 [100, 200]先出结果再谈精度。调完参后记得记录最优参数和运行时长这些细节放进实验报告里会很加分。6. 进阶把预测结果可视化并验证模型稳定性跑通脚本只是第一步想让这份源码真正变成能写进简历的项目还得补两块预测结果可视化和模型稳定性验证。可视化部分我习惯画两张图第一张是验证集的真实价格与预测价格散点图第二张是残差分布图直接用 matplotlib 完成import matplotlib.pyplot as plt plt.figure(figsize(12, 4)) plt.subplot(1, 2, 1) plt.scatter(y_val, val_pred, alpha0.5) plt.plot([y_val.min(), y_val.max()], [y_val.min(), y_val.max()], r--) plt.xlabel(True Price) plt.ylabel(Predicted Price) plt.subplot(1, 2, 2) residual y_val - val_pred plt.hist(residual, bins50) plt.xlabel(Residual) plt.tight_layout() plt.savefig(prediction_check.png)散点图里点越贴近红色对角线说明预测越准残差直方图如果近似正态分布且围绕 0 对称说明模型没有系统性偏差。如果残差图出现明显的喇叭形说明误差随价格升高而增大这时候可以考虑对价格做 log1p 变换也就是训练时预测 log(price)预测后再用 np.expm1 还原能显著压低高价位样本的误差权重。稳定性验证方面我建议至少跑三次不同的 random_state 切分记录 R² 的波动范围。波动超过 0.05 说明数据量不够或者特征不稳这时候不要急着换模型先回头看看异常值是不是没清理干净。我每次拿到这类课程设计源码都会强制走一遍“先检查 → 再复现 → 最后改一版可视化”的流程不直接双击运行。这个习惯帮我省了不少排查时间也让我在别人问起代码逻辑时更有底气。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站