ARTICLE DETAIL

资讯详情

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

房价预测实战:随机森林与SVR的对数变换及网格搜索调参

房价预测实战:随机森林与SVR的对数变换及网格搜索调参 简介这份资源是一套面向数据科学学习者与机器学习初学者的房价预测完整实践案例围绕房产数据集展开重点对比随机森林与支持向量机回归器在房价对数预测和价格直接预测两种策略下的表现差异并完整演示网格搜索超参数调优流程。压缩包共8个文件约2.62MB包含ipynb交互式笔记本、py脚本、csv数据集、pdf实验记录、docx附赠说明及md说明文档等覆盖从数据读取、建模训练到结果评估的全流程。目前已有166人学习下载。读者可借助该案例掌握真实数据下的特征处理、模型构建、超参数搜索空间设置与结果解读方法并通过误差分析和预测可视化对比不同建模策略的实际效果适合希望将机器学习理论落地到项目实操的初学者与专业人员参考练习。1. 从一份房价预测项目拆起随机森林和 SVR 到底怎么选房价预测这个题目几乎每个学机器学习的人都碰过。波士顿房价数据集被用烂了但真正拿一份结构完整的房产数据从清洗走到调参、再走到对数与原始价格两种预测口径对比的项目其实不多。这份资源给的就是这样一套东西Housing.ipynb是主案例Housing.py是脚本版housing.csv是数据外加一份 PDF 笔记和housing.tgz压缩包。它要解决的不是什么是回归这种问题而是让你亲手跑通一条完整链路——用随机森林回归器和支持向量机回归器分别建模比较对房价取对数后再预测与直接预测原价两种策略的差异再用网格搜索把超参数调到位。适合谁如果你已经会train_test_split和fit但一到真实数据就不知道先做哪步、参数往哪调、结果怎么读这份案例正好补上这段。它不教你从零写算法而是把工程流程摆出来给你抄。下面我按自己拆包的顺序把这份资源里真正能落地的部分讲透。2. 数据准备与两种预测口径为什么先取对数再回归2.1 房价数据的分布问题与对数变换的动机拿到housing.csv第一件事不是建模是看目标变量的分布。房价这类数据几乎必然右偏——大部分房子价格集中在中低区间少数高价房把尾巴拉得很长。线性类模型和基于距离的 SVR 对这种情况很敏感少数极端高价样本会在损失函数里占很大权重把拟合方向拽偏。对数变换的作用是把乘性关系变成加性关系。房价的很多影响因素面积、地段对价格的影响是比例式的取log(price)后分布更接近正态模型学起来更稳。这也是这份案例把对数预测和直接预测并列对比的原因——它不是炫技而是让你亲眼看到同一套模型在两种目标口径下误差指标的差别。常见做法是先对目标列做np.log1p预测完再用np.expm1还原回原价算 RMSE。注意log1p而不是log是为了兼容价格里可能出现的 0 值虽然房价一般不为 0但养成习惯没坏处。2.2 缺失值、类别特征与训练集划分真实房产数据一定脏。数值列里total_bedrooms这类字段经常有缺失类别列如ocean_proximity是字符串。处理顺序我一般这样走import pandas as pd import numpy as np from sklearn.model_selection import train_test_split from sklearn.impute import SimpleImputer from sklearn.preprocessing import OneHotEncoder, StandardScaler from sklearn.compose import ColumnTransformer from sklearn.pipeline import Pipeline housing pd.read_csv(housing.csv) # 目标列取对数缓解右偏 housing[log_price] np.log1p(housing[price]) num_cols housing.drop([ocean_proximity, price, log_price], axis1).columns.tolist() cat_cols [ocean_proximity] # 数值列中位数填充 标准化类别列独热编码 num_pipe Pipeline([ (imputer, SimpleImputer(strategymedian)), (scaler, StandardScaler()) ]) cat_pipe Pipeline([ (encoder, OneHotEncoder(handle_unknownignore)) ]) preprocess ColumnTransformer([ (num, num_pipe, num_cols), (cat, cat_pipe, cat_cols) ]) X housing.drop([price, log_price], axis1) y_log housing[log_price] y_raw housing[price] X_train, X_test, y_train_log, y_test_log train_test_split( X, y_log, test_size0.2, random_state42 )逻辑说明ColumnTransformer把数值和类别两条处理链并行接起来避免手写循环。SimpleImputer用中位数而不是均值是因为中位数对偏态和异常值更稳。StandardScaler对 SVR 是必须的——SVR 基于距离量纲不统一会让某个大数值特征主导核函数。随机森林其实不依赖标准化但放进同一条 pipeline 里复用没坏处。参数上test_size0.2是常规起点random_state固定住是为了让每次跑的结果可复现调参对比时尤其重要。类别编码用handle_unknownignore防止测试集出现训练集没见过的类别时直接报错。提示如果你要同时对比对数预测和直接预测记得用同一个random_state划分否则两组结果的差异里混进了数据划分的随机性对比就不干净了。3. 随机森林与 SVR 建模网格搜索调参的实操路径3.1 随机森林回归器的参数空间与网格搜索随机森林的调参相对友好核心就几个树的数量n_estimators、最大深度max_depth、分裂所需最小样本数min_samples_split、叶节点最小样本数min_samples_leaf。树越多越稳但越慢深度越大越容易过拟合。from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import GridSearchCV rf_pipe Pipeline([ (prep, preprocess), (model, RandomForestRegressor(random_state42)) ]) rf_param_grid { model__n_estimators: [100, 200], model__max_depth: [None, 10, 20], model__min_samples_leaf: [1, 2, 4] } rf_grid GridSearchCV( rf_pipe, rf_param_grid, cv3, scoringneg_mean_squared_error, n_jobs-1, verbose1 ) rf_grid.fit(X_train, y_train_log) print(最优参数:, rf_grid.best_params_) print(最优CV分数:, rf_grid.best_score_)逻辑说明GridSearchCV会在cv3折交叉验证上遍历所有参数组合scoring用负均方误差是因为 sklearn 的评分约定越大越好误差取负后才能被正确最大化。n_jobs-1吃满所有 CPU 核verbose1打印进度参数组合多的时候能让你知道跑到哪了。参数怎么读best_params_给出最优组合best_score_是交叉验证上的平均表现。注意这是对数目标下的 MSE不是原始价格的误差解读时要心里有数。如果max_depthNone胜出说明数据量还没大到让树过拟合或者min_samples_leaf已经起到了正则作用。3.2 SVR 的核函数选择与参数敏感性SVR 比随机森林难调因为它对参数极其敏感。核心参数三个C惩罚系数、epsilon容忍带宽度、gammaRBF 核的宽度。C越大越不容忍误差容易过拟合gamma越大核越窄也容易过拟合epsilon控制对多少以内的误差不惩罚。from sklearn.svm import SVR svr_pipe Pipeline([ (prep, preprocess), (model, SVR(kernelrbf)) ]) svr_param_grid { model__C: [0.1, 1, 10], model__epsilon: [0.01, 0.1, 0.5], model__gamma: [scale, 0.1, 0.5] } svr_grid GridSearchCV( svr_pipe, svr_param_grid, cv3, scoringneg_mean_squared_error, n_jobs-1, verbose1 ) svr_grid.fit(X_train, y_train_log) print(最优参数:, svr_grid.best_params_)逻辑说明kernelrbf是默认首选能拟合非线性关系。gammascale让 sklearn 自动按特征数算一个合理值通常比手调更省事。SVR 在样本量上万后训练会明显变慢这份房产数据规模适中还能接受。如果数据再大SVR 就不是好选择了这是它的边界。参数解读C和gamma是一对需要联合看的参数单独调一个往往得不到最优。epsilon太大模型会欠拟合太小又对噪声敏感。网格搜索的价值在这里体现得最明显——手工试这几个参数的组合很容易漏掉真正好的区域。3.3 对数预测与直接预测的对比方法两种口径的对比要控制变量同一份划分、同一套预处理、同一组超参数搜索空间只改目标列。from sklearn.metrics import mean_squared_error, mean_absolute_error def evaluate(grid, X_test, y_test, log_targetTrue): pred grid.predict(X_test) if log_target: pred np.expm1(pred) # 还原到原始价格 y_true np.expm1(y_test) else: y_true y_test rmse np.sqrt(mean_squared_error(y_true, pred)) mae mean_absolute_error(y_true, pred) return rmse, mae # 对数口径 rmse_log, mae_log evaluate(rf_grid, X_test, y_test_log, log_targetTrue) # 直接口径需另训一个以 y_raw 为目标的模型逻辑说明np.expm1是log1p的逆运算把预测值还原回价格尺度后再算 RMSE这样两种口径的误差才可比。如果直接在对数尺度上比 RMSE数值会小很多容易误判成对数预测更好其实只是尺度不同。常见结论是对数预测在 RMSE 上通常更优因为它压住了高价样本的影响但直接预测在 MAE 上有时不落下风因为 MAE 对极端值本来就不敏感。具体哪个赢取决于你的数据分布和业务更在意哪种误差。这份案例把两种都跑一遍就是让你自己看结果而不是背结论。4. 避坑与排查跑这份案例最容易翻车的五个地方4.1 现象SVR 训练极慢甚至卡死原因SVR 的时间复杂度随样本量增长很快如果没做标准化或者C设得过大迭代次数会暴涨。另一个常见原因是把整个数据集丢进去做网格搜索参数组合一多就撑不住。解决先确认StandardScaler在 pipeline 里生效把C的搜索范围从[0.1, 1, 10, 100]这种宽范围收窄先用粗网格定位再细化样本量大时改用随机森林或线性模型SVR 不是万能选择。4.2 现象对数预测还原后 RMSE 大得离谱原因忘了用np.expm1还原或者还原了但y_test没同步还原两边尺度不一致。还有一种情况是预测值里出现了极端大的数expm1一放大就爆炸。解决统一在评估函数里做还原确保pred和y_true都回到原始价格尺度。如果出现爆炸值检查是否有特征没标准化导致 SVR 预测发散。4.3 现象网格搜索报错 Unknown label type 或类别编码失败原因ocean_proximity这类字符串列没进ColumnTransformer的类别分支或者测试集出现了训练集没有的类别而编码器没设handle_unknown。解决确认cat_cols列表和实际列名一致OneHotEncoder加handle_unknownignore如果类别特别多考虑改用目标编码或频率编码独热会让特征维度膨胀。4.4 现象交叉验证分数很好测试集一塌糊涂原因典型的过拟合或者数据泄漏——比如在划分训练测试集之前就做了全局的缺失值填充或标准化测试集的信息漏进了训练过程。解决所有预处理必须放进 pipeline让GridSearchCV在每一折内部独立 fit。这是 sklearn pipeline 存在的核心意义手写预处理再喂给网格搜索是血泪教训级别的错误。4.5 现象housing.tgz解压后和housing.csv对不上原因压缩包里可能是原始数据或另一个版本列名、行数可能和主案例用的 CSV 不一致。解决先head两个文件对比列结构以Housing.ipynb里实际读取的文件为准。如果只是练习直接用housing.csv即可tgz当备份。5. 把结果读明白误差可视化与一个我常用的验证习惯跑完模型光看 RMSE 数字是不够的。我习惯做两件事一是画预测值对真实值的散点图二是看残差分布。散点图能一眼看出模型在高价段是不是系统性偏低——这是房价预测里最常见的偏差模式因为高价样本少模型倾向于往均值回归。import matplotlib.pyplot as plt pred_log np.expm1(rf_grid.predict(X_test)) true np.expm1(y_test_log) plt.figure(figsize(6, 6)) plt.scatter(true, pred_log, alpha0.3, s10) lims [true.min(), true.max()] plt.plot(lims, lims, r--, linewidth1) # 理想对角线 plt.xlabel(True Price) plt.ylabel(Predicted Price) plt.title(Random Forest: Predicted vs True) plt.show() residual true - pred_log plt.hist(residual, bins50) plt.title(Residual Distribution) plt.show()逻辑说明对角线是理想情况点越贴近越好。如果高价区间的点明显压在对角线下方说明模型低估高价房可以考虑对高价样本加权或者换用能更好捕捉尾部的方法。残差直方图如果明显偏离 0 且不对称说明还有系统性偏差没被模型吸收。一个我每次都会走的验证习惯把GridSearchCV的best_estimator_单独拿出来在一个完全没参与调参的留出集上再跑一遍。很多人调完参直接报best_score_就完事但那个分数是交叉验证的平均和真实泛化性能有差距。留出集这一刀能让你在交付前发现过拟合。从那以后我每次调完参都强制走一遍留出验证再决定要不要把模型交出去。这份资源的价值在于它把数据清洗、两种目标口径、两类模型、网格搜索和可视化串成了一条完整链路Housing.ipynb可以逐格跑Housing.py适合直接改成自己的脚本。想上手的话把housing.csv和 notebook 放同一目录按上面的顺序跑一遍再拿自己的数据替换目标列整条流程就能复用。希望帮到你。本文还有配套的精品资源点击获取
返回列表