ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

房价预测系统源码解析与部署指南:数据清洗到模型交付

房价预测系统源码解析与部署指南:数据清洗到模型交付 简介基于机器学习的房价预测系统源码包面向房地产评估、金融风险评估等场景适合具备Python与机器学习基础的学习者、开发者学习参考也可作为课程设计与入门实战项目。包内共4个文件包括两个CSV数据集、一个Python脚本和一份Markdown说明文档压缩包仅176KB结构精简便于快速下载与对照学习Markdown文档帮助了解项目结构与运行步骤Python脚本为核心实现CSV数据用于训练和测试。已有148人学习/下载。源码覆盖数据预处理、异常值处理、缺失值填充、分类特征编码、特征工程以及岭回归、Lasso回归和随机森林回归的模型选择与调参最终完成房价预测并输出结果。读者可借助训练集与测试集直接运行核心Python脚本复现完整建模流程理解不同回归模型的效果差异并在此基础上扩展特征工程思路。1. 拿到「机器学习房价预测系统」的 zip你真正要解决的是这四件事打开压缩包之前很多人以为重点在“解压”上其实真正值得花时间的是“复现”和“验证”。一个基于机器学习的房价预测系统源码包解压只是第一步你要能回答四个问题数据从哪来、模型为什么有效、误差有多大、换台电脑还能不能跑通。这篇文章就按这条线把机器学习房价预测系统从文件结构、数据集选择、模型训练到打包成新 zip完整拆开讲一遍。适合要交课程设计源码的同学也适合第一次把模型交付出去的初级工程师。2. 拆解房价预测系统源码包目录、数据与模型选型在写任何一行训练代码之前我都会先做一件事把压缩包解压后只看文件结构不看代码内容。这个动作能帮我省下后面一晚上的排查时间。一个 zip 里如果只有零散的 .py 文件大概率是个人练习如果目录清晰、入口明确才谈得上“系统”。2.1 一个可直接交付的源码 zip 里通常装了什么我整理过多次交付的压缩包标准布局大致是这张表文件/目录作用缺失时的后果data/housing.csv训练用的原始房价数据集别人拿到代码无法复现train.py数据加载、特征工程、模型训练和保存没有训练环节模型不可追溯predict.py加载模型文件并对新样本预测系统只能训练不能使用models/训练产物如 rf_model.pkl需要等重新训练才能用requirements.txt依赖包及其版本环境不一致import 都可能报错README.md运行步骤、字段说明、版本要求使用者全靠猜三步一卡如果你拿到的 zip 里只有 train.py 和 predict.py 两个文件也能跑但严格说那不是系统只是脚本。真正的系统要能被别人无脑复现。我一般会检查三个细节数据是不是在压缩包内、requirements 有没有锁版本、代码里有没有写死绝对路径。最后一点是很多项目交付翻车的根源——作者在自己电脑上开发得好好的换个机器就 FileNotFoundError几乎都是路径硬编码惹的祸。2.2 房价预测该用哪个数据集波士顿房价之外的备选标题没有限定数据集这是个好消息。波士顿房价数据集是入门教科书最常用的选择sklearn 里自带 load_boston只有 506 个样本、13 个特征训练速度快到眨眼。但把它当作现代系统的唯一数据源会显得单薄一是规模小泛化能力有限二是部分特征的历史背景放在今天的房价市场里解释力不够。除非是课程明确要求否则我一般不会只依赖它。我现在的默认选择是加州住房数据集全名 fetch_california_housing。它有 20640 条样本、8 个特征包含人均收入、房龄、房间数、卧室数、户数、人数、经纬度等字段。这个数据最大的优点是字段本身就覆盖了房价预测的核心维度适合做地理类可视化和特征工程演示另一个优点是它天然带缺失值不用特地构造数据就能练习清洗流程。如果你的目标是贴近真实业务可以抓取本地中介平台的挂牌数据。但要注意两点抓取需要遵守数据协议而且市场数据时效性很强隔半年就已无法反映当前价格。教学设计里我会选加州住房商业化系统里我会选真实挂牌数据。两者工作流完全一样差异在特征工程的复杂度和对外解释的数据来源可信度。2.3 模型选型的三个层次先定基线再追求精度房价预测是回归问题不是拿个线性回归就能交差。我在源码包里一般会预留三档模型每一档对应不同的交付目标。第一档是线性回归。它的可解释性好每个特征的系数可以直接讲成“其他条件不变时该特征每变化一个单位房价变化多少”。作为基线模型线性回归能给出一个 R2 大概在 0.6 左右的参考值帮助你判断后续模型还有多大提升空间。第二档是随机森林。它不要求特征和目标的线性关系能学到比如“面积对价格的影响在不同价位段不同”这样的非线性规律也不需要做标准化只要把缺失值处理好就行。它的主要风险是过拟合样本量小的时候树深不限制就会把训练集背下来。第三档是梯度提升类模型如 XGBoost、LightGBM。这是刷精度用的适合时间充裕、目标是要拿名次或满足严格业务精度的场景。它的调参空间很大学习率、迭代轮数、树深度、采样率都要一起考虑新手很容易一头扎进参数的黑匣子。选型原则是从简单到复杂先用线性回归跑通数据管线把模型文件存下来再用随机森林做一次对比看 R2 提升是否明显如果差得不多停下用简单模型交付如果差得多再上梯度提升。这个流程看起来笨但能保证你任何时刻手里都有一个可用结果而不是调参到半夜发现模型无法复现。模型选型时还要理解回归任务的三种评分。MSE 是平方误差后的平均值单位是价格的平方不直观RMSE 取平方根后单位恢复成价格能直接说“平均差多少钱”MAE 是误差绝对值的平均对异常点更鲁棒。R2 表示模型相对预测均值提升了多少越接近 1 越好R2 为负数说明模型比简单用均值还差这时该查特征而不是继续调参。交付文档里通常要同时给 RMSE 和 R2因为 RMSE 给出业务单位R2 给出解释力度。2.4 先做一次特征相关性检查选型前的例行体检进入训练脚本之前我会先跑一次相关性矩阵这个步骤能解决后面一大半“模型为什么这么差”的疑问。它做的事情很简单看看哪些特征和房价目标值强相关哪些特征之间高度冗余。import pandas as pd df pd.read_csv(data/housing.csv) corr df.corr() print(corr[MedHouseVal].sort_values(ascendingFalse))corr() 默认计算所有数值列的两两 Pearson 相关系数sort_values 之后你能一眼找到对房价影响最强的特征。常见的现象是“人均收入”相关系数最高这在加州住房数据里几乎总成立也提醒你这项特征值得优先保留。如果你发现两个特征的相关系数超过 0.9比如房间数和卧室数就要考虑只保留其中一个或者加正则化防止多重共线性干扰系数解释。这段检查不是锦上添花而是决定模型选型的依据。如果这一步看到的都是弱相关模型再复杂也只是在噪声上拟合。3. 用源码复现第一步从解压到跑通最小训练管线拿到 zip 之后我跟大多数人的习惯不太一样不先看 train.py先动手搭环境。因为哪怕代码写得再完美依赖环境不对也是白搭。3.1 重建环境的三个命令从 zip 解压到最小依赖安装假设你已经把压缩包解压到本地目录。先别用系统里的 Python 直接 pip install我一般按下走三步cd 房价预测系统 python -m venv venv source venv/bin/activate pip install -r requirements.txt三行命令分别是创建虚拟环境避免和系统其他项目互相影响激活它让后续所有 pip 都装进当前项目按 requirements.txt 安装依赖。如果你拿到的 zip 里没有 requirements.txt可以先用最小集合跑通pip install pandas numpy scikit-learn joblib flask参数说明pandas 负责读 DataFramenumpy 提供底层数组运算scikit-learn 提供模型和指标joblib 用来保存和加载模型flask 仅在需要做接口时使用。这里特意没有写 statsmodels因为机器学习房价预测前期用 sklearn 就够了多加依赖只会拖慢复现。有一个细节requirements.txt 有时候写的是 版本而不是精确版本。遇到这种情况要格外小心最好在干净的 venv 里先跑一遍训练脚本确认核心接口没有被新版改名。如果脚本里用的是 sklearn 0.24 时代的函数而当前环境装到 1.5很多 API 的传参方式都可能变过。3.2 数据加载与特征清洗缺失值、异常值和冗余列环境建好后的第一步是加载数据。假设压缩包里的数据文件叫 housing.csv字段包括经度、纬度、人均收入、房龄、房间数、卧室数等。先用两行代码探路import pandas as pd df pd.read_csv(data/housing.csv) print(df.info()) print(df.isnull().sum())df.info() 会输出每一列的名字、非空数量和数据类型帮助发现 CSV 里的数值列是否被读成了 object例如“房间数”列如果带了逗号分隔符就会被读成字符串。isnull().sum() 列出每一列的缺失数量这是处理缺失值的起点。接下来是清洗。缺失值处理我一般用中位数填充特别是卧室数、人口这类偏态分布的字段比均值填充更抗异常值df[AveBedrms].fillna(df[AveBedrms].median(), inplaceTrue) house_age_upper df[HouseAge].quantile(0.99) df df[df[HouseAge] house_age_upper]fillna 的 inplaceTrue 直接改源 DataFrame不用再赋值quantile(0.99) 用于截断最极端的 1% 房龄样本避免个别异常值拉偏回归系数。但要注意一点如果训练前截断了异常值交付预测接口时也要把同样的截断规则写进接口保持逻辑一致。很多人只把截断写在训练脚本里上线后却忘了在接口里套用真实样本过来时分布和训练对不上。3.3 训练脚本主干划分样本、训练模型、输出三个指标清洗完之后写一个能独立运行的 train.py。核心逻辑按“取特征、划数据、训练、评估”四步走from sklearn.model_selection import train_test_split from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_squared_error, r2_score X df.drop(MedHouseVal, axis1) y df[MedHouseVal] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) model LinearRegression() model.fit(X_train, y_train) pred model.predict(X_test) print(RMSE:, mean_squared_error(y_test, pred, squaredFalse)) print(R2:, r2_score(y_test, pred))df.drop(MedHouseVal, axis1) 把目标列拆出来防止模型用房价预测房价。train_test_split 的 test_size0.2 表示 20% 作为测试集random_state42 固定随机种子保证每次切分得到相同子集这能让实验结果被别人复现。mean_squared_error 的 squaredFalse 直接返回 RMSE单位是房价原来的货币单位业务上更好解释。如果只有线性回归系统精度往往不够用。我会在同一份代码里再加一个随机森林做对照from sklearn.ensemble import RandomForestRegressor rf RandomForestRegressor( n_estimators200, max_depth12, random_state42 ) rf.fit(X_train, y_train) pred_rf rf.predict(X_test) print(RF RMSE:, mean_squared_error(y_test, pred_rf, squaredFalse)) print(RF R2:, r2_score(y_test, pred_rf))n_estimators200 是树的数量数值越大结果的随机波动越小但训练时间近似线性增长max_depth12 限制树的深入防止每个节点分到只剩一个样本。这两个参数是随机森林最先要动的之后再考虑 min_samples_leaf 等。这里还可以打印特征重要性直接回答“哪个特征在决定房价时最有发言权”import numpy as np for name, imp in zip(X.columns, rf.feature_importances_): print(f{name}: {imp:.3f}) print(top1:, X.columns[np.argmax(rf.feature_importances_)])zip(X.columns, rf.feature_importances_) 把特征名和重要性分数配对:.3f 保留三位小数np.argmax 找出最大重要性的索引。这个输出比相关性矩阵更有说服力因为树模型里的重要性考虑了特征交互。操作逻辑是你先跑基线再跑候选模型对比二者在同一测试集上的表现。如果随机森林比线性回归提升不到 1%说明数据里的信息快用尽了此时应转向特征工程而不是换更大的模型。4. 做成“系统”而不是脚本预测接口与源码打包交付一个人能跑通训练脚本不代表这个项目能交付成“系统”。真正可用的系统要能让别人或者让一个接口拿到新数据直接产出房价预测。常见做法有两种把预测封装成 Web 接口或者把训练好的模型文件和加载脚本一起打包。4.1 用 Flask 包一个可调用的房价预测接口我不会在源码里硬塞一个完整前端页面那会让系统变得臃肿。一般用 Flask 写一个最小的 POST 接口接收 JSON 格式的房屋特征返回预测价格。这样接口可以被其他系统调用后续接前端、接报表都很自然。from flask import Flask, request, jsonify import joblib model joblib.load(models/rf_model.pkl) app Flask(__name__) app.route(/predict, methods[POST]) def predict(): data request.get_json() features [ data[MedInc], data[HouseAge], data[AveRooms], data[AveBedrms] ] price model.predict([features])[0] return jsonify({price: price}) if __name__ __main__: app.run(host0.0.0.0, port8000)joblib.load 从训练阶段保存的 pkl 文件读入模型不需要重新拟合POST 请求里用 request.get_json() 拿到参数按照训练时相同的特征顺序组成二维数组传给 predict最后返回 JSON 给调用方。host0.0.0.0 允许外部机器访问port8000 要避免与常用开发端口冲突。这里必须提醒接口代码中的特征顺序要和训练时 df.drop() 之后的列顺序严格一致。如果训练时用的是 A、B、C 列顺序接口里却按 C、B、A 组装模型其实是拿错数据在做预测只是数值表面上还能输出。4.2 可复现打包依赖、数据、模型一个都不能少打包是一个系统工程。我在交付之前会盘一遍依赖列表是否完整数据文件是否存在模型文件要不要带进去。以下是我常用的打包命令。zip -r 房价预测系统_v1.0.zip \ data/ models/ train.py predict.py app.py requirements.txt README.mdzip -r 递归打包目录data/ 和 models/ 下的子文件都会被含进去把 requirements.txt 和 README.md 放在最后是便于检查的习惯。如果想减小压缩包体积可以用 -x data/*.csv 排除原始数据但为了保证别人能完整复现我建议保留。有一个容易踩的坑打包前清理临时文件。训练脚本留下的pycache、.ipynb_checkpoints、.DS_Store 会被一并打进去交到别人手里还容易引起困惑。打包前加一步删除find . -name __pycache__ -type d | xargs rm -rf find . -name *.pyc -delete4.3 给 CSV 加一个批量预测入口不用等接口也能验证接口适合单条调用但要验证整个系统时我习惯再写一个批量脚本输入一条带所有特征的新 CSV输出一列预测价格。这样不用开 Web 服务也能方便地做回归测试。import pandas as pd import joblib model joblib.load(models/rf_model.pkl) new_df pd.read_csv(data/new_houses.csv) X_new new_df[[MedInc, HouseAge, AveRooms, AveBedrms]] new_df[PredPrice] model.predict(X_new) new_df.to_csv(data/new_houses_with_pred.csv, indexFalse) print(new_df.head())代码逻辑加载已经训练好的模型把新数据的特征列按训练时顺序取出pred predict 后写入新列再保存回 CSV。这个脚本解决的是“不在你电脑上的人怎么快速验证”的问题。4.4 最后一步验证在新机器上按 README 走一遍打包完成后工作还没结束。我一般会再做一个独立验证用一台干净的机器或者新建一个全新虚拟环境严格按 README 的步骤走一遍。要检查四件事解压是否报错、依赖安装是否顺利完成、训练脚本是否在可接受时间内产出模型、predict.py 是否能返回有效预测值。如果这段验证不过宁可晚一天交付也不要交出去再被打回来。这个环节看似简单但很多源码包就栽在“作者自己顺手过了别人按文档跑却对不上”。5. 房价预测避坑实操5 个最容易翻车的地方在机器学习项目里跑了两三年后我发现新人踩的坑高度重复。每一条都按现象、原因、解决的顺序展开希望对你有用。5.1 数据泄漏先标准化还是先切分后果差很远现象训练时 R2 很高切到验证集 R2 突然掉成负数测试集上的 RMSE 比理论预期低很多。原因把 StandardScaler 放在 train_test_split 之前scaler 已用全量数据的均值和方差做变换相当于把测试集信息提前带进了训练流程。这在统计上叫数据泄漏是房价预测里最容易出现的漏水口。解决先切分再在训练集上 fit然后用同一个 scaler 去 transform 测试集。from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_train scaler.fit_transform(X_train) X_test scaler.transform(X_test)fit_transform 里的 fit 阶段只统计训练集的均值和方差transform 阶段把该统计量应用到两个集合。这样测试集的尺度信息不会混入训练过程。记住predict 阶段的新样本也必须用同一个 scaler 变换所以训练完记得把 scaler 也存下来打包进 zip。5.2 中文路径与 zip 编码解压后脚本找不到数据现象解压后运行脚本提示 FileNotFoundError: data/housing.csv或者数据文件名变成乱码导致 pandas 读不到。原因压缩包在 Windows 下用默认编码创建解压到 macOS 或 Linux 后文件名出现乱码也可能是代码里写了 C:\用户... 这样的中文绝对路径跨平台直接失效。解决把所有路径改成相对路径用 os.path.join 拼接文件名统一为英文zip 打包时指定 UTF-8 编码。如果已经拿到乱码包最省事的处理是让交付方以 UTF-8 重新打包不要自己一个个去改名修补。5.3 模型一直欠拟合光调参不动特征工程现象线性回归、随机森林、XGBoost 都试了一遍R2 卡在 0.5 上下散点图里预测值系统性偏移真实值。原因大多数人把问题归咎于“模型不行”其实是特征信息不足。只用了房间数、房龄、住宅数丢掉了区位特征而房价和地理位置相关性极强。解决把经纬度或地理分区做成新特征。对加州住房数据可以构造经纬度交叉项或区域聚类。至少先做特征交互from sklearn.preprocessing import PolynomialFeatures poly PolynomialFeatures(degree2, interaction_onlyTrue) X_train_poly poly.fit_transform(X_train)degree2 表示两两特征相乘interaction_onlyTrue 表示不加自身平方项能减少维度膨胀。房价预测里面积乘以地点这类交互项经常比单一特征有用。5.4 requirements.txt 漂移别人机器上装不出同样环境现象作者用 pandas 2.x 训练正常用户环境默认装了更新的版本运行到某行 API 报错或者 sklearn 升级后 joblib 加载旧模型文件出现 TypeError。原因requirements.txt 里只写了包名没写版本号或写了 而不锁死。pip 装的是当前最新版而新版接口可能发生破坏性变更。解决交付前把当前安装版本导出为锁定文件pip freeze requirements.lock.txtpip freeze 会把每个已安装包的精确版本写死别人使用 pip install -r requirements.lock.txt 才能复现你的训练环境。如果担心导出混入虚拟环境外的无关包就在干净的 venv 里重新导一次。5.5 反复用测试集调参模型在未知数据上不再“未知”现象每次调参都用测试集看效果手工选出的“最佳”组合最终上线表现一塌糊涂。原因测试集被反复用来调参模型和参数在测试集上发生了间接的“记忆”测试集已经失去评估未知数据的意义。这本质上也是一种泄漏只是过程更隐蔽。解决把数据切成三段train、valid、test。valid 用来调参test 只在最后评估一次。如果 test 分数和 valid 分数差很远优先怀疑过拟合或数据分布不一致而不是继续用 test 去反推参数。这个习惯越早养成后面吃亏越少。6. 把预测结果做得更可信交叉验证、残差分析和模型文件化系统能跑、能交付之后下一步是对输出质量有把握。这部分我每次对接项目都会做三个小动作。第一个动作是交叉验证。不要拿一次 train_test_split 的结果当最终精度我一般先用 5 折交叉验证看均值和波动。均值高说明模型整体效果好标准差大说明对数据划分很敏感此时应检查随机种子或考虑增加数据。from sklearn.model_selection import cross_val_score scores cross_val_score(rf, X, y, cv5, scoringr2) print(CV R2:, scores.mean(), /-, scores.std())cv5 表示把数据平均切成 5 份轮流拿一份做验证余下训练最终得到 5 个分数。第二个动作是看残差图。把模型预测值减去真实值得到残差。如果残差在中间价格区间出现喇叭形分布说明存在异方差性即房间数特别大时误差也同步变大。这个问题不容易完全消除但至少要在 README 里承认这个局限比用户后来发现再解释诚恳得多。第三个动作是模型持久化。训练一次模型把参数存起来之后不用重新拟合。我会同时保存模型文件和特征顺序清单两者一起打包。import joblib, json joblib.dump(rf, models/rf_model.pkl) with open(models/feature_order.json, w) as f: json.dump(list(X.columns), f)joblib.dump 把训练好的随机森林对象序列化到磁盘feature_order.json 记录训练时输入的列名顺序预测接口组装特征时不会顺序错乱。一次交付里我吃过没保存特征顺序的亏模型传给同事对方按自己的习惯把列顺序一换预测结果全部错乱查了两小时才发现。后来固定了这个双文件规范再也没有发生同类事故。希望帮到你。本文还有配套的精品资源点击获取
返回列表